bash 使用 sed 匹配包含换行符的字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23850789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Match a string that contains a newline using sed
提问by Romeo Mihalcea
I have a string like this one:
我有一个这样的字符串:
#
pap
which basically translates to a \t#\n\tpap
and I want to replace it with:
这基本上转化为 a \t#\n\tpap
,我想用以下内容替换它:
#
pap
python
which translates to \t#\n\tpap\n\tpython
.
这转化为\t#\n\tpap\n\tpython
.
Tried this with sed
in a lot of ways but it's not working maybe because sed
uses new lines in a different way. I tried with:
尝试sed
了很多方法,但它不起作用可能是因为sed
以不同的方式使用新行。我试过:
sed -i "s/\t#\n\tpap/\t#\tpython\n\tpap/" /etc/freeradius/sites-available/default
...and many different other ways with no result. Any idea how can I do my replace in this situation?
...以及许多不同的其他方式,但没有结果。知道在这种情况下我该如何更换吗?
采纳答案by Kent
try this line with gawk:
用 gawk 试试这一行:
awk -v RS="sed ':a;N;$!ba;s/\t#\n\tpap/NewString/g' file
" -v ORS="" '{gsub(/\t#\n\tpap/,"yourNEwString")}7' file
if you want to let sed
handle new lines, you have to read the whole file first:
如果你想让sed
处理新行,你必须先阅读整个文件:
sed '/^\t#$/{n;/^\tpap$/{p;s//\tpython/}}' file
回答by potong
This might work for you (GNU sed):
这可能对你有用(GNU sed):
sed '/^\t#$/ {n;/^\tpap$/a\tpython'$'\n''}' file
If a line contains only \t#
print it, then if the next line contains only \tpap
print it too, then replace that line with \tpython
and print that.
如果一行只包含\t#
打印它,那么如果下一行也只包含\tpap
打印它,则将该行替换为\tpython
并打印。
回答by mklement0
A GNU sed
solution that doesn't require reading the entire file at once:
一个GNUsed
的解决方案,不需要读取整个文件一次:
sed '/^'$'\t''#$/ {n; /^'$'\t''pap$/ s//&\'$'\n\t'python'/;}' file
/^\t#$/
matches comment-only lines (matching\t#
exactly), in which case (only) the entire{...}
expression is executed:n
loads and prints the nextline./^\tpap/
matches that next line against\tpap
exactly.- in case of a match,
a\\tpython
will then output\n\tpython
before the followingline is read - note that the spliced-in newline ($'\n'
) is required to signal the end of the text passed to thea
command (you can alternatively use multiple-e
options).
/^\t#$/
匹配仅注释行(\t#
完全匹配),在这种情况下(仅)执行整个{...}
表达式:n
加载并打印下一行。/^\tpap/
与下一行\tpap
完全匹配。- 如果匹配,
a\\tpython
将\n\tpython
在读取下一行之前输出- 请注意,需要拼接换行符 ($'\n'
) 来表示传递给a
命令的文本的结尾(您也可以使用多个-e
选项)。
(As an aside: with BSD sed
(OS X), it gets cumbersome, because
(顺便说一句:对于BSD sed
(OS X),它变得很麻烦,因为
- Control chars. such as
\n
and\t
aren't directly supported and must be spliced in as ANSI C-quoted literals. Leading whitespace is invariably stripped from the text argument to the
a
command, so a substitution approach must be used:s//&\'$'\n\t'python'/
replaces thepap
line with itself plusthe line to append:sed '/^'$'\t''#$/ {n; /^'$'\t''pap$/ s//&\'$'\n\t'python'/;}' file
- 控制字符。例如
\n
和\t
不受直接支持,必须拼接为 ANSI C 引用的文字。 前导空格总是从
a
命令的文本参数中去除,因此必须使用s//&\'$'\n\t'python'/
替换方法:将pap
行替换为自身加上要附加的行:awk '{print} /^\t#$/ {f=1;next} f && /^\tpap$/ {print "\tpython"} {f=0}' file
)
)
An awk
solution(POSIX-compliant) that also doesn't require reading the entire file at once:
也不需要一次读取整个文件的awk
解决方案(符合 POSIX 标准):
in=$'\t#\n\tpap\n' # input string
echo "${in/$'\t#\n\tpap\n'/$'\t#\n\tpap\n\tpython\n'}"
{print}
: prints every input line/^\t#$/ {f=1;next}
: sets flagf
(for 'found') to1
if a comment-only line (matching\t#
exactly) is found and moves on to the next line.f && /^\tpap$/ {print "\tpython"}
: if a line is preceded by a comment line and matches\tpap
exactly, outputs extra line\tpython
.{f=0}
: resets the flag that indicates a comment-only line.
{print}
: 打印每个输入行/^\t#$/ {f=1;next}
:如果f
找到1
仅注释行(\t#
完全匹配)并移至下一行,则设置标志(用于 'found')。f && /^\tpap$/ {print "\tpython"}
: 如果一行前面是注释行并且\tpap
完全匹配,则输出额外的 line\tpython
。{f=0}
: 重置指示仅注释行的标志。
回答by mklement0
A couple of pure bash
solutions:
几个纯粹的bash
解决方案:
Concise, but somewhat fragile, using parameter expansion:
简洁,但有点脆弱,使用参数扩展:
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"
- Parameter expansion only supports patterns(wildcard expressions) as search strings, which limits the matching abilities:
- Here the assumption is made that
pap
is followed by\n
, whereas no assumption is made about what precedes\t#
, potentially resulting in false positives. - If the assumption could be made that
\t#\n\tpap
is always enclosedin\n
,echo "${in/$'\n\t#\n\tpap\n'/$'\n\t#\n\tpap\n\tpython\n'}"
would work robustly; otherwise, see below.
- 参数扩展仅支持模式(通配符表达式)作为搜索字符串,这限制了匹配能力:
- 这里的假设是在
pap
之后进行\n
,而没有对 之前的内容进行假设\t#
,这可能会导致误报。 - 如果假设可以作出
\t#\n\tpap
总是封闭在\n
,echo "${in/$'\n\t#\n\tpap\n'/$'\n\t#\n\tpap\n\tpython\n'}"
将有力的工作; 否则,请参见下文。
Robust, but verbose, using the =~
operator for regex matching:
健壮但冗长,使用=~
运算符进行正则表达式匹配:
The =~
operator supports extended regular expressionson the right-hand side and thus allows more flexible and robust matching:
所述=~
操作者支撑件延伸的正则表达式的右手侧,并因此允许更灵活和强大的匹配: