Транспонирование строк в столбцы в циклическом порядке с помощью сценария оболочки

Следующий perl-скрипт будет работать с любым количеством полей и любыми именами полей во входных данных.

Требуются библиотечные модули Time ::Date и List ::MoreUtils . Оба они, вероятно, уже упакованы для вашего дистрибутива (, например. на дебианеsudo apt-get install libtimedate-perl liblist-moreutils-perl). Сценарий можно было бы написать так, что эти модули ему не нужны, но нет смысла заново -изобретать колесо, когда уже существует повторно -пригодный для использования библиотечный код, который выполняет именно ту работу, которая вам нужна.

Сценарий предполагает, что строки ввода ограничены любым количеством пробелов (, т. е. одним или несколькими пробелами, символами табуляции и т. д. ). Если ввод разделен табуляцией -, измените строки с split;на split /\t/;. Разделители табуляции были бы хорошей идеей, если бы любое из имен в первом поле содержало пробелы (, например. Firstname Surname), или если какое-либо из имен полей содержало пробелы.

Если вам нужно жестко -закодировать определенный часовой пояс, например. GMT, измените следующую строку:

    $ts = str2time($_);

к, например,. (обратите внимание на пробел в кавычках передGMT):

    $ts = str2time($_. ' GMT');
#!/usr/bin/perl

use strict;
use Date::Parse;
use List::MoreUtils qw(pairwise);

my @columns;
my $ts='';

while(<>) {
  s/^\s*|\s*$//g;    #/ strip leading and trailing spaces
  next if (/^$/);    #/ skip empty lines
  chomp;

  # line begins with two digits and a slash? it's a date.
  if (m/^\d\d\//) {
    # get the date and parse it so that we have seconds since the epoch
    $ts = str2time($_);

    # get the next line and split it into column headers
    $_ = readline;
    @columns = split;

  } else {
    # split the current line into @row array
    my @row=split;
    # use pairwise() function from List::MoreUtils module to merge the
    # @columns and @row arrays.
    print join(" ", (pairwise { "$a=$b" } @columns, @row), "ts=$ts"), "\n";
  }

}

Пример вывода:

$./reformat.pl input.txt 
Name=Clyde Cans=12 Bucks=2 Puns=79 ts=1566439258
Name=Sheila Cans=32 Bucks=16 Puns=42 ts=1566439258
Name=Elmo Cans=44 Bucks=18 Puns=21 ts=1566439258
Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566550822
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566550822
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566550822

ПРИМЕЧАНИЕ. :Это не сломается, если в какой-либо из строк данных будет больше или меньше столбцов, чем количество заголовков столбцов, но это приведет к необычному выводу. Для отсутствующих полей он просто напечатает имена полей с символом =и без значения (, например. если в строке ввода было только 2 поля, она вывела бы Puns=), а если бы полей было больше, она бы напечатала только значение с префиксом =(, например.если в строке ввода есть дополнительное поле со значением 20, будет выведено =20).

напр. если в вашем образце входных данных был третий блок данных, подобный этому:

08/23/2019 23:30:01
Name Cans Bucks Puns
Clyde 18 21 46
Sheila 37 2 11
Elmo 41 3 10
Missing 41 3
Extra 41 3 10 20

Это приведет к дополнительному выводу:

Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566567001
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566567001
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566567001
Name=Missing Cans=41 Bucks=3 Puns= ts=1566567001
Name=Extra Cans=41 Bucks=3 Puns=10 =20 ts=1566567001

2
09.11.2020, 10:42
3 ответа
awk 'BEGIN{ OFS="|"; printhdr=1 }
  NF{
    hdr=(hdr=="" ? "" : hdr OFS) $1
    row=(row=="" ? "" : row OFS) $2
    next
  }
  printhdr{ print hdr; printhdr=0 }
  { print row; row="" }
  END{ print row }
' file

Добавить поля для заголовка и транспонированных строк к переменным hdrи rowесли количество полей не равно нулю, и вывести переменные, когда будет найдена запись без полей. Заголовок печатается только один раз, если флаг printhdrустановлен и переменная rowтакже печатается в блоке ENDдля печати последней транспонированной строки входного файла.

2
18.03.2021, 22:51

С помощью Miller преобразовать формат «xtab» в формат «csvlite»:

$ mlr --ixtab --ocsvlite --ofs '|' cat source.txt
[ID]|[NAME]|[AGE]
10|TOM|25
11|SAM|26
3
18.03.2021, 22:51
awk -v RS= -v OFS='|' '
    NR==1 { for (i=1; i<NF; i+=2) printf "%s%s", $i, (i<(NF-1) ? OFS : ORS) }
    { for (i=2; i<=NF; i+=2) printf "%s%s", $i, (i<NF ? OFS : ORS) }
' file
[ID]|[NAME]|[AGE]
10|TOM|25
11|SAM|26
1
18.03.2021, 22:51

Теги

Похожие вопросы