【问题标题】:Replace a word with multiple lines using sed?使用sed用多行替换一个单词?
【发布时间】:2012-04-23 20:20:49
【问题描述】:

我正在编写一个 bash 脚本,该脚本必须准备发送给用户的电子邮件。

它聚合了一些数据,最终形成多行数据。对于存储在$DATA中的示例。

现在,经过一些 stfw,我发现了一些东西,例如 sed -ei "s/_data_/${DATA}/g" mail.tplsed replace with variable with multiple lines。它们都不起作用。

现在的问题是,如何让sed 用多行文本替换某些内容?

(也欢迎sed 的替代品!)

【问题讨论】:

  • @ZsoltBotykai 你不说? :D 我什至在我的问题中提到了它......
  • 顺便说一句,不要为您的私有 shell 变量使用大写。
  • @tripleee 为什么不呢?
  • 因为这样可以避免与保留变量(大写)发生意外冲突。对于这确切的含义没有达成共识,但是您的脚本的私有变量绝对不是POSIX本条款中所预期的“系统变量”。见stackoverflow.com/q/673055/874188

标签: regex bash sed replace


【解决方案1】:

您可以使用 AWK 使用变量替换来做到这一点。我们可以使用-v 在AWK 中设置一个变量,然后使用AWK 的gsub 函数将所有出现的正则表达式替换为该变量。

例如,如果文件test具有以下内容...

foo
bar
blah _data_and_data_
foo
_data_ foobar _data_ again

... 而 Bash 变量 $DATA 是...

1
2
3
4
5

...然后awk -v r=$DATA '{gsub(/_data_/,r)}1' test 将文件test 中所有出现的正则表达式_data_ 替换为$DATA 的内容,结果如下:

foo
bar
blah 1
2
3
4
5and1
2
3
4
5
foo
1
2
3
4
5 foobar 1
2
3
4
5 again

【讨论】:

  • 天哪,这工作!我希望我能再为你 +1 几次,你救了我!
  • 嘿肯特,这是一个旧的,但您似乎缺少 $DATA 周围的引号。确认?
  • 确实应该添加引号:awk -v r="$DATA" '{gsub(/_data_/,r)}1' test
  • 显然有些 (?) awks 会不喜欢这个并说"newline in string"(尽管DATA 周围没有双引号的答案会以不同的方式失败)。
  • 对 awk-noobs 的简短解释会很好
【解决方案2】:

如果您使用“\n”构建多行文本,则可以使用简单的sed 命令:

DATA=`echo ${DATA} | tr '\n' "\\n"`
#now, DATA="line1\nline2\nline3"
sed "s/_data_/${DATA}/" mail.tpl

【讨论】:

  • 我从那个方法得到sed: -e expression #1, char 32: unterminated 's' command
  • 这不是完全可移植的;并非所有sed 方言都接受\n 有向图来表示文字换行符。在其他一些方言中,您可能需要在文字换行符之前使用反斜杠。此页面上的其他答案会更详细地向您展示如何做到这一点。
  • 再看这个,我认为tr 没有any 版本可以将一个字符替换为两个字符;这只是这项工作的错误工具。换行符意外丢失,因为您错误地没有引用echo 的参数,但这有多种其他不良副作用;而tr 在这里根本什么都不做。 这不应该是公认的答案。
  • 是的对我也不起作用。试试this solution (below)
  • 鉴于 shell 是 Bash,那么就不需要(损坏的)tr 调用 - just use ${DATA//$'\n'/\\n}
【解决方案3】:

我试过sed 's/pattern/\na\nb\nc/g',但它不适用于所有系统。起作用的是在替换模式中添加\,后跟换行符,如下所示:

sed 's/pattern/a\
b\
c/g'

当看到模式时,这会附加一行包含b 和一行包含c

要将其放入变量中,请使用双反斜杠:

export DATA="\\
a\\
b\\
c"

然后:

sed "s/pattern/${DATA}/g"

注意双引号。

【讨论】:

    【解决方案4】:

    您可以将数据放入临时文件并运行:

    $ sed '/_data_/r DATA_FILE' mail.tpl | sed '/_data_/d'> temp; mv temp mail.tpl
    

    【讨论】:

      【解决方案5】:

      我建议简单地将 sed 替换为 perl 命令,如下所示:

      perl -i.bak -pe 's/_data_/$ENV{"DATA"}/g' mail.tpl 
      

      【讨论】:

      • 这是唯一对我有用的 OSX。所有其他基于 sed 和 awk 的方法对我来说都失败了。
      • 如果DATA 包含斜杠,这将会以令人不安的方式失败。但是您可以export DATA 并以$ENV{"DATA"} 从Perl 内部访问它,而不会混淆代码和数据。所以's/_data_/$ENV{"DATA"}/g' 很简单。
      • @anubhava 和 @tripleee,你们拯救了我的一天。这是唯一对我有用的解决方案:sedawk 都不能解决问题。谢谢。
      【解决方案6】:

      戒指持有人的回答对我不起作用;我认为tr 的使用是错误的,它的编写方式只是通过使用echo 来去除换行符。

      相反,我使用了sed。我使用来自another answer 的代码来替换换行符(来源:Zsolt Botykai)。我还期望在我的输入中出现一些美元符号 ($),所以我也处理了这一点。您可能需要添加其他输入处理。请注意在echo 中使用双引号来保留换行符。

      DATA="$(cat whatever)"
      ESCAPED_DATA="$(echo "${DATA}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')"
      

      那么你可以在sed中使用${ESCAPED_DATA}

      cat input | sed 's/one liner/'"${ESCAPED_DATA}"'/' > output 
      

      只是想我会分享。

      【讨论】:

      • 当在带有$'\n' 构造的bash 脚本中构建了多行字符串时,这也很有效(当然,开头的cat 是不需要的,因为字符串已经在一个变量中)。
      【解决方案7】:

      将变量回显到临时文本文件中。

      将文本文件插入 mail.tpl 并从 mail.tpl 中删除 data

      echo ${DATA} > temp.txt    
      sed -i -e "/_data_/r temp.txt" -e "//d" mail.tpl
      

      【讨论】:

      • 在尝试了许多其他解决方案之后,这是唯一一个对我有用的解决方案。
      【解决方案8】:

      不确定您是否尝试将“\n”放入替换部分

      sed 's/[pattern]/\
      [line 1]\n\
      [line 2]\n\
      [line n]\n\
      /g' mail.tpl
      

      出于可读性原因,第一行有 /\。之后的每一行都是一个独立的行,就像您在文本编辑器中找到的那样。最后一行是独立的,再次出于可读性原因。如果需要,您可以制作所有这一条线。当我测试它时,它可以在 Debian Jessie 上运行。

      【讨论】:

      • 这太sed了。:)
      【解决方案9】:

      \ 转义所有换行符(最后一个除外)对我有用。 最后一个换行符不得转义以免破坏s 命令。

      例子:

      DATA="a
      b
      c"
      
      ESCAPED=$(echo "${DATA}" | sed '$!s@$@\\@g')
      echo "${ESCAPED}" 
      a\
      b\
      c
      
      sed "s/pattern/${ESCAPED}/" file
      

      【讨论】:

      • 我必须使用双斜线。
      猜你喜欢
      • 2019-03-07
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 2022-01-19
      • 1970-01-01
      相关资源
      最近更新 更多