0,/re/ 允许正则表达式 match on the very first line also . 换句话说:这样的地址将创建从第1行到包括与 re 匹配的行的范围 - 无论 re 出现在第1行还是后续行 .
将此与POSIX兼容表单 1,/re/ 进行对比,该表单创建一个范围,该范围从第1行开始,包括与后续行匹配 re 的行;换句话说:这个 will not detect the first occurrence of an re match if it happens to occur on the 1st line 以及 prevents the use of shorthand // 用于重用最近使用的正则表达式(见下一点) . [1]
如果将 0,/re/ 地址与使用相同正则表达式的 s/.../.../ (替换)调用组合在一起,则命令将仅在与 re 匹配的第一行上执行替换 . sed 提供了方便 shortcut for reusing the most recently applied regular expression : empty delimiter pair, // .
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
A POSIX-features-only sed such as BSD (macOS) sed (也适用于GNU sed ):
由于 0,/re/ 无法使用,并且 1,/re/ 形式如果碰巧发生在第一行(见上文), 1,/re/ 将无法检测到 special handling for the 1st line is required .
sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'
解释:
sed '
/\(#include\).*/!b # Only one regex used. On lines not matching
# the text `#include` **yet**,
# branch to end, cause the default print. Re-start.
//{ # On first line matching previous regex.
h # hold the line.
s//\1 "newfile.h"/ # append ` "newfile.h"` to the `#include` matched.
G # append a newline.
} # end of replacement.
:1 # Once **one** replacement got done (the first match)
n # Loop continually reading a line each time
b1 # and printing it by default.
' # end of sed script.
19 回答
作为替代建议,您可能需要查看
ed
命令 .可能的解决方案:
说明:
读取行直到我们找到#include,打印这些行然后开始新的循环
插入新的包含行
进入一个只读行的循环(默认sed也会打印这些行),我们不会从这里回到脚本的第一部分
我会用awk脚本执行此操作:
然后用awk运行它:
可能很草率,我是新手 .
或者,如果您愿意:编者注:仅适用于GNU sed .
Source
写一个sed脚本,只会用“Banana”替换第一次出现的“Apple”
示例输入:输出:
这是一个简单的脚本:编者注:仅适用于GNU sed .
这对我有用 .
例
编者注:两者都只适用于GNU sed .
overview 中有很多有用 existing answers ,辅以 explanations :
这里的示例使用简化的用例:仅在第一个匹配行中将'foo'替换为'bar' . 由于使用ANSI C引用的字符串($'...')来提供样本输入行,因此将bash,ksh或zsh假定为shell .
GNU sed only:
Ben Hoffstein's anwswer向我们展示了GNU为POSIX specification for sed提供了一个扩展,它允许以下2地址形式:
0,/re/
(re
表示这里的任意正则表达式) .0,/re/ 允许正则表达式 match on the very first line also . 换句话说:这样的地址将创建从第1行到包括与
re
匹配的行的范围 - 无论re
出现在第1行还是后续行 .re
的行;换句话说:这个 will not detect the first occurrence of an re match if it happens to occur on the 1st line 以及 prevents the use of shorthand // 用于重用最近使用的正则表达式(见下一点) . [1]如果将
0,/re/
地址与使用相同正则表达式的s/.../.../
(替换)调用组合在一起,则命令将仅在与re
匹配的第一行上执行替换 .sed
提供了方便 shortcut for reusing the most recently applied regular expression : empty delimiter pair, // .A POSIX-features-only sed such as BSD (macOS) sed (也适用于GNU
sed
):由于
0,/re/
无法使用,并且1,/re/
形式如果碰巧发生在第一行(见上文),1,/re/
将无法检测到 special handling for the 1st line is required .MikhailVS's answer提到了这项技术,在这里举了一个具体的例子:
注意:
此处使用空的正则表达式
//
快捷方式两次:一次用于范围的 endpoints ,一次用于s
调用;在这两种情况下,regexfoo
都被隐式重用,允许我们不必复制它,这使得更短和更易维护的代码 .POSIX
sed
在某些函数之后需要实际换行符,例如在标签名称之后甚至是其遗漏之后,如t
这样;策略性地将脚本拆分为多个-e
选项是使用实际换行符的替代方法:结束每个-e
脚本块,其中通常需要换行 .1 s/foo/bar/
仅在第1行替换foo
,如果在那里找到的话 . 如果是这样,t
分支到脚本的末尾(跳过该行上的剩余命令) . (仅当最近的s
调用执行实际替换时,t
函数才会分支到标签;如果没有标签,则此处的情况就是脚本的末尾分支到) .发生这种情况时,范围地址
1,//
(通常从第2行开始查找第一次出现)将不匹配,并且不会处理范围,因为当当前行已经是2
时会计算地址 .相反,如果第一行没有匹配项,将输入
1,//
,并找到真正的第一场比赛 .净效果与GNU
sed
的0,/re/
相同:只有第一次出现被替换,无论是在第一行还是其他任何一行 .NON-range approaches
potong's answer演示 loop techniques 那 bypass the need for a range ;因为他使用GNU
sed
语法,这里是 POSIX-compliant equivalents :循环技术1:在第一次匹配时,执行替换,然后 enter a loop that simply prints the remaining lines as-is :
循环技术2,适用于 smallish files only : read the entire input into memory, then perform a single substitution on it .
[1] 1.61803提供了1,/ re /,有和没有后续s //: - sed'1,/ foo / s / foo / bar /'<<< $ 1foo \ n2foo'产生的例子$ '1BAR \ n2bar';即两条线都被更新,因为第1行与第1行匹配,而regex / foo / - 范围的结束 - 仅在下一行开始查找 . 因此,在这种情况下选择两行,并且对它们两者执行s / foo / bar /替换 . - sed'1,/ foo / s // bar /'<<< $ 1foo \ n2foo \ n3foo'失败:使用sed:first RE may不是空的(BSD / macOS)和sed:-e表达式#1,char 0:没有前一个正则表达式(GNU),因为,在处理第一行时(由于行号1开始该范围),还没有应用正则表达式,所以//不引用任何东西 . 除了GNU sed的特殊0,/ re /语法之外,任何以行号开头的范围都有效地排除了//的使用 .
你可以使用awk做类似的事情..
说明:
当行匹配“#include”并且我们尚未处理它时,在{}之间运行操作语句 .
这打印#include“newfile.h”,我们需要转义引号 . 然后我们将done变量设置为1,因此我们不添加更多包含 .
这意味着“打印出行” - 空行动默认打印$ 0,打印出整行 . 一个班轮,比sed IMO更容易理解:-)
关于linuxtopia sed FAQ的全面答案 . 它还强调了人们提供的一些答案不适用于非GNU版本的sed,例如
在非GNU版本中必须是
但是,此版本不适用于gnu sed .
这是一个适用于以下两个版本的版本:
例如:
只需在最后添加出现次数:
此脚本的工作原理:对于1和第一个
#include
之间的行(在第1行之后),如果该行以#include
开头,则在前面添加指定的行 .但是,如果第一个
#include
在第1行,那么第1行和下一个后续的#include
都将前面加上该行 . 如果您正在使用GNUsed
,它有一个扩展名,其中0,/^#include/
(而不是1,
)将做正确的事情 .我终于让它在一个Bash脚本中工作,用于在RSS提要的每个项目中插入一个唯一的时间戳:
它仅更改第一次出现 .
${nowms}
是Perl脚本设置的时间(以毫秒为单位),$counter
是用于脚本中循环控制的计数器,\
允许命令在下一行继续 .读入文件并将stdout重定向到工作文件 .
我理解它的方式,
1,/====RSSpermalink====/
通过设置范围限制告诉sed何时停止,然后s/====RSSpermalink====/${nowms}/
是用第二个替换第一个字符串的熟悉的sed命令 .在我的情况下,我把命令放在双引号中因为我在带有变量的Bash脚本中使用它 .
如果要处理的文件中没有
include
语句,请使用 FreeBSDed
并避免ed
的"no match"错误:这可能适合你(GNU sed):
或者如果内存不是问题:
我知道这是一个旧帖子,但我有一个我以前使用的解决方案:
基本上使用grep找到第一次出现并停在那里 . 还打印行号,即5行 . 管道进入sed并删除:以及之后的所有内容,只需要留下行号 . 管道进入sed,它将s /.*/替换为末尾,它给出一个1行脚本,该脚本通过管道传输到最后一个sed作为文件脚本运行 .
因此,如果regex = #include和replace = blah并且grep第一次出现在第5行,那么通过管道传输到最后一个sed的数据将是5s /.*/ blah / .
如果有人来这里替换所有行中第一次出现的字符(比如我自己),请使用:
例如,通过将1更改为2,您可以仅替换所有第二个a .
以下命令删除文件中第一次出现的字符串 . 它也删除了空行 . 它出现在xml文件中,但它适用于任何文件 .
如果您使用xml文件并且想要删除标记,则非常有用 . 在此示例中,它删除了第一次出现的“isTag”标记 .
命令:
源文件(source.txt)
结果文件(output.txt)
ps:它在Solaris SunOS 5.10(相当陈旧)上对我不起作用,但它适用于Linux 2.6,sed版本4.1.5
没有什么新的,但也许更具体的答案:
sed -rn '0,/foo(bar).*/ s%%\1%p'
示例:
xwininfo -name unity-launcher
生成如下输出:使用
xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
提取窗口ID会产生:POSIXly(在sed中也有效),只使用 one 正则表达式,只需要一行内存(像往常一样):
解释: