我想要更新的大量C++源文件包括一个额外的指令》之前的任何现有的#包括。这种任务时,我通常使用一个小小的庆典脚本sed重新写的文件。

我怎么得到的 sed 来取代仅仅是第一个出现的串在一个文件,而不是取代每次发生?

如果我用

sed s/#include/#include "newfile.h"\n#include/

它取代了所有#包括。

替代建议,以实现同样的事情,也是受欢迎的。

有帮助吗?

解决方案

 # 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,/RE/s//to_that/' file 

来源

其他提示

编写一个sed脚本,它只会替换第一次出现的<!>“Apple <!>”;通过<!>“香蕉<!>”

示例输入:输出:

     Apple       Banana
     Orange      Orange
     Apple       Apple

这是一个简单的脚本:编者注:仅适用于 GNU sed

sed '0,/Apple/{s/Apple/Banana/}' filename
sed '0,/pattern/s/pattern/replacement/' filename

这对我有用。

例如

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

编者注:两者都只使用 GNU sed

一个 概述 许多有用的 现有的答案, ,辅以 解释:

该例子在这里使用的简化使用情况:词取代'foo'与'bar'在第一匹配线只。
由于使用 ANSI C-quoted strings($'...') 提供的样本输入线, bash, ksh, 或 zsh 是假定为外壳。


GNU sed 只有:

本Hoffstein的anwswer 向我们显示,GNU提供了一个 扩展 来的 POSIX规范 sed 允许以下2-地址的形式: 0,/re/ (re 表示一种任意的经常表达这里)。

0,/re/ 允许regex 匹配 在第一个也行.换句话说:这样一个地址将创建一个范围从1行,包括线路相匹配 re -是否 re 发生在1线或在任何随后的路线。

  • 与此形成对比的POSIX符合标准的形式 1,/re/, ,它创建了一个范围相匹配,从第1行,包括线路相匹配 re随后 线;换句话说:此 会检测不到先生 re 匹配如果发生这种情况发生的 1 线 并且还 防止使用的缩写 // 对于重复使用的最近使用regex(见下一点)。[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(mac os) sed (还将与 GNU sed):

由于 0,/re/ 不能使用和形式 1,/re/ 会检测不到 re 如果发生这种情况发生在第一线(见上文), 特别处理第1行是必需的.

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 话;在这两种情况下,regex foo 是隐含地重复使用,使我们不得不重复,这使得既可用于较短的和更易于维护的代码。

  • POSIX sed 需求的实际内容的行后的某些功能,例如后名称的标签,甚至省略,因为这种情况 t 在这里;战略性地分成多个脚本 -e 选择是可以替代使用的一个实际内容的行:每个结束 -e 脚本块在一个新行通常需要去。

1 s/foo/bar/ 替换 foo 在第1行,如果发现存在。如果是这样, t 分支机构,以结束脚本(跳过其余的命令在线)。(的 t 功能分支的标签只有如果最近的 s 呼吁执行一个实际的替代;在没有一个标签,因为是这里的情况,结束脚本是支)。

当发生这种情况,范围内的地址 1,//, 通常认为第一次出现 从2号线, 将 匹配,和范围 被处理,因为该地址是评价当前线已经 2.

相反,如果没有匹配在第1行, 1,// 可以进入,并将找到真正的第一场比赛。

净效果是相同的作用GNU sed's 0,/re/:只有第一个发生替换为,无论它发生在1线路或任何其他。


非范围的办法

波东的答案 演示了 循环 技术绕过需要一个范围;因为他使用 GNU sed 语法,这里的 POSIX符合当量:

循环技术的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 相匹配的第1行,并regex /foo/ -结束的范围,然后只找起 线。因此, 线的选择在这种情况下,和 s/foo/bar/ 取代执行它们两个。
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' 失败:与 sed: first RE may not be empty (BSD/mac os)和 sed: -e expression #1, char 0: no previous regular expression (GNU),因为,在1号线正在处理的(由于线路的数量 1 开始的范围内),没有regex已经应用,所以 // 不是指什么。
除GNU sed's特别 0,/re/ 语法, 任何 范围内,开始 线数量 有效地排除使用 //.

您可以使用awk做类似的事情......

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

说明:

/#include/ && !done

当行匹配<!>时,在{}之间运行操作语句; #include <!> quot;我们还没有处理过它。

{print "#include \"newfile.h\""; done=1;}

这打印#include <!> quot; newfile.h <!> quot;,我们需要转义引号。然后我们将done变量设置为1,因此我们不添加更多包含。

1;

这意味着<!>“打印出<!>行; - 空操作默认打印$ 0,打印出整行。一个班轮,比sed IMO更容易理解: - )

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
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

此脚本的工作原理:对于1和第一个#include之间的行(第1行之后),如果行以sed开头,则在前面添加指定的行。

但是,如果第一个0,/^#include/在第1行,那么第1行和下一个后续1,都将前面加上该行。如果您使用的是GNU <=>,则它有一个扩展名,其中<=>(而不是<=>)将做正确的事。

一个可能的解决方案:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :
    n
    b

说明:

  • 读线,直到我们找到#包括打印这些行,然后开始新的周期
  • 插入新的包括线
  • 进入一个循环,只是读取行(通过默认sed也将打印这些行),我们不会回到第一部分脚本来自这里

我知道这是一篇旧帖子,但我有一个以前常用的解决方案:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

基本上使用grep找到第一次出现并停在那里。还打印行号,即5行。管道进入sed并删除:以及之后的所有内容,只需要留下行号。管道进入sed,它将s /.*/替换为末尾,它给出一个1行脚本,该脚本通过管道传输到最后一个sed作为文件脚本运行。

所以如果regex = #include和replace = blah并且grep第一次出现在第5行,那么通过管道传输到最后一个sed的数据将是5s /.*/ blah /.

如果有人来这里替换所有行中第一次出现的字符(比如我自己),请使用:

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,您可以仅替换所有第二个。

我会用awk脚本执行此操作:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}    
END {}

然后用awk运行它:

awk -f awkscript headerfile.h > headerfilenew.h

可能很草率,我是新手。

作为替代建议,您可能需要查看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

我终于在一个Bash脚本中使用它,用于在RSS提要的每个项目中插入一个唯一的时间戳:

        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并避免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

这可能适合你(GNU sed):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

或者内存不是问题:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...

与GNU sed的 -z 选择你可能处理整个文件作为如果这只是一线。这样一个 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|$)//.

以下命令删除文件中第一次出现的字符串。它也删除了空行。它出现在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 -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.
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top