У меня есть запись предметов с их идентификатором и ценой, и я пытаюсь обновить цену некоторых из этих предметов в соответствии с другим «справочным файлом», формат которого — Item# NewPrice OutFile. В конечном итоге у нас будет столько выходных записей, сколько # строк в ref-файле (здесь 4 выхода). OutFiles будут аналогичны исходным файлам, за исключением скорректированного элемента. Ниже мои файлы и скрипт awk, который не работает. Я ценю вашу помощь.
Main file [Item # current_value]:
Item 1 20
Item 2 30
Item 3 40
Reference file [Item# new_value outputfile]
1 22 It1_22
1 25 It1_25
2 32 It2_32
3 45 It3_45
Expected Output files:
(It1_22)
Item,1,22
Item,2,30
Item,3,40
(It1_25)
Item,1,25
Item,2,30
Item,3,40
(It2_32)
Item,1,20
Item,2,32
Item,3,40
(It3_45)
Item,1,20
Item,2,30
Item,3,45
Мой скрипт не генерирует никаких файлов:
awk 'BEGIN{OFS = ","};FNR == NR{a[$1]=$2;d=$3;next} $2 in a{print $1,$2,a[$2];next}1' ref main > d
Я расширил все нужные мне выходные файлы. Надеюсь, это объясняет мою логику. Для любого вывода будет изменен только назначенный элемент в справочном файле. Сценарий должен просматривать справочный файл и обновлять только определенный элемент в каждой строке.
Хорошо объяснил, и вы получили ответ быстро. Я бы и сам не справился лучше (вероятно, даже хуже).
вот одно решение без какой-либо обработки ошибок для отсутствующих значений и т. д. Чтобы сохранить порядок файла данных, свяжите каждый с номером строки и восстановите во время печати.
$ awk -v OFS=, 'NR==FNR{k=$2; n[k]=$1; v[k]=$3; ord[NR]=k; next}
{for(i=1;i<=length(ord);i++)
{k=ord[i];
print n[k],k,k==$1?$2:v[k] > $3}}' mainfile reffile
$ head It*
==> It1_22 <==
Item,1,22
Item,2,30
Item,3,40
==> It1_25 <==
Item,1,25
Item,2,30
Item,3,40
==> It2_32 <==
Item,1,20
Item,2,32
Item,3,40
==> It3_45 <==
Item,1,20
Item,2,30
Item,3,45
Большое спасибо. Это умный код. Единственное, выходные строки идут в обратном порядке, т.е. Item,3,40 , затем Item2 и последний Item 1. Легко ли это исправить?
поскольку массивы на самом деле являются ассоциативными массивами, порядок итерации может отличаться от порядка вставки, хотя для небольшого количества записей с целочисленными ключами его следует сохранить. Несмотря на это, я добавил еще один массив, чтобы сохранить первоначальный порядок.
Я получаю синтаксическую ошибку с новым кодом: Синтаксическая ошибка Контекст: >>> ,NR==FNR{k=$2; п[к]=$1; v[k]=$3; порядок[ <<<
выглядит нормально для меня, убедитесь, что вы правильно скопировали / вставили. Кажется, перед NR стоит запятая, которой не должно быть в вашем сообщении об ошибке.
Я подумал, что это как-то связано с оператором ord[NR]=k. Не уверен, что?
Это не должно быть реальной проблемой со сценарием в том виде, в котором он написан, но ord()
— это имя функции в библиотеке ordchr
GNU awk, поэтому обычно избегайте его использования в качестве имени переменной.
length(ord)
будет терпеть неудачу в некоторых awks, поскольку это неопределенное поведение в POSIX, где length()
может принимать только строковый аргумент, а не массив.
Вы также можете столкнуться с ошибкой «слишком много открытых файлов» из-за того, что не закрыли выходные файлы по ходу работы, это зависит только от того, сколько выходных файлов создает скрипт и какой вариант awk вы используете.
Вы также можете столкнуться с проблемами с тернарным выражением без скобок в некоторых контекстах в некоторых awks, см. unix.stackexchange.com/questions/588714/….
Не могли бы вы вкратце объяснить, как работала первая версия скрипта? awk -v OFS=, 'NR==FNR {n[$2]=$1; v[$2]=$3; next} {for(k in n) print n[k],k,k==$1?$2:v[k] > $3}' main ref
Использование GNU awk для массивов массивов и неограниченного количества открытых файлов:
$ cat tst.awk
BEGIN { OFS = "," }
NR == FNR {
vals[$3][$1] = $2
next
}
{
for ( fname in vals ) {
print $1, $2, ($2 in vals[fname] ? vals[fname][$2] : $3) > fname
}
}
$ awk -f tst.awk ref main
$ head It*
==> It1_22 <==
Item,1,22
Item,2,30
Item,3,40
==> It1_25 <==
Item,1,25
Item,2,30
Item,3,40
==> It2_32 <==
Item,1,20
Item,2,32
Item,3,40
==> It3_45 <==
Item,1,20
Item,2,30
Item,3,45
Вы можете сделать то же самое с помощью любого awk, просто он будет работать немного медленнее, так как должен открывать/закрывать каждый выходной файл для каждой записи, в отличие от gawk, где он будет делать это только при необходимости, чтобы избежать «слишком большого количества открытых файлов». файлы» ошибка:
$ cat tst.awk
BEGIN { OFS = "," }
NR == FNR {
vals[$3,$1] = $2
fnames[$3]
printf "" > $3
close($3)
next
}
{
for ( fname in fnames ) {
print $1, $2, ((fname,$2) in vals ? vals[fname,$2] : $3) >> fname
close(fname)
}
}
Эд, большое спасибо за сценарии. Интересно, не могли бы вы вкратце объяснить поток первой версии скрипта от karakfa? awk -v OFS=, 'NR==FNR {n[$2]=$1; v[$2]=$3; next} {for(k in n) print n[k],k,k==$1?$2:v[k] > $3}' main ref
Пожалуйста. Вы должны попросить @karakfa объяснить их сценарий.
Ваш последний код быстрый и полезный. Я пытаюсь понять это. Можете ли вы объяснить логику и поток?
Как ни странно, я получаю сообщение об ошибке при повторном запуске кода как tst.awk ref main --> C:/Program Files (x86)/MKS Toolkit/mksnt/awk.exe: Синтаксическая ошибка Контекст: >>> \ << < Есть мысли?
Поскольку в моем сценарии нет \
, я думаю, вы неправильно скопировали/вставили его и куда-то добавили \
.
Я думаю, что сценарий довольно прост после беглого взгляда на справочные страницы awk, но есть ли у вас что-то конкретное, что вы нашли в нем неясным, что вы хотели бы, чтобы я разъяснил?
Конкретно не могу понять vals[$3,$1] = $2. Что делает эта строка? К чему относится каждое поле?
$1
— первое поле (значение через пробел в текущей строке ввода), $2
второе поле, $3
третье поле. vals[]
— это массив, проиндексированный конкатенацией $3
и $1
и содержащий $2
по этому индексу. См. gnu.org/software/gawk/manual/gawk.html#Массивы.
Объясните свою логику для желаемого результата. It1_22 заменяет цену товара 1 с 20 на 22. Хорошо. Но It2_34 заменяет цену товара 1 и цену товара 2. Там должно быть 5 файлов нет? Или вам нужна самая высокая цена только для каждого товара? Самая высокая цена за пункт 1 составляет 26, так что ...