【问题标题】:How to use sed to replace only the first occurrence in a file?如何使用 sed 仅替换文件中的第一个匹配项?
【发布时间】:2010-09-13 23:45:08
【问题描述】:

我想在任何现有 #include 之前使用额外的 include 指令更新大量 C++ 源文件。对于这类任务,我通常使用带有 sed 的小型 bash 脚本来重写文件。

如何让sed 仅替换文件中第一次出现的字符串,而不是替换每次出现的字符串?

如果我使用

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

它替换了所有#include。

也欢迎其他实现相同目标的建议。

【问题讨论】:

    标签: command-line sed text-processing


    【解决方案1】:

    一个sed 脚本,它只会将第一次出现的“Apple”替换为“Banana”

    例子

         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 中,范围说明符是also“从这里开始”和“从这里结束”(包括)。然而,最低的“开始”是第一行(第 1 行),如果“这里的结束”是一个正则表达式,那么它只会尝试匹配“开始”之后的下一行,所以最早可能的结束是行2. 因此,由于范围包括在内,最小可能范围是“2 行”,最小起始范围是第 1 行和第 2 行(即,如果第 1 行出现,第 2 行的出现也将改变,在这种情况下不需要)。 GNU sed 添加了自己的扩展,允许将 start 指定为“伪”line 0 以便范围的结尾可以是 line 1,如果正则表达式匹配第一个,则允许它的范围为“仅第一行”行。

    或者简化版本(像//这样的空RE意味着重复使用之前指定的那个,所以这是等效的):

    sed '0,/Apple/{s//Banana/}' input_filename
    

    对于s 命令,花括号是optional,所以这也是等价的:

    sed '0,/Apple/s//Banana/' input_filename
    

    所有这些都只适用于 GNU sed

    您还可以使用自制软件brew install gnu-sed 在 OS X 上安装 GNU sed。

    【讨论】:

    • 翻译成人类语言:从第 0 行开始,继续直到匹配 'Apple',在大括号中执行替换。 cfr:grymoire.com/Unix/Sed.html#uh-29
    • 在 OS X 上,我得到 sed: 1: "…": bad flag in substitute command: '}'
    • @ELLIOTTCABLE 在 OS X 上,使用 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 ,'`
    【解决方案2】:
     # 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 
    

    Source

    【讨论】:

    • 我想我更喜欢“或者如果你喜欢”的解决方案。解释答案也是很好的——让答案直接解决问题,然后概括,而不是仅仅概括。但很好的答案。
    • 仅供 Mac 用户参考,您必须将 0 替换为 1,因此:sed '1,/RE/s//to_that/' 文件
    • 有人可以解释一下“或者如果你更喜欢”的解决方案吗?我不知道将“来自”模式放在哪里。
    • @Jean-LucNacifCoelho: using s// - 即一个 empty 正则表达式 - 意味着最近应用的正则表达式被隐式重用;在这种情况下,RE。这个方便的快捷方式意味着您不必在 s 调用中复制范围结束正则表达式。
    • 完整示例:echo "abc\nabc" | sed '0,/^abc/s//ab/'
    【解决方案3】:
    sed '0,/pattern/s/pattern/replacement/' filename
    

    这对我有用。

    例子

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

    编者注:两者都适用于 GNU sed

    【讨论】:

    • @Landys 这仍然会替换其他行中的实例;不只是第一个实例
    • @sarat 是的,你是对的。 sed '1,/pattern/s/pattern/replacement/' filename 仅在 Mac 上“第一行不会出现该模式”时才有效。我会删除我之前的评论,因为它不准确。详细信息可以在这里找到 (linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/…)。 Andy 的答案仅适用于 GNU sed,但不适用于 Mac。
    • 最简单的答案!恭喜。
    【解决方案4】:

    许多有用的现有答案概述,并辅以解释

    此处的示例使用了一个简化的用例:仅在第一个匹配行中将单词 'foo' 替换为 'bar'。
    由于使用ANSI C-quoted strings ($'...') 提供示例输入行,bashkshzsh 被假定为shell。


    GNU 仅限sed

    Ben Hoffstein's anwswer 向我们展示了 GNU 为POSIX specification for sed 提供了一个扩展,它允许以下2-address form0,/re/re 在这里代表任意正则表达式)。

    0,/re/ 允许正则表达式匹配第一行。换句话说:这样的地址将创建一个范围,从第一行到匹配re 的行(包括re)——无论re 出现在第一行还是任何后续行。

    • 将此与符合 POSIX 的表单 1,/re/ 进行对比,后者创建的范围从第一行到匹配 后续 上的 re 的行(包括) em> 行;换句话说:如果 re 匹配恰好出现在 1st 行,则此 不会检测到第一次出现的匹配,并且还 防止使用速记// 用于重用最近使用的正则表达式(见下一点)。1

    如果您将0,/re/ 地址与使用same 正则表达式的s/.../.../(替换)调用结合起来,您的命令将仅在first em> 匹配 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's answer提到了技术,这里举个具体的例子:

    $ 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
    

    注意:

    • 空的正则表达式// 快捷方式在这里使用了两次:一次用于范围的端点,一次用于s 调用;在这两种情况下,正则表达式 foo 都被隐式重用,让我们不必复制它,这使得代码更短且更易于维护。

    • POSIX sed 在某些函数之后需要实际的换行符,例如在标签的名称之后,甚至在它的省略之后,就像这里的t 一样;策略性地将脚本拆分为多个 -e 选项是使用实际换行符的替代方法:在通常需要换行符的地方结束每个 -e 脚本块。

    1 s/foo/bar/ 仅替换第一行的foo(如果在那里找到)。 如果是这样,t 会跳转到脚本的末尾(跳过该行的剩余命令)。 (仅当最近的 s 调用执行了实际替换时,t 函数才会分支到标签;在没有标签的情况下,就像这里的情况一样,脚本的结尾会分支到)。

    当这种情况发生时,范围地址1,//,通常会找到第一个匹配项从第 2 行开始,将匹配,并且范围将不匹配 被处理,因为当当前行已经是2 时,地址被评估。

    反之,如果第一行没有匹配,1,//会被输入,并且会找到真正的第一个匹配。

    最终效果与 GNU sed0,/re/ 相同:仅替换第一个匹配项,无论它出现在第一行还是其他任何位置。


    非范围进近

    potong's answer 演示 循环 技术绕过对范围的需求;因为他使用 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
    

    11.61803 提供了1,/re/ 发生的情况的示例,有和没有后续的s//

    • sed '1,/foo/ s/foo/bar/' &lt;&lt;&lt;$'1foo\n2foo' 产生 $'1bar\n2bar';即,两行都更新了,因为行号1匹配第一行,而正则表达式/foo/ - 范围的结尾 - 然后只在下一个 行。因此,在这种情况下,两行都被选中,并且在这两行上都执行了s/foo/bar/替换。
    • sed '1,/foo/ s//bar/' &lt;&lt;&lt;$'1foo\n2foo\n3foo' fails:使用 sed: first RE may not be empty (BSD/macOS) 和 sed: -e expression #1, char 0: no previous regular expression (GNU),因为当时正在处理第一行(由于行号 1 开始range),还没有应用正则表达式,所以// 没有引用任何东西。
      除了 GNU sed 的特殊 0,/re/ 语法之外,以 行号 开头的 any 范围实际上排除了 // 的使用。

    【讨论】:

      【解决方案5】:

      你可以使用 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",我们需要转义引号。然后我们将 done 变量设置为 1,所以我们不会添加更多的包含。

      1;
      

      这意味着“打印出一行”——一个空动作默认打印出 $0,它打印出整行。比 sed IMO 更容易理解的单行代码 :-)

      【讨论】:

      • 这个答案比依赖 gnu sed 等的 sed 解决方案更便携。(例如 OS-X 中的 sed 很烂!)
      • 这确实更容易理解,但对我来说它添加了一行而不是替换它;使用的命令:awk '/version/ &amp;&amp; !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
      • 这里相同,最容易理解的命令,但它在找到的字符串上方添加一行而不是替换它
      • 答案可读性很强。这是我替换字符串而不是添加新行的版本。 awk '/#include/ &amp;&amp; !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
      【解决方案6】:

      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
      

      【讨论】:

      • 确实,在 Ubuntu Linux v16 和 FreeBSD v10.2 上进行了测试。谢谢你。
      【解决方案7】:
      #!/bin/sed -f
      1,/^#include/ {
          /^#include/i\
      #include "newfile.h"
      }
      

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

      但是,如果第一个 #include 位于第 1 行,则第 1 行和下一个后续 #include 都会将该行放在前面。如果您使用的是 GNU sed,它有一个扩展名,0,/^#include/(而不是 1,)会做正确的事情。

      【讨论】:

        【解决方案8】:

        只要在最后加上出现次数即可:

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

        【讨论】:

        • 不幸的是,这不起作用。它替换文件每一行的第一次出现,而不是文件中的第一次出现。
        • 此外,它是 GNU sed 扩展,而不是标准 sed 功能。
        • 嗯...时间过去了。 POSIX 2008/2013 for sed 指定替换命令为:[2addr]s/BRE/replacement/flags 并指出“标志的值应为零或多个:n 替换仅在模式空间中发现的第 n 次出现 BRE。”因此,至少在 POSIX 2008 中,结尾的 1 不是 GNU sed 扩展。确实,即使在 SUS/POSIX 1997 标准中,也支持这一点,所以我在 2008 年就严重脱节了。
        【解决方案9】:

        使用 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|$)//

        【讨论】:

          【解决方案10】:

          一个可能的解决方案:

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

          解释:

          • 读取行直到我们找到#include,打印这些行然后开始新的循环
          • 插入新的包含行
          • 进入一个只读取行的循环(默认情况下 sed 也会打印这些行),我们不会从这里回到脚本的第一部分

          【讨论】:

          • sed: file me4.sed line 4: ":" lacks a label
          • 显然在最近的 sed 版本中发生了一些变化,并且不再允许使用空标签。更新了答案
          【解决方案11】:

          我知道这是一篇旧帖子,但我有一个我曾经使用过的解决方案:

          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 脚本或 sed 命令,除了 s 和行号之外的任何内容,所以我支持这种方法。这是我用于我的用例的内容(与 bash 一起使用): filepath=/etc/hosts ;帕特='^\(127\.0\.0\.1.*\)' ; repl='\1 newhostalias' ; sed $( IFS=: linearray=($(grep -E -m 1 -n "$patt" "$filepath")) && echo ${linearray[0]})s/"$patt"/"$repl" / "$文件路径"
          • 它有效。虽然只有足够聪明的 sed 可以接受 sed -f - ,但有些不是,但你可以解决它:)
          【解决方案12】:

          我会使用 awk 脚本来做到这一点:

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

          然后用 awk 运行它:

          awk -f awkscript headerfile.h > headerfilenew.h
          

          可能有点草率,我是新手。

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

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

                      sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
                          production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter
              

              它只改变第一次出现。

              ${nowms} 是由 Perl 脚本设置的时间(以毫秒为单位),$counter 是用于脚本内循环控制的计数器,\ 允许在下一行继续执行命令。

              文件被读入,标准输出被重定向到工作文件。

              按照我的理解,1,/====RSSpermalink====/ 通过设置范围限制告诉 sed 何时停止,然后 s/====RSSpermalink====/${nowms}/ 是熟悉的 sed 命令,用第二个字符串替换第一个字符串。

              在我的例子中,我将命令放在双引号中,因为我在带有变量的 Bash 脚本中使用它。

              【讨论】:

                【解决方案15】:

                使用 FreeBSD ed 并避免 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
                

                【讨论】:

                • 这与 timoanswer 非常相似,但在一年多之后添加。
                【解决方案16】:

                这可能对你有用(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...
                

                【讨论】:

                  【解决方案17】:

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

                  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 ado just first matchfor every matching line
                  • 我和 Samveen 在一起。 这也不能回答这里提出的问题。我建议删除这个答案。
                  • 问题是“文件中的第一次出现”而不是“一行中的第一次出现”
                  【解决方案18】:

                  用例可能是您的事件分布在整个文件中,但您知道您唯一关心的是前 10、20 或 100 行。

                  然后只需解决这些问题即可解决问题 - 即使 OP 的措辞仅考虑到第一。

                  sed '1,10s/#include/#include "newfile.h"\n#include/'
                  

                  【讨论】:

                    【解决方案19】:

                    以下命令删除文件中第一次出现的字符串。它也删除了空行。它显示在 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

                    【讨论】:

                    • 这看起来与许多先前答案的基本思想非常相似,但需要注意的是它仅适用于 GNU sed(因此它不适用于 Solaris)。你应该删除这个,拜托——它确实没有为你回答时已经有 4.5 年历史的问题提供独特的新信息。诚然,它确实有一个可行的例子,但是当问题的答案与这个问题一样多时,这具有值得商榷的价值。
                    【解决方案20】:

                    没有什么新鲜的,但也许更具体的答案: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
                    

                    【讨论】:

                      【解决方案21】:

                      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.
                      

                      【讨论】:

                        【解决方案22】:

                        这里一个可能的解决方案可能是告诉编译器包含标头而不在源文件中提及它。在 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.
                        

                        微软的编译器有/FI(强制包含)选项。

                        此功能对于一些常见的标头很方便,例如平台配置。 Linux 内核的 Makefile 为此使用了-include

                        【讨论】:

                          【解决方案23】:
                          sed -e 's/pattern/REPLACEMENT/1' <INPUTFILE
                          

                          【讨论】:

                            【解决方案24】:

                            我将提出一个不完全是原始问题所要求的建议,但对于那些还想专门替换可能第二次出现的匹配项或任何其他具体枚举的正则表达式匹配项的人。使用 python 脚本和 for 循环,如果需要,从 bash 脚本中调用它。这对我来说是这样的,我正在替换包含字符串 --project 的特定行:

                            def replace_models(file_path, pixel_model, obj_model):
                                # find your file --project matches
                                pattern = re.compile(r'--project.*')
                                new_file = ""
                                with open(file_path, 'r') as f:
                                    match = 1
                                    for line in f:
                                        # Remove line ending before we do replacement
                                        line = line.strip()
                                        # replace first --project line match with pixel
                                        if match == 1:
                                            result = re.sub(pattern, "--project='" + pixel_model + "'", line)
                                        # replace second --project line match with object
                                        elif match == 2:
                                            result = re.sub(pattern, "--project='" + obj_model + "'", line)
                                        else:
                                            result = line
                                        # Check that a substitution was actually made
                                        if result is not line:
                                            # Add a backslash to the replaced line
                                            result += " \\"
                                            print("\nReplaced ", line, " with ", result)
                                            # Increment number of matches found
                                            match += 1
                                        # Add the potentially modified line to our new file
                                        new_file = new_file + result + "\n"
                                    # close file / save output
                                    f.close()
                                fout = open(file_path, "w")
                                fout.write(new_file)
                                fout.close()
                            

                            【讨论】:

                              猜你喜欢
                              • 2018-12-18
                              • 2011-06-17
                              • 2019-08-27
                              • 2011-09-10
                              • 1970-01-01
                              • 1970-01-01
                              • 2020-12-09
                              相关资源
                              最近更新 更多