sed replace last line matching pattern

后端 未结 14 2082
暗喜
暗喜 2020-12-11 01:45

Given a file like this:

a
b
a
b

I\'d like to be able to use sed to replace just the last line that contains an instance of \"a

相关标签:
14条回答
  • 2020-12-11 02:05

    A two-pass solution for when buffering the entire input is intolerable:

    sed "$(sed -n /a/= file | sed -n '$s/$/ s,a,c,/p' )" file

    (the earlier version of this hit a bug with history expansion encountered on a redhat bash-4.1 install, this way avoids a $!d that was being mistakenly expanded.)

    A one-pass solution that buffers as little as possible:

    sed '/a/!{1h;1!H};/a/{x;1!p};$!d;g;s/a/c/'
    

    Simplest:

    tac | sed '0,/a/ s/a/c/' | tac
    
    0 讨论(0)
  • 2020-12-11 02:06

    Here's the command:

    sed '$s/.*/a/' filename.txt
    

    And here it is in action:

    > echo "a
    > b
    > a
    > b" > /tmp/file.txt
    
    > sed '$s/.*/a/' /tmp/file.txt
    a
    b
    a
    a
    
    0 讨论(0)
  • 2020-12-11 02:07

    Not quite sed only:

    tac file | sed '/a/ {s//c/; :loop; n; b loop}' | tac
    

    testing

    % printf "%s\n" a b a b a b | tac | sed '/a/ {s//c/; :loop; n; b loop}' | tac
    a
    b
    a
    b
    c
    b
    

    Reverse the file, then for the first match, make the substitution and then unconditionally slurp up the rest of the file. Then re-reverse the file.

    Note, an empty regex (here as s//c/) means re-use the previous regex (/a/)

    I'm not a huge sed fan, beyond very simple programs. I would use awk:

    tac file | awk '/a/ && !seen {sub(/a/, "c"); seen=1} 1' | tac
    
    0 讨论(0)
  • 2020-12-11 02:09

    This might work for you (GNU sed):

    sed -r '/^PATTERN/!b;:a;$!{N;/^(.*)\n(PATTERN.*)/{h;s//\1/p;g;s//\2/};ba};s/^PATTERN/REPLACEMENT/' file
    

    or another way:

    sed '/^PATTERN/{x;/./p;x;h;$ba;d};x;/./{x;H;$ba;d};x;b;:a;x;/./{s/^PATTERN/REPLACEMENT/p;d};x' file
    

    or if you like:

    sed -r ':a;$!{N;ba};s/^(.*\n?)PATTERN/\1REPLACEMENT/' file
    

    On reflection, this solution may replace the first two:

    sed  '/a/,$!b;/a/{x;/./p;x;h};/a/!H;$!d;x;s/^a$/c/M' file
    

    If the regexp is no where to found in the file, the file will pass through unchanged. Once the regex matches, all lines will be stored in the hold space and will be printed when one or both conditions are met. If a subsequent regex is encountered, the contents of the hold space is printed and the latest regex replaces it. At the end of file the first line of the hold space will hold the last matching regex and this can be replaced.

    0 讨论(0)
  • 2020-12-11 02:11

    Here is all done in one single awk

    awk 'FNR==NR {if ($0~/a/) f=NR;next} FNR==f {$0="c"} 1' file file
    a
    b
    c
    b
    

    This reads the file twice. First run to find last a, second run to change it.

    0 讨论(0)
  • 2020-12-11 02:15

    Another one:

    tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
    

    in action:

    $ printf "%s\n" a b a b a b | tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
    a
    b
    a
    b
    c
    b
    
    0 讨论(0)
提交回复
热议问题