Bash
用變數替換文件內容中匹配的正則表達式
我想做以下事情
^[ \t]*services:[ \t]*
使用模式 ( )搜尋文件內容- 如果找到,將模式替換為字元串(在變數中)
- 否則,將字元串(在變數中)附加到文件末尾
我被困在第 2 步,腳本的第 2 步將
services:
在文件中替換為${serviceLoopBack}
. 我的sed
命令有什麼問題?測試腳本
#!/bin/bash set -e destfile=config.yml serviceLoopBack="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route" serviceLoopBackWithFrontSpace="|NL||NL|$serviceLoopBack" if grep -E "^[ \t]*services:[ \t]*" $destfile then # service tag found, replace target string sed -i -e 's/^[ \t]*services:[ \t]*/${serviceLoopBack}/g' $destfile else # service tag not found, append at end of file echo $serviceLoopBackWithFrontSpace >> $destfile fi # replace separator 1 to new line sed -i -e 's/|NL|/\n/g' $destfile # replace separator 2 to space sed -i -e 's/|FS|/ /g' $destfile
問題是在您的第二次
sed
呼叫中,您已將程序括在單引號 (' ... '
) 中。雖然這是推薦的做法,但單引號會阻止 shell 擴展 shell 變數,例如${serviceLoopBack}
,因此文本將保持逐字不變,而不是被 shell 變數的內容替換。一種可能性是使用雙引號而不是單引號,如
sed -i -e "s,^[ \t]*services:[ \t]*,${serviceLoopBack}," "$destfile"
或者,使用以下命令將表達式組裝到
sed
shell 變數中printf
:printf -v prog 's,^[ \t]*services:[ \t]*,%s,' "$serviceLoopBack" sed -i -e "$prog" "$destfile"
請注意,我使用
,
as 分隔符而不是/
,因為您的替換文本/
也包含,否則會混淆sed
. 另外,我省略了該g
選項,因為我假設每行只有一個匹配項。無論如何,您可能都想查看您的 shell 腳本中的引用,例如,請參閱此答案以了解原因。
但是,如果
sed
不是嚴格要求,幾乎可以將整個 shell 腳本替換為一個awk
程序(這樣更快):awk -v slb="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route" \ '/^[ \t]*services:/{$0=slb;f=1} {gsub(/\|NL\|/,"\n"); gsub(/\|FS\|/," ")} 1; END{if (!f) {print "\n\n" slb}}' "$destfile"
awk
這將在變數中導入“服務環回”規範slb
。- 然後它將檢查是否有任何行與
services:
模式匹配,如果是,則將整行替換為儲存在slb
其中的字元串(我推斷這是您實際想要實現的)。同時,將標誌設置f
為 1。- 使用
gsub()
,用|NL|
換行符替換所有出現的 ,|FS|
用空格替換所有出現的 。- 看似“流浪”的
1
指示awk
列印目前行,包括到目前為止所做的所有修改。- 最後,如果
f
仍未設置,則將“服務循環返回”行附加到前導模式(但是,已經轉換為\n\n
)。請注意,除非您有理解該選項
awk
的實現,否則不會就地編輯文件。您可能必須將輸出重定向到臨時文件,然後用臨時文件替換。awk``-i inplace``$destfile
但是,總而言之,由於您使用的是結構化語言 (YAML),因此使用專用解析器(例如,
yq
代替面向行的文本處理工具)可能是值得的。