【问题标题】:Sed string substitution with Shell Variable使用 Shell 变量替换 Sed 字符串
【发布时间】:2021-09-28 20:52:42
【问题描述】:

输入:

$ 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>.

【问题讨论】:

    标签: linux bash shell sed


    【解决方案1】:

    $# 是 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#'
    

    【讨论】:

    • 谢谢。我真的没有意识到 shell 从我的 sed 表达式中处理 $# 字符串的方式。我专注于其他事情。
    【解决方案2】:

    您的尝试 2 几乎是正确的。只需转义您希望从 shell 扩展中保留的 $ 标志:

    sed -r "s#(.*/)([[:digit:]]+)([[:space:]]+/)\$#\1$REPOVER\3#" testing.list
    

    另一种选择是混合使用单引号和双引号:

    sed -r 's#(.*/)([[:digit:]]+)([[:space:]]+/)$#\1'"$REPOVER"'\3#' testing.list
    

    但只有当变量的值不包含 sed 会解释的特殊字符时,所有这些才有效。如果您有这样的字符,您将需要先转义它们。例如,如果REPOVER 的值可以包含#&amp;\ 字符,您可以:

    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
    

    【讨论】:

    • 感谢您的解释。这无疑为我提供了更多探索的指导,以提高我的整体理解。
    【解决方案3】:

    根据提供的样本数据,提议的sed可以简化一点:

    $ sed -r "s|/[0-9]+ |/${REPOVER} |" testing.list
    $ sed -r "s|/[[:digit:]]+ |/${REPOVER} |" testing.list
    

    查找模式/&lt;at_least_one_number&gt;&lt;space&gt;并将其替换为/${REPOVER}&lt;space&gt;

    两者都产生:

    deb [trusted=yes] http://10.47.4.220/repos/test-repo/12  /
    

    【讨论】:

    • sed 总是发生在我身上。每次我尝试做某事时,我所做的任何事情都会有一个简化的解决方案:)
    【解决方案4】:

    更改分隔符并将其放在双引号中对我有用。

    sed -r "s|(.*/)([[:digit:]]+)([[:space:]]+/)$|\1$REPOVER\3|"
    

    【讨论】:

    • 就是这样。 # 分隔符导致$# 字符串出现问题。因此将分隔符更改为| 是完美的。
    猜你喜欢
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    • 2015-04-18
    • 1970-01-01
    • 2021-07-04
    • 2015-11-18
    • 1970-01-01
    • 2014-01-09
    相关资源
    最近更新 更多