awk: выведите $ 1 с различным количеством дополнительных полей в одной строке - PullRequest
3 голосов
/ 19 марта 2019

У меня есть входной файл с ~ 100 строк и ~ 100 полей в строке.Каждое поле представляет положительное или отрицательное значение.Я хочу вывести $ 1, за которым следуют только положительные или отрицательные поля в каждой строке.Количество положительных или отрицательных полей в строке является случайным.

выборочный ввод

0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

желаемый вывод

положительный

0 9 8 7
2 7 6
4 4 3 2 1

отрицательный

0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

контекст и попытка

Приведенные выше выходные данные выводят $ 1, за которым следуют положительные или отрицательные значения в оставшихся полях в той же строке, что и $ 1.

Текущий кодЯ попытался (для положительных значений, начиная со строки 6 в моем вводе):

awk 'NR>5{for(i=3; i<=NF; i++) if ( $i > 0 ) print $1, $i}' input > output

Это работает нормально, за исключением того, что я печатаю вывод как:

 0 9
 0 8
 0 7
 2 7
 2 6
 4 4
 4 3
 4 2
 4 1

Я также пробовал:

awk 'BEGIN {ORS="\t"} NR>5 {print $1} {for(i=3;i<=NF;i++) if ( $i > 0 && i <= NF} {print $i}}' input > output

но тогда я никогда не перейду на новую строку в выводе.Если я изменяю ORS обратно на \ n с помощью некоторого условия 'else if (i = NF) {ORS = ...}', то он выводит все выходные данные поля для каждого i на новой строке, так как инструкция BEGIN не имеет никакого эффекта.

вопрос

Как я могу сказать awk напечатать $ 1, затем распечатать все другие выходные данные из той же строки ввода в той же строке вывода, затем продвинуть 1 новую строку в выходе и повторить процессдля следующей строки ввода?

Спасибо.

ответ на ответ Tiw

Я пытался выполнить это в цикле для моих двух файлов:

for j in 1 2; do
positive=ofile.p0
negative=ofile.m0

awk 'NR>5{
    printf $1>"positive";
    printf $1>"negative";
    for(i=3;i<=NF;i++)
       if($i~/[-+]?[0-9]+/)
           if ($i>0) printf OFS $i>"positive";
           else if($i<0) printf OFS $i>"negative";
    print "">"positive";
    print "">"negative";
}'ofile.0$j

mv positive $positive$j
mv negative $negative$j

done

но зависает.Изменить: Ответ Tiw обновлен с% s в printf.Это работает с этим изменением.

Ответы [ 3 ]

2 голосов
/ 19 марта 2019

Первое, что вам нужно сделать, это проверить, является ли поле числом, если это так, вы можете сделать проверку.В awk вы можете проверить, является ли переменная числом, добавив к ней ноль, и проверить, возвращает ли она то же значение.

Для положительных чисел вы делаете это:

awk '{for(i=1;i<=NF;++i) if ($i+0 == $i && $i >= 0) printf $i OFS; printf ORS}' file
2 голосов
/ 19 марта 2019

Попробуйте:

awk 'NF>5{printf "%s",$1>"positive";printf "%s",$1>"negative"; for(i=2;i<=NF;i++) if($i~/^[-+]?[0-9]+$/) if ($i>0) printf "%s",OFS $i>"positive"; else if($i<0) printf "%s",OFS $i>"negative"; print "">"positive";print "">"negative";}' input

С файлом с именем input:

0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

Это создаст два файла,
один positive:

0 9 8 7
2 7 6
4 4 3 2 1

one negative:

0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

Чтобы улучшить удобочитаемость, вставьте несколько строк:

awk 'NF>5{
    printf "%s",$1>"positive";
    printf "%s",$1>"negative"; 
    for(i=2;i<=NF;i++) 
        if($i~/^[-+]?[0-9]+$/)     ## Another and better way is $i == $i + 0 
            if ($i>0) printf "%s",OFS $i>"positive"; 
            else if($i<0) printf "%s",OFS $i>"negative"; 
    print "">"positive";
    print "">"negative";
}' input

Это довольно просто, поэтому я думаю, что вам легко понять.
Примечание. Я не использовал {}, чтобы заключить блок в кавычки после for и if с, поскольку каждая из них имеет только одну команду после, поэтому кавычки могут быть сохранены.
print напечатаетсимвол новой строки \n в конце, printf не будет.
Также NR означает N число R записей, то есть номер строки, которую я изменилна NF, что означает N число F ields, я думаю, что это то, что вы хотели.

if($i~/^[-+]?[0-9]+$/) это проверить поле это число.
Если поле не будет пустым, тогда лучше использовать $i==$+0.
И в сочетании с тестированием поле не является 0 или пустым, используйте $i && ($i==$i+0).

1 голос
/ 19 марта 2019

Если Perl является опцией,

Ввод:

$ cat blaisem.txt
0 x 9 8 7 -1 -2 -3
2 x 7 6 -2 -3 -4 -5
4 x 4 3 2 1 -6 -7

$

+ ve и -ve отдельные прогоны

$ perl -ne ' @p=/(\S+)(?<=\d)/g;print "$p[0] "; for(@p[1..$#p]) { print "$_ " if $_ >=0 } print "\n" ' blaisem.txt
0 9 8 7
2 7 6
4 4 3 2 1

$ perl -ne ' @p=/(\S+)(?<=\d)/g;print "$p[0] "; for(@p[1..$#p]) { print "$_ " if $_ < 0 } print "\n" ' blaisem.txt
0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

$

+ ve и -ve в одном скрипте

$  perl -ne ' open(POS,">>pos.txt"); open(NEG,">>neg.txt"); @p=/(\S+)(?<=\d)/g; 
          print POS "$p[0] "; print NEG "$p[0] "; 
           for(@p[1..$#p]) { print NEG "$_ " if $_ < 0; print POS "$_ " if $_>=0  } 
             print POS "\n"; print NEG "\n" ' blaisem.txt

$ cat pos.txt
0 9 8 7
2 7 6
4 4 3 2 1

$ cat neg.txt
0 -1 -2 -3
2 -2 -3 -4 -5
4 -6 -7

$
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...