Используйте файл списка для редактирования главного файла через awk

все. У меня есть два файла: ports.lst и master.tbl

ports.lst выглядит так:

hawaii-P1 hawaii-P2 hawaii-P3 losangeles-P1 losangeles-P3 

master.tbl выглядит так:

 #Site 1 Honolulu servername HAWAII-A hawaii-P1 InitFileA OutFileA otherfields servername HAWAII-A hawaii-P2 InitFileA OutFileA otherfields #servername HAWAII-A hawaii-P3 InitFileA OutFileA otherfields servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields #Site 16 Dallas servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields #Site 8 LA #servername LOSANGELES-A losangeles-P1 InitFileA OutFileA otherfields servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields #servername LOSANGELES-A losangeles-P3 InitFileA OutFileA otherfields 

Мне нужно найти файл master.tbl для каждого порта, указанного в port.lst, и заменить «InitFileA» и «OutFileA», оставив файл следующим образом:

 #Site 1 Honolulu servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields #servername HAWAII-A hawaii-P3 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields #Site 16 Dallas servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields #Site 8 LA #servername LOSANGELES-A losangeles-P1 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields #servername LOSANGELES-A losangeles-P3 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields 

Вот где я, прямо сейчас, но это терпит неудачу – понятно.

 awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=(echo $3| awk -F '-' {print $1});$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl 

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

 awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", $c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl 

Я вытаскиваю свои волосы из-за этого. Может ли кто-нибудь здесь дать представление о том, что я делаю неправильно?

У вас есть правильная основная идея разбить задачу на два раунда, но затем вы идете и вызываете awk в правиле awk .. вот где я перестала его читать; это слишком сложный способ решить такую ​​простую проблему.

Рассмотрим этот фрагмент awk:

 awk 'BEGIN { RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n)"; FS = "[\t\v\f ]+" } FNR==1 { file++ } /^#/ { next } file==1 { port[$1] = $1 } file>=2 && ($3 in port) { base = $3; sub(/-[^-]*$/, "", base); $4 = "InitFileB-" base "-username-ALPHA-password"; $5 = "OutFileB-" base "-username-ALPHA"; } file>=2 { printf "%s\n", $0 } ' ports.lst master.tbl 

Примечание. Я добавил необходимые точки с запятой, чтобы вы могли написать все вышеперечисленное в одной строке.

Если вы запустите приведенный выше пример с входными файлами примера, вы получите

 losangeles-P1 losangeles-P3 servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields 

Правило BEGIN просто устанавливает универсальную поддержку новой строки, если файлы были перенесены из какой-либо другой системы (например, Windows) с другой кодировкой новой строки.

Правило FNR==1 используется для обновления file переменной, так что оно отражает обрабатываемый файл (1 для первого, 2 секунды).

Правило /^#/ { next } пропускает все строки, начинающиеся с метки хэша. Это комментарии, поэтому их не нужно хранить. Мы могли бы также добавить правило /^[\t\v\f ]*$/ { next } чтобы пропустить все пустые строки, если вы хотите сжать выходной файл.

Правило file == 1 { port[$1] = $1 } добавляет все первые поля в первом файле в port ассоциативного массива. Значение, присвоенное ( = $1 ), не имеет значения, поэтому мы могли бы просто использовать здесь = 0 .

Правило file >= 2 && ($3 in port) применяется ко второму и любым последующим файлам и выполняется, если третье поле соответствует одному из ключей в port ассоциативного массива. (Значения не имеют значения, проверяются только ключи). Другими словами, это правило применяется только тогда, когда третье поле является одним из ключей, указанных в списке портов.

Третье поле копируется в переменную base – это соответствует одному из ключей в port[] -, и все после последнего - удаляется с помощью sub() . Затем мы модифицируем четвертое и пятое поля. Обратите внимание, что в awk нет оператора конкатенации строк; мы просто указываем строки рядом друг с другом. Другими словами, ("foo" a "bar") представляет собой одну строку, состоящую из «foo», за которой сразу следует значение переменной a преобразованное в строку, сразу же за которым следует «bar».

Окончательное правило печатает (возможно, измененную) запись, но обеспечивает использование \n новой строки. Используются только записи во втором и последующих файлах.

Теперь, если ports.lst содержит соответствующие имена пользователей и пароли, я бы немного изменил выше (возможно, три строки изменились?), Но я надеюсь, что вы сможете увидеть общий подход.

Кажется, я понял ответ. Моя проблема, похоже, заключалась в размещении знаков доллара, рядом с переменной «c». Для этого это сработало:

 awk 'NR==FNR{z[$0];next} { if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl 

Теперь, что касается того, как это работает, я боюсь, что не могу объяснить. Я буквально хватался за соломинку и отчаялся. Я прибегал к удалению слоев команды, пока ошибка не исчезла, а затем медленно добавляла команды и возилась до тех пор, пока каждый из них не работал.

Я не делаю достаточно awk-скриптов, чтобы просто вводить инструкции, но я бы хотел найти способ использовать структуру «master» и иметь несколько блоков.

План концептуального решения

 BEGIN { # get it ready ... } /^$/ { # maybe just skip lines # otherwise potential post processing for #Site XX Name } /^#Site/ { # initialize processing for a new site } { # default block for the site processing 'input' } FINISH { # 'master' is parsed - now fill in the blanks using 'port' # ie, start of second pass to complete the work } 

Я знаю, что в этом нет никаких awk-команд, но мне также интересно узнать, видят ли специалисты awk это общий подход к awk-решениям. Слишком часто меня отключает использование awk, потому что я теряюсь в «однострочных awk-операторах», т. Е. Одна команда, которая будет обрабатывать все строки независимо от того, есть ли повторяющиеся блоки ввода.

И даже если это окажется смехотворным подходом – надеюсь, что комментарии просветят меня (и других) таким образом, что мне станет лучше с awk. Благодаря!