[英]Sed string substitution with Shell Variable
輸入:
$ cat testing.list
deb [trusted=yes] http://10.47.4.220/repos/test-repo/11 /
$ echo $REPOVER
12
預期輸出:
$ cat testing.list
deb [trusted=yes] http://10.47.4.220/repos/test-repo/12 /
請注意最后數字的變化。
用單引號嘗試 1(不工作):
$ sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1$REPOVER\3#' testing.list
deb [trusted=yes] http://10.47.4.220/repos/test-repo/$REPOVER /
嘗試 1.1 帶單引號而不帶變量(工作):
$ sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\112\3#' testing.list
deb [trusted=yes] http://10.47.4.220/repos/test-repo/12 /
用雙引號嘗試 2(不工作):
$ sed -r "s#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1$REPOVER\3#" testing.list
sed: -e expression #1, char 44: unterminated `s' command
嘗試 2.1 帶雙引號而不帶變量(不工作):
$ sed -r "s#(.*/)([[:digit:]]+)([[:space:]]+/)$#\112\3#" testing.list
sed: -e expression #1, char 44: unterminated `s' command
所以,似乎sed
不喜歡double quotes
。 但是,除非我使用double quotes
否則 Shell 不會擴展變量。
FWIW:
$ bash --version | head -1
GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)
$ sed --version
sed (GNU sed) 4.7
Packaged by Debian
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
$#
是 shell 中帶有多個參數的變量。
$ ( set -x ; sed -r "s#(.*/)([[:digit:]]+)([[:space:]]+/)$#\112\3#" )
+ sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)0\112\3#'
sed: -e expression #1, char 44: unterminated `s' command
所以$#
被0
替換(在我的 shell 中,它是交互式的,沒有參數)。
研究單引號和雙引號之間的區別。 研究何時在 shell 中引用。 研究引用在 shell 中是如何工作的。 你似乎想要:
sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1'"$REPOVER"'\3#'
您的嘗試 2 幾乎是正確的。 只需將要從 shell 擴展中保留的$
符號轉義即可:
sed -r "s#(.*/)([[:digit:]]+)([[:space:]]+/)\$#\1$REPOVER\3#" testing.list
另一種選擇是混合使用單引號和雙引號:
sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1'"$REPOVER"'\3#' testing.list
但是,只有當您的變量的值不包含 sed 會解釋的特殊字符時,所有這些才有效。 如果你有這樣的字符,你需要先轉義它們。 例如,如果REPOVER
的值可以包含#
、 &
或\\
字符,您可以:
ESCAPED_REPOVER="$(echo "$REPOVER" | sed -r 's/(#|\\|&)/\\\1/g')"
sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1'"$ESCAPED_REPOVER"'\3#' testing.list
最后一個復雜的,以防REPOVER
真的可以是任何東西,包括結束符、bash 和 sed 特殊字符等。但是我們假設它本身不包含以deb
開頭的行(如果它包含我們需要一個自定義分隔符,但是原理幾乎相同)。
我們首先打印變量,然后 cat 文件,並將所有這些傳遞給 sed。 sed 腳本首先收集保持空間中的前導碼,即排除第一個^deb
行之前的所有內容。 然后它使用保留空間的內容在它遇到的所有^deb
行中進行替換。 序言后的非^deb
行未經修改地打印:
{ printf '%s\n' "$REPOVER" ; cat testing.list; } | \
sed -rn '1,/^deb/{/^deb/!{H;b;}};/^deb/{G;s#[[:digit:]]+([[:space:]]+/)\n\n(.*)#\2\1#;};p'
演示:
$ cat testing.list
deb [trusted=yes] http://10.47.4.220/repos/test-repo/11 /
deb [trusted=yes] http://10.47.4.220/repos/foo-repo/11 /
# comment
deb [trusted=yes] http://10.47.4.220/repos/bar-repo/11 /
# another comment
$ printf -v REPOVER '\\$&#'
$ { printf '%s\n' "$REPOVER" ; cat testing.list; } | \
sed -rn '1,/^deb/{/^deb/ba;H;b;};:a;/^deb/{G;s#[[:digit:]]+([[:space:]]+/)\n\n(.*)#\2\1#;};p'
deb [trusted=yes] http://10.47.4.220/repos/test-repo/\$&# /
deb [trusted=yes] http://10.47.4.220/repos/foo-repo/\$&# /
# comment
deb [trusted=yes] http://10.47.4.220/repos/bar-repo/\$&# /
# another comment
根據提供的示例數據,可以稍微簡化建議的sed
:
$ sed -r "s|/[0-9]+ |/${REPOVER} |" testing.list
$ sed -r "s|/[[:digit:]]+ |/${REPOVER} |" testing.list
查找模式/<at_least_one_number><space>
並將其替換為/${REPOVER}<space>
兩者都產生:
deb [trusted=yes] http://10.47.4.220/repos/test-repo/12 /
更改分隔符並將其放在雙引號中對我有用。
sed -r "s|(.*/)([[:digit:]]+)([[:space:]]+/)$|\1$REPOVER\3|"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.