Answers:
# sed script to change "foo" to "bar" only on the first occurrence
1{x;s/^/first/;x;}
1,/foo/{x;/first/s///;x;s/foo/bar/;}
#---end of script---
或者,如果您愿意:编者注:仅适用于GNU sed
。
sed '0,/foo/s//bar/' file
0,
仅适用于gnu sed
s//
-即空的正则表达式-意味着隐式重用了最近应用的正则表达式;在这种情况下,RE
。此便捷的快捷方式意味着您不必在s
通话中重复范围结尾的正则表达式。
一个sed
脚本,将只能通过“香蕉”替换“苹果”的第一次出现
例
Input: Output:
Apple Banana
Apple Apple
Orange Orange
Apple Apple
这是简单的脚本:编者注:仅适用于GNU sed
。
sed '0,/Apple/{s/Apple/Banana/}' input_filename
前两个参数0
和/Apple/
是范围说明符。该s/Apple/Banana/
是什么是该范围内执行。因此,在这种情况下,“在开始(0
)的范围内,直到的第一个实例Apple
,替换Apple
为Banana
。只有第一个Apple
会被替换。
背景:传统上sed
,范围说明符也是 “从此处开始”和“从此处结束”(含)。但是,最低的“ begin”是第一行(第1行),如果“ end here”是一个正则表达式,则仅尝试在“ begin”之后的下一行进行匹配,因此最早的结尾是line 2.因此,由于范围是包括在内的,因此最小的可能范围是“ 2行”,最小的起始范围是第1行和第2行(即,如果第1行出现了情况,那么第2行出现的情况也将被更改,在这种情况下不希望如此) )。GNU
sed添加了自己的扩展名,允许将start指定为“ pseudo”,line 0
以便范围的结尾可以为line 1
,从而允许其范围为“仅第一行”
或简化版本(像这样的空RE //
意味着可以重复使用在其之前指定的RE ,因此等效):
sed '0,/Apple/{s//Banana/}' input_filename
花括号对于命令来说是可选的s
,因此这也是等效的:
sed '0,/Apple/s//Banana/' input_filename
所有这些sed
仅在GNU上起作用。
您也可以使用homebrew在OS X上安装GNU sed brew install gnu-sed
。
sed: 1: "…": bad flag in substitute command: '}'
sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'
。从@MikhailVS的答案(当前)到下面。
sed '0,/foo/s/foo/bar/'
sed: -e expression #1, char 3: unexpected
,'
sed '0,/pattern/s/pattern/replacement/' filename
这对我有用。
例
sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt
编者注:两者都仅适用于GNU sed
。
sed '1,/pattern/s/pattern/replacement/' filename
仅在Mac上仅当“模式不会出现在第一行”时才有效。由于不正确,我将删除之前的评论。可以在这里找到详细信息(linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/…)。Andy的答案仅适用于GNU sed,而不适用于Mac。
现有许多有用答案的概述,并附有解释:
此处的示例使用了一个简化的用例:仅在第一条匹配行中将单词'foo'替换为'bar'。
由于使用的ANSI C引号字符串($'...'
),以提供所述样品输入线,bash
,ksh
,或zsh
假定为壳。
sed
仅GNU:
本Hoffstein的anwswer告诉我们,GNU提供了一个扩展的POSIX规范sed
,允许下列2地址形式:0,/re/
(re
代表一个任意的正则表达式在这里)。
0,/re/
也允许正则表达式在第一行进行匹配。换句话说:这样的地址将创建从第一线到一个范围,并包括相匹配的线re
-是否re
在第一行上或任何后续的线路中发生。
1,/re/
,它创建了一个范围,从第1行匹配直到并包括线匹配re
于随后的线; 换句话说:如果碰巧发生在第一行,它将不会检测到第一次匹配,re
并且还会阻止使用速记//
来重复使用最近使用的正则表达式(请参阅下一点)。1个如果将0,/re/
地址与s/.../.../
使用相同正则表达式的(替代)调用组合在一起,则命令将仅在与匹配的第一行有效地执行替换re
。
sed
提供了一个方便的快捷方式来重用最近应用的正则表达式:空的定界符对,//
。
$ 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
仅POSIX功能,sed
例如BSD(macOS)sed
(也可以与GNU一起使用 sed
):
由于0,/re/
无法使用,并且表单1,/re/
将无法检测re
它是否恰好发生在第一行(请参见上文),因此需要对第一行进行特殊处理。
MikhailVS的答案提到了该技术,这里将其作为一个具体示例:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
注意:
空的regex //
快捷方式在这里使用了两次:一次用于范围的端点,一次在s
调用中;在这两种情况下,正则表达式foo
都是隐式重用的,这使我们不必重复它,这使得代码更短,更易于维护。
POSIX sed
在某些功能之后需要实际的换行符,例如在标签名之后,甚至在其省略之后,例如t
此处;从策略上将脚本分成多个-e
选项是使用实际换行符的一种替代方法:在每个-e
脚本块的正常换行处结束。
1 s/foo/bar/
foo
如果在第一行替换,则仅在第一行替换。如果是这样,则t
分支到脚本的末尾(跳过该行上的其余命令)。(t
仅当最近的s
调用执行了实际替换时,该函数才会分支到标签;在没有标签的情况下(如此处所示),脚本的末尾会分支到)。
发生这种情况时,1,//
通常会从第2行开始找到第一个匹配项的范围地址将不匹配,并且不会处理该范围,因为该地址是在当前行已经存在时才进行评估的2
。
相反,如果第一行没有匹配项,1,//
则将输入该值,并找到真正的第一个匹配项。
净效果是一样的与GNU sed
的0,/re/
:只有第一发生替换,不管它发生在第一线或任何其他。
非范围方法
potong的答案演示了绕过一定范围需求的循环技术;由于他使用GNU语法,因此这里是POSIX兼容的等效项: sed
循环技术1:在第一次匹配时,执行替换,然后进入一个循环,该循环仅按原样打印其余行:
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
循环技术2,仅适用于较小的文件:将整个输入读取到内存中,然后对它执行一次替换。
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803提供了1,/re/
有或没有后续情况的例子s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
收益$'1bar\n2bar'
; 也就是说,两行都已更新,因为行号1
与第一行匹配,/foo/
然后仅寻找正则表达式(范围的末尾)从下一行开始。因此,在这种情况下都选择了两条线,并且s/foo/bar/
对它们都进行了替换。
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
失败:使用sed: first RE may not be empty
(BSD / macOS)和sed: -e expression #1, char 0: no previous regular expression
(GNU),因为在处理第一行时(由于行号1
从该范围开始),尚未应用任何正则表达式,因此//
什么都没提到
除GNU sed
的特殊0,/re/
语法外,任何以行号开头的范围都将有效地禁止使用//
。
您可以使用awk做类似的事情。
awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c
说明:
/#include/ && !done
当该行与“ #include”匹配并且尚未处理时,在{}之间运行该动作语句。
{print "#include \"newfile.h\""; done=1;}
这会打印#include“ newfile.h”,我们需要转义引号。然后,将完成变量设置为1,因此我们不添加更多包含。
1;
这意味着“打印出该行”-一个空操作默认会打印$ 0,这会打印出整行。一个衬里,比sed IMO更容易理解:-)
awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
关于linuxtopia sed FAQ的相当多的答案。它还强调了人们提供的一些答案不适用于非GNU版本的sed,例如
sed '0,/RE/s//to_that/' file
在非GNU版本中必须
sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'
但是,此版本不适用于gnu sed。
这是可以同时使用的版本:
-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'
例如:
sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
只需在末尾添加出现次数:
sed s/#include/#include "newfile.h"\n#include/1
sed
指定替换命令,[2addr]s/BRE/replacement/flags
并带有:并指出“标志的值应为零或更大的值: n 仅替换在模式空间中找到的BRE的第n次出现。” 因此,至少在POSIX 2008中,结尾1
不是GNU sed
扩展。的确,即使在SUS / POSIX 1997标准中,该功能也得到了支持,因此我在2008
可能的解决方案:
/#include/!{p;d;}
i\
#include "newfile.h"
:a
n
ba
说明:
sed: file me4.sed line 4: ":" lacks a label
我知道这是一篇旧文章,但是我曾经使用过一个解决方案:
grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file
基本上使用grep打印第一个匹配项并在此停止。另外打印行号,即5:line
。用管道将其插入sed并删除:和之后的所有内容,因此只剩下一个行号。通过管道将其添加到sed中,在末尾添加s /.*/ replace,从而生成1行脚本,该脚本将通过管道传输到最后一个sed中,以作为文件上的脚本运行。
因此,如果regex = #include
和replace = blah
并且grep发现的第一个匹配项位于第5行,则通过管道传输到最后一个sed的数据将为5s/.*/blah/
。
即使第一次出现在第一行上也有效。
sed -f -
其中的一些,但您可以解决它:)
如果有人来这里替换所有行中第一个出现的字符(例如我自己),请使用以下命令:
sed '/old/s/old/new/1' file
-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12
例如,通过将1更改为2,可以替换所有第二个a。
's/a/b/'
手段match a
,和do just first match
for every matching line
使用GNU sed的-z
选项,您可以处理整个文件,就好像它只是一行一样。这样,a s/…/…/
只会替换整个文件中的第一个匹配项。请记住:s/…/…/
仅替换每行中的第一个匹配项,但使用该-z
选项sed
会将整个文件视为一行。
sed -z 's/#include/#include "newfile.h"\n#include'
在一般情况下,您必须重写sed表达式,因为模式空间现在可以容纳整个文件,而不仅仅是一行。一些例子:
s/text.*//
可以重写为s/text[^\n]*//
。[^\n]
匹配除换行符以外的所有内容。[^\n]*
在text
到达换行符之后将匹配所有符号。s/^text//
可以重写为s/(^|\n)text//
。s/text$//
可以重写为s/text(\n|$)//
。作为替代建议,您可能需要查看ed
命令。
man 1 ed
teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'
# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
H
/# *include/i
#include "newfile.h"
.
,p
q
EOF
我终于在一个用于在RSS feed中的每个项目中插入唯一时间戳的Bash脚本中使它工作:
sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter
它仅更改第一次出现。
${nowms}
是Perl脚本设置的时间(以毫秒为单位),$counter
是用于脚本内循环控制的计数器,\
允许命令在下一行继续。
读入文件,并将stdout重定向到工作文件。
我的理解方式是,1,/====RSSpermalink====/
通过设置范围限制来告诉sed何时停止,然后s/====RSSpermalink====/${nowms}/
是熟悉的sed命令将第二个字符串替换为第一个字符串。
就我而言,我将命令放在双引号中,因为我在带有变量的Bash脚本中使用了该命令。
如果要处理的文件中没有语句,请使用FreeBSD ed
并避免出现ed
“ no match”错误include
:
teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'
# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
H
,g/# *include/u\
u\
i\
#include "newfile.h"\
.
,p
q
EOF
以下命令删除文件中字符串的首次出现。它也删除了空行。它显示在xml文件中,但可以与任何文件一起使用。
如果您使用xml文件并且要删除标签,则很有用。在此示例中,它删除了“ isTag”标签的第一次出现。
命令:
sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//} -e 's/ *$//' -e '/^$/d' source.txt > output.txt
源文件(source.txt)
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<isTag>false</isTag>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
结果文件(output.txt)
<xml>
<testdata>
<canUseUpdate>true</canUseUpdate>
<moduleLocations>
<module>esa_jee6</module>
<isTag>false</isTag>
</moduleLocations>
<node>
<isTag>false</isTag>
</node>
</testdata>
</xml>
ps:它对我在Solaris SunOS 5.10(旧版本)上不起作用,但是在Linux 2.6(sed版本4.1.5)上起作用
sed
(因此不适用于Solaris)。您应该删除它-确实,它对于回答已经有4½年历史的问题并没有提供独特的新信息。当然,它确实有一个可行的示例,但是当问题的答案与此答案相同时,这具有可争议的价值。
没什么新鲜的,但也许更具体的答案: sed -rn '0,/foo(bar).*/ s%%\1%p'
示例:xwininfo -name unity-launcher
产生如下输出:
xwininfo: Window id: 0x2200003 "unity-launcher"
Absolute upper-left X: -2980
Absolute upper-left Y: -198
Relative upper-left X: 0
Relative upper-left Y: 0
Width: 2880
Height: 98
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x20 (installed)
Bit Gravity State: ForgetGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable
Override Redirect State: no
Corners: +-2980+-198 -2980+-198 -2980-1900 +-2980-1900
-geometry 2880x98+-2980+-198
提取带有xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
产生的窗口ID :
0x2200003
POSIXly(在sed中也有效),仅使用一个正则表达式,仅需要为一行存储(通常):
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.
此处可能的解决方案可能是告诉编译器包括标头,而不在源文件中提及标头。在GCC中,有以下选项:
-include file
Process file as if "#include "file"" appeared as the first line of
the primary source file. However, the first directory searched for
file is the preprocessor's working directory instead of the
directory containing the main source file. If not found there, it
is searched for in the remainder of the "#include "..."" search
chain as normal.
If multiple -include options are given, the files are included in
the order they appear on the command line.
-imacros file
Exactly like -include, except that any output produced by scanning
file is thrown away. Macros it defines remain defined. This
allows you to acquire all the macros from a header without also
processing its declarations.
All files specified by -imacros are processed before all files
specified by -include.
Microsoft的编译器具有/ FI(强制包含)选项。
对于某些常见的标头(例如平台配置),此功能可能很方便。Linux内核的Makefile -include
为此使用。