#for-loop #awk #unix-text-processing
Вопрос:
Так как я учусь awk
, я обнаружил, FNR==NR
что подход является очень распространенным методом обработки двух файлов. Если FNR==NR
; то это first file
означает , что при FNR
сбросе во 1
время чтения каждой строки из связанных файлов это означает !(FNR==NR)
, и это, очевидно, то second file
.
Когда дело доходит до трех или более файлов, я не вижу способа, который является вторым и третьим файлом, так как у обоих одно и то же !(FNR==NR)
условие. Это заставило меня попытаться понять, как может быть что-то вроде FNR2
и FNR3
?
Поэтому я реализовал метод обработки трех файлов в одном awk
. Предполагая, что FNR1
FNR2
FNR3
для каждого файла есть. Для каждого созданного мной файла for loop
, который запускается отдельно. Условие одинаково для каждого цикла NR==FNR#
и на самом деле получается то, что я ожидал:
Поэтому мне интересно, существуют ли более трезвые, лаконичные методы, которые дают аналогичные результаты с awk
помощью кода ниже
Пример Содержимого Файла
$ cat file1
X|A1|Z
X|A2|Z
X|A3|Z
X|A4|Z
$ cat file2
X|Y|A3
X|Y|A4
X|Y|A5
$ cat file3
A1|Y|Z
A4|Y|Z
AWK для цикла
$ cat fnrarray.sh
awk -v FS='[|]' '{ for(i=FNR ; i<=NR amp;amp; i<=FNR amp;amp; NR==FNR; i ) {x ; print "NR:",NR,"FNR1:",i,"FNR:",FNR,"tfirst filet"}
for(i=FNR ; i x<=NR amp;amp; i<=FNR amp;amp; NR==FNR x; i ) {y ; print "NR:",NR,"FNR2:",i x,"FNR:",FNR,"tsecond filet"}
for(i=FNR ; i x y<=NR amp;amp; i<=FNR amp;amp; NR==FNR x y; i ) {print "NR:",NR,"FNR3:",i x y,"FNR:",FNR,"tthird filet"}
}' file1 file2 file3
Ток и желаемая мощность
$ sh fnrarray.sh
NR: 1 FNR1: 1 FNR: 1 first file
NR: 2 FNR1: 2 FNR: 2 first file
NR: 3 FNR1: 3 FNR: 3 first file
NR: 4 FNR1: 4 FNR: 4 first file
NR: 5 FNR2: 5 FNR: 1 second file
NR: 6 FNR2: 6 FNR: 2 second file
NR: 7 FNR2: 7 FNR: 3 second file
NR: 8 FNR3: 8 FNR: 1 third file
NR: 9 FNR3: 9 FNR: 2 third file
Вы можете видеть NR
, совпадает с FNR#
и читается, что NR
для чего file#
.
Другой Метод
Я нашел этот метод FNR==1{ f} f==1 {}
здесь, обрабатывая 3 файла с помощью awk
Но этот метод заменяется arr1[1]
, когда каждый раз читается новая строка
Неудачная попытка 1
$ awk -v FS='[|]' 'FNR==1{ f} f==1 {split($2,arr); print arr1[1]}' file1 file2 file3
A1
A2
A3
A4
Успех с циклом for ( arr1[1]
не изменен)
$ awk -v FS='[|]' '{for(i=FNR ; i<=NR amp;amp; i<=FNR amp;amp; NR==FNR; i ) {arr1[ k]=$2; print arr1[1]}}' file1 file2 file3
A1
A1
A1
A1
Комментарии:
1. Вы хотите выполнить проверку разных условий для разных файлов при чтении более 3 файлов?
2. да @RavinderSingh13, я буду создавать
arr1[i]=$2
изfile1
,arr2[j]=$3
изfile2
,arr3[k]=$1
изfile3
. затем создайтеarr12
для элементов, которые являются общими вarr1
иarr2
иarr123
для общих элементов, присутствующих вarr1 arr2 arr3
Ответ №1:
Чтобы идентифицировать файлы по порядку, используя GNU awk, несмотря ни на что:
awk '
ARGIND == 1 { do 1st file stuff }
ARGIND == 2 { do 2nd file stuff }
ARGIND == 3 { do 3rd file stuff }
' file1 file2 file3
например, чтобы получить текст в разделе «вывод» в вашем вопросе из 3 предоставленных вами примеров входных файлов:
awk '
ARGIND == 1 { pos = "first" }
ARGIND == 2 { pos = "second" }
ARGIND == 3 { pos = "third" }
{ print "NR:", NR, "FNR" ARGIND ":", NR, "FNR:", FNR, pos " file" }
' file1 file2 file3
NR: 1 FNR1: 1 FNR: 1 first file
NR: 2 FNR1: 2 FNR: 2 first file
NR: 3 FNR1: 3 FNR: 3 first file
NR: 4 FNR1: 4 FNR: 4 first file
NR: 5 FNR2: 5 FNR: 1 second file
NR: 6 FNR2: 6 FNR: 2 second file
NR: 7 FNR2: 7 FNR: 3 second file
NR: 8 FNR3: 8 FNR: 1 third file
NR: 9 FNR3: 9 FNR: 2 third file
или использовать любой awk, если все имена файлов уникальны, независимо от того, пусты они или нет:
awk '
FILENAME == ARGV[1] { do 1st file stuff }
FILENAME == ARGV[2] { do 2nd file stuff }
FILENAME == ARGV[3] { do 3rd file stuff }
' file1 file2 file3
или, если файлы не пусты, то являются ли они уникальными или нет (обратите внимание file1
дважды в списке аргументов):
awk '
FNR == 1 { argind }
argind == 1 { do 1st file stuff }
argind == 2 { do 2nd file stuff }
argind == 3 { do 3rd file stuff }
' file1 file2 file1
если имена файлов могут появляться несколько раз в списке arg, и некоторые файлы могут быть пустыми, то с awk, не относящимся к GNU, становится сложнее, поэтому GNU awk имеет ARGIND, например, что-то вроде (непроверенное):
awk '
BEGIN {
for (i=1; i<ARGC; i ) {
fname = ARGV[i]
if ( (getline line < fname) > 0 ) {
# file is not empty so save its position in the args
# list in an array indexed by its name and the number
# of times that name has been seen so far
arginds[fname, tmpcnt[fname]] = i
}
close(fname)
}
}
FNR == 1 { argind = arginds[FILENAME, cnt[FILENAME]] }
argind == 1 { do 1st file stuff }
argind == 2 { do 2nd file stuff }
argind == 3 { do 3rd file stuff }
' file1 file2 file1
Комментарии:
1. Очень информативно, чисто для понимания. Высоко ценю!
Ответ №2:
Когда дело доходит до трех или более файлов, я не вижу способа, который является вторым и третьим файлом, поскольку оба имеют одинаковое условие !(FNR==NR). Это заставило меня попытаться понять, как может существовать что-то вроде FNR2 и FNR3?
Вот пример:
$ cat f1
X|A1|Z
X|A2|Z
X|A3|Z
X|A4|Z
$ cat f2
X|Y|A3
X|Y|A4
X|Y|A5
$ cat f3
A1|Y|Z
A4|Y|Z
Пример вывода:
$ awk -F '|' 'FNR==1{file }{array[file, FNR]=$0; max=max>FNR?max:FNR}END{for(f=1; f<=file; f ){ for(row=1; row<=max; row ){ key=f SUBSEP row; if(key in array)print "file: "f,"row :"row,"record: "array[key] } }}' f1 f2 f3
file: 1 row :1 record: X|A1|Z
file: 1 row :2 record: X|A2|Z
file: 1 row :3 record: X|A3|Z
file: 1 row :4 record: X|A4|Z
file: 2 row :1 record: X|Y|A3
file: 2 row :2 record: X|Y|A4
file: 2 row :3 record: X|Y|A5
file: 3 row :1 record: A1|Y|Z
file: 3 row :2 record: A4|Y|Z
Объяснение:
awk -F '|' 'FNR==1{ # FNR will reset for every file
file # so whenever FNR==1 increment variable file
}
{
# array name : array
# array key being : file, FNR
# array value : $0 which current record/row
array[file, FNR] = $0;
# here we find which row count in all available files
max = max > FNR ? max : FNR
}
END{ # end block when all files are read
# start iterating over file
# as we now variable file hold total no files read
for(f=1; f<=file; f )
{
# iterate now for record from each file
# variable max holds max row count
for(row=1; row<=max; row )
{
# variable key will now have
# key = file-number SUBSET row-number
key=f SUBSEP row;
# if key exists in array
# print array value
if(key in array)
print "file: "f,"row :"row,"record: "array[key]
}
}
}' f1 f2 f3
Другим вариантом было бы использовать истинные многомерные массивы, как показано ниже. gawk
конкретно, конечно.
Предполагая, что имена файлов уникальны, в противном случае используйте FNR==1{ file }
и вместо FILENAME
использования file
$ awk --version
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2018 Free Software Foundation.
$ awk -F '|' '{
true_multi_array[FILENAME][FNR] = $0
}
END{
for(file in true_multi_array)
for(row in true_multi_array[file])
print "file:",file, "row :" row, "record:" true_multi_array[file][row]
}' f1 f2 f3
file: f1 row :1 record:X|A1|Z
file: f1 row :2 record:X|A2|Z
file: f1 row :3 record:X|A3|Z
file: f1 row :4 record:X|A4|Z
file: f2 row :1 record:X|Y|A3
file: f2 row :2 record:X|Y|A4
file: f2 row :3 record:X|Y|A5
file: f3 row :1 record:A1|Y|Z
file: f3 row :2 record:A4|Y|Z