How to use sed to replace only the first occurrence in a file?

前端 未结 23 782
别跟我提以往
别跟我提以往 2020-11-22 04:27

I would like to update a large number of C++ source files with an extra include directive before any existing #includes. For this sort of task, I normally use a small bash s

相关标签:
23条回答
  • 2020-11-22 04:33

    A sed script that will only replace the first occurrence of "Apple" by "Banana"

    Example

         Input:      Output:
    
         Apple       Banana
         Apple       Apple
         Orange      Orange
         Apple       Apple
    

    This is the simple script: Editor's note: works with GNU sed only.

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

    The first two parameters 0 and /Apple/ are the range specifier. The s/Apple/Banana/ is what is executed within that range. So in this case "within the range of the beginning (0) up to the first instance of Apple, replace Apple with Banana. Only the first Apple will be replaced.

    Background: In traditional sed the range specifier is also "begin here" and "end here" (inclusive). However the lowest "begin" is the first line (line 1), and if the "end here" is a regex, then it is only attempted to match against on the next line after "begin", so the earliest possible end is line 2. So since range is inclusive, smallest possible range is "2 lines" and smallest starting range is both lines 1 and 2 (i.e. if there's an occurrence on line 1, occurrences on line 2 will also be changed, not desired in this case). GNU sed adds its own extension of allowing specifying start as the "pseudo" line 0 so that the end of the range can be line 1, allowing it a range of "only the first line" if the regex matches the first line.

    Or a simplified version (an empty RE like // means to re-use the one specified before it, so this is equivalent):

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

    And the curly braces are optional for the s command, so this is also equivalent:

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

    All of these work on GNU sed only.

    You can also install GNU sed on OS X using homebrew brew install gnu-sed.

    0 讨论(0)
  • 2020-11-22 04:33

    You could use awk to do something similar..

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

    Explanation:

    /#include/ && !done
    

    Runs the action statement between {} when the line matches "#include" and we haven't already processed it.

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

    This prints #include "newfile.h", we need to escape the quotes. Then we set the done variable to 1, so we don't add more includes.

    1;
    

    This means "print out the line" - an empty action defaults to print $0, which prints out the whole line. A one liner and easier to understand than sed IMO :-)

    0 讨论(0)
  • 2020-11-22 04:33

    I know this is an old post but I had a solution that I used to use:

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

    Basically use grep to print the first occurrence and stop there. Additionally print line number ie 5:line. Pipe that into sed and remove the : and anything after so you are just left with a line number. Pipe that into sed which adds s/.*/replace to the end number, which results in a 1 line script which is piped into the last sed to run as a script on the file.

    so if regex = #include and replace = blah and the first occurrence grep finds is on line 5 then the data piped to the last sed would be 5s/.*/blah/.

    Works even if first occurrence is on the first line.

    0 讨论(0)
  • 2020-11-22 04:35

    The use case can perhaps be that your occurences are spread throughout your file, but you know your only concern is in the first 10, 20 or 100 lines.

    Then simply adressing those lines fixes the issue - even if the wording of the OP regards first only.

    sed '1,10s/#include/#include "newfile.h"\n#include/'
    
    0 讨论(0)
  • 2020-11-22 04:36

    Using FreeBSD ed and avoid ed's "no match" error in case there is no include statement in a file to be processed:

    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
    
    0 讨论(0)
  • 2020-11-22 04:37

    i would do this with an awk script:

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

    then run it with awk:

    awk -f awkscript headerfile.h > headerfilenew.h
    

    might be sloppy, I'm new to this.

    0 讨论(0)
提交回复
热议问题