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

pprl5pva  于 2022-10-23  发布在  Linux
关注(0)|答案(5)|浏览(355)

我有一根这样的线:


# 

    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

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

o7jaxewo

o7jaxewo1#

试着用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
zbwhf8kr

zbwhf8kr2#

这可能适用于您(GNU Sed):

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

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

fbcarpbf

fbcarpbf3#

**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行替换为其自身加上要附加的行:
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}:如果找到仅用于注解的行(与\t#完全匹配)并移至下一行,则将标志f(表示“找到”)设置为1
  • f && /^\tpap$/ {print "\tpython"}:如果一行前面有注解行,并且与\tpap完全匹配,则输出额外的\tpython行。
  • {f=0}:重置指示仅注解行的标志。
5f0d552i

5f0d552i4#

几个纯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"
von4xj4u

von4xj4u5#

您只需将字符\n转换为另一个字符,然后应用sed,然后应用反向转换。如果使用tr,它必须是一个1字节的字符,例如\v(垂直制表,现在几乎不使用)。

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

相关问题