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

问题描述 投票:0回答:5

我有一根像这样的绳子:

    #
    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

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

linux bash ubuntu awk sed
5个回答
12
投票

用 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

6
投票

这可能对你有用(GNU sed):

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

如果一行仅包含

\t#
打印它,那么如果下一行仅包含
\tpap
也打印它,然后用
\tpython
替换该行并打印它。


2
投票

一个 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
    命令的文本参数中删除,因此必须使用替换方法:
    s//&\'$'\n\t'python'/
    用自身替换
    pap
    plus 要附加的行:

    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}
    :重置指示仅注释行的标志。

1
投票

您只需翻译该字符即可 到另一个,然后应用

sed
,然后应用反向翻译。例如,如果使用
tr
,则它必须是 1 字节字符(垂直制表,现在几乎未使用)。

cat FILE|tr '\n' '\v'|sed 's/\t#\v\tpap/&\v\tpython/'|tr '\v' '\n'|sponge FILE

或者,没有海绵:

cat FILE|tr '\n' '\v'|sed 's/\t#\v\tpap/&\v\tpython/'|tr '\v' '\n' >FILE.bak && mv FILE.bak FILE

1
投票

几个纯粹的

bash
解决方案:

简洁,但有些脆弱,使用参数扩展:
in=$'\t#\n\tpap\n' # input string

echo "${in/$'\t#\n\tpap\n'/$'\t#\n\tpap\n\tpython\n'}"
  • 参数扩展仅支持patterns(通配符表达式)作为搜索字符串,这限制了匹配能力:
  • 此处假设
    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"
© www.soinside.com 2019 - 2024. All rights reserved.