使用Bash变量进行sed替换

84

我正在尝试使用Bash脚本中的sed命令更改文本文件中的值,代码行如下:

sed 's/draw($prev_number;n_)/draw($number;n_)/g' file.txt > tmp

这段代码将被放在一个 for 循环中,为什么它不起作用呢?

6个回答

117

在Bash中,单引号'内的变量不会被替换。如果你需要进行字符串替换(或者如果你熟悉Perl的话,叫做插值),你需要使用双引号"替换单引号:

# Enclose the entire expression in double quotes
$ sed "s/draw($prev_number;n_)/draw($number;n_)/g" file.txt > tmp

# Or, concatenate strings with only variables inside double quotes
# This would restrict expansion to the relevant portion
# and prevent accidental expansion for !, backticks, etc.
$ sed 's/draw('"$prev_number"';n_)/draw('"$number"';n_)/g' file.txt > tmp

# A variable cannot contain arbitrary characters
# See link in the further reading section for details
$ a='foo
bar'
$ echo 'baz' | sed 's/baz/'"$a"'/g'
sed: -e expression #1, char 9: unterminated `s' command

更多阅读:


11
也许还要提到,如果变量包含斜杠,您需要使用一个不在字符串中出现的不同分隔符(或反斜杠转义每个分隔符的出现)。斜杠是s的常见默认值,但您可以使用任何标点符号;sed "s%draw($prev_number;n_)%draw($number;n_)%g" file.txts#from#to#gs_from_to_g 等。 - tripleee
如果变量包含换行符("\n"),它将无法正常工作。 - dentex
1
但它曾经逃脱了(对于我的用例),也包括斜杠:changelogEscaped=$(sed -e 's%\/%\\/%g; $!a\'$'\n''\\n' <<<"$changelog" | tr -d '\n') 感谢上面的链接。 - dentex

17

单引号中的变量不会被扩展,但在双引号内会被扩展。在这种情况下请使用双引号。

sed "s/draw($prev_number;n_)/draw($number;n_)/g" file.txt > tmp

你也可以使用 eval 让它起作用,但不要那样做!!


谢谢,那个起作用了。你介意解释一下你所说的“扩展”是什么意思吗?我的直觉告诉我应该是类似于变量替换之类的东西。另外,为什么不使用eval? - csta

4
这可能有所帮助:
sed "s/draw($prev_number;n_)/draw($number;n_)/g" 

1
您可以像下面这样使用变量。就像这里一样,我想替换文件中的系统变量hostname。我正在查找字符串look.me并将整行替换为look.me=<system_name>
sed -i "s/.*look.me.*/look.me=`hostname`/"

你还可以将系统值存储在另一个变量中,并使用该变量进行替换。 host_var=`hostname` sed -i "s/.*look.me.*/look.me=$host_var/"

输入文件:

look.me=demonic

文件输出(假设我的系统名称为prod-cfm-frontend-1-usa-central-1):
look.me=prod-cfm-frontend-1-usa-central-1

1

另一种变体,使用 printf:

SED_EXPR="$(printf -- 's/draw(%s;n_)/draw(%s;n_)/g' $prev_number $number)"

sed "${SED_EXPR}" file.txt

或者在一行中:

sed "$(printf -- 's/draw(%s;n_)/draw(%s;n_)/g' $prev_number $number)" file.txt

使用printf构建替换表达式应该可以安全地防止各种奇怪的事情发生,这就是为什么我喜欢这个变体。

1
我需要在 Github Actions 中从我的发布中输入 Github 标签。这样,当发布时,它将自动打包并推送代码到 Artifactory。
以下是我的做法。 :)
  - name: Invoke build
    run: |
      # Gets the Tag number from the release
      TAGNUMBER=$(echo $GITHUB_REF | cut -d / -f 3)
      
      # Setups a string to be used by sed
      FINDANDREPLACE='s/${GITHUBACTIONSTAG}/'$(echo $TAGNUMBER)/
      
      # Updates the setup.cfg file within version number
      sed -i $FINDANDREPLACE setup.cfg
      
      # Installs prerequisites and pushes 
      pip install -r requirements-dev.txt
      invoke build

回想起来,我希望我用Python编写并进行测试。不过,使用Bash也是有趣的体验。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接