Linux 使用sed匹配包含换行符的字符串
我有一根像这样的线:Linux 使用sed匹配包含换行符的字符串,linux,bash,ubuntu,awk,sed,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 …还有许多其他方法都没有结果。你知道
#
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
…还有许多其他方法都没有结果。你知道在这种情况下我该如何替换吗?用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
GNU
sed
解决方案,不需要一次读取整个文件:
只匹配注释行(精确匹配/^\t#$/
),在这种情况下(仅)执行整个\t#
表达式:{…}
加载并打印下一行n
将下一行与/^\tpap/
精确匹配\tpap
- 如果匹配,
将在读取下一行之前输出a\\tpython
-请注意,拼接的换行符(\n\tpython
)需要表示传递给$'\n'
命令的文本结束(您也可以使用多个a
选项)-e
sed
(OSX),它会变得很麻烦,因为
- 控件字符。例如
和\n
不受直接支持,必须作为ANSI C引号的文本拼接\t
- 在
命令中,前导空格总是从文本参数中去掉,因此必须使用替换方法:a
将s/&\'$'\n\t'python'/
行替换为自身加上要追加的行:pap
sed '/^'$'\t''#$/ {n; /^'$'\t''pap$/ s//&\'$'\n\t'python'/;}' file
一种不需要一次读取整个文件的
awk
解决方案(兼容POSIX):
:打印每个输入行{print}
:如果只找到注释行(精确匹配/^\t#$/{f=1;next}
),则将标志\t#
(用于“找到”)设置为f
,并移到下一行1
:如果一行前面有注释行并且与f&&/^\tpap$/{print“\tpython”}
完全匹配,则输出额外的行\tpap
\tpython
:重置指示仅注释行的标志{f=0}
bash
解决方案:
简洁,但有些脆弱,使用参数展开:
- 参数扩展仅支持模式(通配符表达式)作为搜索字符串,这限制了匹配能力:
- 这里假设
后面跟着pap
,而没有假设\n
前面是什么,可能会导致误报\t#
- 如果可以假设
总是包含在\t\n\tpap
中,\n
将可靠地工作;否则,请参见下文回音“${in/$”\n\t\tpap\n'/$”\n\t\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"
这可能适用于您(GNU-sed):
如果一行只包含
\t#
打印它,那么如果下一行只包含\tpap
也打印它,那么将该行替换为\tpython
并打印它。这里是sed,没有先读取整个文件:sed-e':b/^\t#$/{N;s/\N\tpap$/&\N\tpython/;te;P;D;};:e'
+`用于awk解决方案。我忽略了sed一个,所以我可以+1 awk一个:-)。顺便说一句,我最近发现有人解析包含NUL
字符的文本文件,因此使用RS='\0'
对他们不起作用,所以我默认使用RS='^$'
切换到使用^$
之所以有效,是因为这两个字符匹配字符串的开头和结尾,而gawk将输入文件视为要拆分为记录的字符串,因此RS='^$'
仅对空字符串/文件有效,不能存在于包含任何内容的文件中。如果他们不能呆呆地看,那么接下来就是我提到的警告。RS='\0'
。在gsub
结束后,7
是做什么的?@LironYahdavprint
不幸的是,没有解释为什么这样效果更好。sed是一个很好的工具,可以在一行中进行简单的替换。它不适用于涉及跨多行匹配REs的任何问题。20世纪70年代中期,当awk被发明时,sed语言的结构已经过时代码>在你的前面,它会起作用。+1-聪明。起初对p
感到困惑,因为n
通常会打印新加载的行,直到我意识到您的s
命令通过在替换字符串中不引用该行来有效地删除该行;换句话说:p;s//\typthon/
相当于:s/&\n\tpython/
。
awk '{print} /^\t#$/ {f=1;next} f && /^\tpap$/ {print "\tpython"} {f=0}' file
in=$'\t#\n\tpap\n' # input string
echo "${in/$'\t#\n\tpap\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"
sed '/^\t#$/{n;/^\tpap$/{p;s//\tpython/}}' file