簡體   English   中英

重擊:替換整行

[英]Bash : Replace entire line

我正在使用以下行來獲取發生特定字符串的行號:

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時,沒有任何追加。

這是沒有意義的。 只要找到它就將其替換:

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

旁:按以下所述使用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

因此,如果您不希望擴展轉義序列,但ENVIRON更好,但確實需要更多代碼,並且如果需要多次foo的值(例如,在循環中使用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

當然這是行不通的,您需要了解的細微事情是,在調用awk之前發生的用於shell分配的shell變量的名稱不一定總是您想要的shell變量的名稱。獲得值,因為它甚至可能不是您可以分配給的變量:

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

在所有情況下都可以使用的替代方法是在參數列表中指定變量值:

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

但這也有一些警告,因為您不能再簡單地遍歷ARGV來獲取輸入文件的名稱。

現在,讓我們比較兩種腳本的現實發展可能性,一種使用-v ,另一種使用ENVIRON

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

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

現在,假設我們要使用var作為循環的最終值。 這是第三次嘗試時間:

$ 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版本的代碼要簡潔得多。

在上述兩種情況下,除了初始化awk變量然后在其余代碼中使用它之外,您實際上並不想使用ENVIRON [“ var”]。

$ 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}'

關鍵是-擴展轉義符幾乎總是所需的效果,因此編寫更長,更慢,更麻煩的代碼以禁用該效果將不是一種好的默認代碼編寫方法。

恕我直言-除非您不想擴展轉義序列並且不想在賦值中轉義它們,否則請使用-v (最常見的情況是,如您在上面的示例中那樣將要從其賦值的值存儲在shell變量中):

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

最后的想法-在編寫默認情況下使用的shell循環時,我總是告訴人們:

while IFS= read -r var
do
      whatever
done

特別是我說的是默認情況下使用read -r var來阻止轉義在shell變量中擴展,而在awk中我說的是使用awk -v var=導致轉義在awk變量中擴展。

明顯不一致的原因是:shell是一種用於處理文件和進程的工具,並可以順序調用其他工具,而awk是一種用於處理文本的工具。

例如,如果在循環中分配外殼變量,則它應該是文件名上的循環,因此,至關重要的是不要擴展轉義符,否則結果變量將不包含預期的文件名。

如果您分配awk變量,則與操作文本有關,最常見的事情是該文本包含文字標簽,換行符等。不是該文本包含文字\\t s和\\n s,因此通過awk擴展FS='\\t'FS=<a literal tab>就是您要解析由制表符分隔的值的文件的行為。

因此,除非您有其他特殊原因-不要編寫shell變量初始化代碼來擴展轉義,因為給定了shell的用途,這可能不是您想要的,而是編寫awk變量初始化代碼來擴展轉義,因為這可能就是您想要的給定awk的用途。

要替換線路,您可以使用

Sed或Awk

如下例所示,以sed或awk表示NR(記錄數)指定行號

awk'NR == 34 {sub(“ AAA”,“ BBB”)}'

如果要在命令行上指定多個文件,請使用FNR(文件編號記錄)。

awk'FNR == 34 {sub(“ AAA”,“ BBB”)}

' 要么

sed'34s / AAA / BBB /'

您也可以使用$ sign1來使用變量進行替換

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM