2015-09-30 2 views
0

Я использую следующую строку, чтобы получить номер строки, в которой происходит конкретная строка:Bash: Заменить всю строку

nLine=$(awk '/text/{ print NR; exit }' $1) 
echo "line = $nline" 

возвращается:

line = 78 

Теперь я хотел бы заменить это конкретную линию с другой строкой, используя:

awk 'NR==$nLine {$0="new text $2"} 1' test.xml 

Где $ 2 - это параметр, присвоенный скрипту bash.

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

awk 'NR==78 {$0="new text data"} 1' test.xml 

, но он никогда не работает, как ожидается, когда параметры приведены в команде ..

Кроме того, возможно ли избежать печати в терминале? Потому что когда я положил>/dev/null в конце строки, ничего не добавляется.

+0

pass bash variables to awk using '-v'. –

+0

Он работает для первого параметра, если я использую awk -v line = $ nLine 'NR == line ..Но, похоже, это не работает для второго. В самом деле. awk -v line = $ nLine second = $ 2 'NR == line {$ 0 = "new text second"} 1' test.xml не работает должным образом. – ogs

+0

Вы пробовали 'awk -v line = $ nLine -v second = $ 2 'NR == line {$ 0 =" new text "second} 1' test.xml'? В любом случае, ответ на ваш [другой вопрос] (http://stackoverflow.com/questions/32863518/bash-inexplicable-behavior-sed), вероятно, делает этот вопрос ненужным. –

ответ

0

Это не имеет никакого смысла. Просто замените его, когда вы найдете его:

awk -v nText="$2" '/text/{$0="new text " nText} 1' test.xml 

ASIDE: WRT использование ENVIRON, как описано ниже, считают это:

$ foo='a\tb' 
$ printf '%s\n' "$foo" 
a\tb 
$ awk -v foo="$foo" 'BEGIN{ print foo }' 
a  b 
$ foo="$foo" awk 'BEGIN{ print ENVIRON["foo"] }' 
a\tb 

Итак, ЭНВАЙРОН лучше, если вы не хотите escape-последовательность расширена, но для этого требуется больше кода, и она менее эффективна, если вам нужно значение foo несколько раз, например в цикле (в этом случае вы бы использовали еще больше кода и больше дублирования слова foo: foo="$foo" awk 'BEGIN{ foo=ENVIRON["foo"]; print foo }'.)

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

$ set -- 'a\tb' 
$ printf '%s\n' "$1" 
a\tb 
$ awk -v foo="$1" 'BEGIN{ print foo }' 
a  b 
$ 1="$1" awk 'BEGIN{ print ENVIRON["1"] }' 
-bash: 1=a\tb: command not found 

Конечно, это не работает, и тонкая вещь, которую вы должны быть осведомлены о том, что имя переменной оболочки для задания оболочки что происходит перед Teh вызова AWK не всегда может быть именем переменной оболочки вы хотите, чтобы получить значение, как это не может даже быть переменной можно присвоить:

$ foo="$1" awk 'BEGIN{ print ENVIRON["foo"] }' 
a\tb 

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

$ awk 'BEGIN{ foo=ARGV[1]; ARGV[1]=""; print foo }' "$1" 
a\tb 

, но это также имеет оговорки, так как вы можете больше не просто прокручивать ARGV, чтобы получить имена ваших входных файлов.

Теперь давайте сравним самые реалистичные возможности эволюции 2 сценариев, один с помощью -v и один ENVIRON:

$ awk -v var=100000000 'BEGIN{ print var }' 
100000000 

$ var=100000000 awk 'BEGIN{ print ENVIRON["var"] }' 
100000000 

Теперь предположим, что мы хотим использовать var в качестве конечного значения для цикла.Вот 3-ий пример:

$ time awk -v var=100000000 'BEGIN{ for (i=1;i<=var;i++) i }' 
real 0m7.813s 
user 0m7.706s 
sys  0m0.031s 

$ time var=100000000 awk 'BEGIN{ for (i=1;i<=ENVIRON["var"];i++) i }' 
real 0m11.673s 
user 0m11.637s 
sys  0m0.031s 

Обратите внимание, насколько менее эффективна версия ENVIRON.

С другой стороны, что делать, если вам нужно просто использовать его пару раз в скрипте:

$ awk -v var=100000000 'BEGIN{ print var; if (var > 5) var = 5; print var }' 
100000000 
5 

$ var=100000000 awk 'BEGIN{ print ENVIRON["var"]; if (ENVIRON["var"] > 5) ENVIRON["var"] = 5; print ENVIRON["var"] }' 
100000000 
5 

Обратите внимание, насколько менее лаконичный код для версии ENVIRON есть.

В обоих вышеуказанных случаях, то вы не действительно хотите использовать ENVIRON [ «переменной»], кроме как инициализировать переменную AWK, а затем использовать это в остальной части кода:

$ time var=100000000 awk 'BEGIN{ var=ENVIRON["var"]; for (i=1;i<=var;i++) i }' 
real 0m7.692s 
user 0m7.612s 
sys  0m0.031s 

$ var=100000000 awk 'BEGIN{ var=ENVIRON["var"]; print var; if (var > 5) var = 5; print var }' 
100000000 
5 

поэтому если вы не хотите, чтобы переписать код, как он развивается, если вы собираетесь использовать ENVIRON то эквивалент:

awk -v var=val 'BEGIN{ print var }' 

не

var=val awk 'BEGIN{ print ENVIRON["var"] }' 

, но вместо этого:

var=val awk 'BEGIN{ var=ENVIRON["var"]; print var }' 

, который является длительным и повторы в лучшем случае по сравнению с использованием -v.

что-то еще, чтобы рассмотреть, что, когда вы разборе файла и каждая запись отделена символом новой строки (предположим, что это не по умолчанию) вы хотите написать:

awk -v RS='\n' '1' 

или

awk -v RS="$'\n'" '1' 

установить RS на новую строку? Первая из них является, конечно же, гораздо более удобным и интуитивно понятно, и вы, конечно, не хотите, чтобы написать:

RS="$'\n'" awk 'BEGIN{ RS=ENVIRON["RS"] } 1' 

насчет FS быть вкладка:

awk -v FS='\t' '{print NF}' 

против

FS="$'\t'" awk 'BEGIN{ FS=ENVIRON["FS"] } {print NF}' 

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

IMHO - просто используйте -v, если вы не хотите, чтобы escape-последовательности были расширены и не хотели их избегать в назначении (чаще всего, когда значение, которое вы назначаете, хранится в переменной оболочки, как в приведенном выше примере):

$ awk -v foo='a\tb' 'BEGIN{ print foo }' 
a  b 
$ awk -v foo='a\\tb' 'BEGIN{ print foo }' 
a\tb 

Final мысль - я постоянно говорю людям при написании циклов оболочки для использования по умолчанию:

while IFS= read -r var 
do 
     whatever 
done 

в частности, я имею в виду использовать read -r var по умолчанию, чтобы остановить побег расширяется в shell var iable, хотя в awk я говорю, используя awk -v var=, чтобы вызвать расширение esk в переменной awk.

Причина очевидной несогласованности заключается в том, что оболочка - это инструмент для управления файлами и процессами и последовательные вызовы другим инструментам, в то время как awk - это инструмент для управления текстом.

Если вы назначаете переменную оболочки в цикле, например, она должна быть петлей на имена файлов, поэтому критически важно не расширять экраны, или результирующая переменная НЕ будет содержать имя файла, как предполагалось.

Если присвоить переменную AWK, то у него есть что-то делать с манипулирует текстом и наиболее распространенным является то, что текст содержит буквенные символы табуляции, новой строки и т.д. НЕ, что текст содержит буквенные \t с и \n сек Итак, AWK расширяется FS='\t' до FS=<a literal tab>, который является поведением, которое вы хотите для разбора файла с разделителями табуляции.

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

+0

Вы должны передать '$ shell_var' в' awk' через переменную среды, например 'nText = $ 2 awk '/ text/{$ 0 =" новый текст "ENVIRON [" nText "]}; 1''. Использование '-v var =" $ shell_var "', 'awk' будет расширять экранированные последовательности в' $ shell_var'. Также обратите внимание на использование '; 1' для POSIX-совместимого. – cuonglm

+0

См. Http://austingroupbugs.net/view.php?id=226 для требования о ';'. 'ENVIRON' требуется POSIX, и у nawk это есть. AFAICT, я вижу только «ястреб» из сундука из реликвии, у него его нет. Также обратите внимание, что эта переменная доступна только через сеанс 'awk', она прошла после завершения awk. – cuonglm

+0

Это не разрешает ''/x/{print} {print} '', что означает, что вы должны вставить'; ', чтобы сделать его действительным' awk'. Существует возможность найти реализацию POSIX 'awk', если отсутствует'; '. Добавление ';' никогда не прерывать существующий скрипт, поскольку, когда это действительная форма по грамматике. Я не вижу здесь эффективности? В этом случае, как вы можете передать литерал '\ t'? Использование '-v var' расширит его до tab, почему' ENVIRON' не делает. Использование '-v var' для получения внешнего ввода не является надежным. – cuonglm

0

Для замены линии (ы) можно либо использовать

Sed или Awk

Укажите номер строки в SED или NR (номер записи) в AWK, как показано в приведенном ниже примере

AWK 'NR == 34 {суб ("ААА", "ВВВ")}'

или используйте FNR (запись номера файла), если вы хотите указать более одного файла в командной строке.

AWK 'FNR == 34 {суб ("ААА", "ВВВ")}

' или

SED '34s/ААА/ВВВ /'

Вы также можете использовать переменные для замены, используя $ sign1

Смежные вопросы