使用sed用多行替换一个单词?

Posted

技术标签:

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

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

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

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

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

(也欢迎sed 的替代品!)

【问题讨论】:

sed replace with variable with multiple lines的可能重复 @ZsoltBotykai 你不说? :D 我什至在我的问题中提到了它...... 顺便说一句,不要为您的私有 shell 变量使用大写。 @tripleee 为什么不呢? 因为这样可以避免与保留变量(大写)发生意外冲突。对于这确切的含义没有达成共识,但是您的脚本的私有变量绝对不是POSIX本条款中所预期的“系统变量”。见***.com/q/673055/874188 【参考方案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

【讨论】:

我必须使用双斜线。

以上是关于使用sed用多行替换一个单词?的主要内容,如果未能解决你的问题,请参考以下文章

使用 sed 替换多行

sed 用多次出现的单词替换单词分隔符

在 bash 脚本中使用 sed 替换多行模式

正则表达式仅用替换文件中的单词替换单词

如何使用 sed 或 awk 正确查找和替换多行文本?

使用 grep 和 sed 在 shell 中查找和替换同一文件中的多行