使用sed匹配包含换行符的字符串

时间:2014-05-24 23:59:37

标签: linux bash ubuntu awk sed

我有一个像这样的字符串:

    #
    pap

基本上翻译为\t#\n\tpap,我想将其替换为:

    #
    pap
    python

转换为\t#\n\tpap\n\tpython

在很多方面使用sed尝试了这一点,但它不起作用可能是因为sed以不同的方式使用新行。我尝试过:

sed -i "s/\t#\n\tpap/\t#\tpython\n\tpap/" /etc/freeradius/sites-available/default

......以及许多不同的其他方式,没有结果。我知道在这种情况下如何更换?

4 个答案:

答案 0 :(得分:6)

这可能适合你(GNU sed):

sed '/^\t#$/{n;/^\tpap$/{p;s//\tpython/}}' file

如果某一行仅包含\t#,则如果下一行仅包含\tpap,则将其替换为\tpython并将其打印出来。

答案 1 :(得分:4)

用gawk尝试这一行:

awk -v RS="\0" -v ORS="" '{gsub(/\t#\n\tpap/,"yourNEwString")}7' file

如果你想让sed处理新行,你必须先阅读整个文件:

sed ':a;N;$!ba;s/\t#\n\tpap/NewString/g' file

答案 2 :(得分:3)

GNU sed 解决方案不需要立即读取整个文件

sed '/^\t#$/ {n;/^\tpap$/a\\tpython'$'\n''}' file
  • /^\t#$/匹配仅注释行(完全匹配\t#),在这种情况下(仅)执行整个{...}表达式:
    • n加载并打印 next 行。
    • /^\tpap/\tpap的下一行完全匹配。
    • 如果匹配,a\\tpython将在跟随行之前输出\n\tpython - 请注意拼接的换行符($'\n')需要发出传递给a命令的文本的结尾(您可以使用多个-e选项)。

(顺便说一下:使用 BSD sed(OS X),它会变得很麻烦,因为

  • 控制字符。例如\n\t不受直接支持,必须以ANSI C引用的文字拼接。
  • 前导空格总是从a命令的text参数中删除,因此必须使用替换方法:s//&\'$'\n\t'python'/用自己替换pap加上要追加的行:

    sed '/^'$'\t''#$/ {n; /^'$'\t''pap$/ s//&\'$'\n\t'python'/;}' file
    


awk解决方案(符合POSIX标准),不需要一次阅读整个文件

awk '{print} /^\t#$/ {f=1;next} f && /^\tpap$/ {print "\tpython"} {f=0}' file
  • {print}:打印每个输入行
  • /^\t#$/ {f=1;next}:如果找到仅有注释的行(完全匹配f),则将标记1(对于'找到')设置为\t#并转到下一行。
  • f && /^\tpap$/ {print "\tpython"}:如果一行前面有注释行并且与\tpap完全匹配,则输出额外的行\tpython
  • {f=0}:重置指示仅注释行的标志。

答案 3 :(得分:1)

一些纯粹的bash解决方案:

简洁但有些脆弱,使用参数扩展:

in=$'\t#\n\tpap\n' # input string

echo "${in/$'\t#\n\tpap\n'/$'\t#\n\tpap\n\tpython\n'}"
  • 参数扩展仅支持模式(通配符表达式)作为搜索字符串,这限制了匹配能力:
  • 此处假设pap后跟\n,而没有假设\t#之前的内容,可能会导致误报。
  • 如果可以假设\t#\n\tpap\n总是封闭echo "${in/$'\n\t#\n\tpap\n'/$'\n\t#\n\tpap\n\tpython\n'}"可以有效地运作;否则,见下文。

使用=~运算符进行正则表达式匹配时,功能强大但很冗长:

=~运算符在右侧支持扩展正则表达式,因此可以实现更灵活,更强大的匹配:

in=$'\t#\n\tpap' # input string 

# Search string and string to append after.
search=$'\t#\n\tpap'
append=$'\n\tpython'

out=$in # Initialize output string to input string.
if [[ $in =~ ^(.*$'\n')?("$search")($'\n'.*)?$ ]]; then # perform regex matching
    out=${out/$search/$search$append} # replace match with match + appendage
fi

echo "$out"
相关问题