![](/img/trans.png)
[英]bash: How to replace an entire line in a text file by a part of its content
[英]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.