簡體   English   中英

使用 Shell 變量替換 sed 字符串

[英]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.

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