sed replace last line matching pattern

后端 未结 14 2081
暗喜
暗喜 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 01:56

    tac infile.txt | sed "s/a/c/; ta ; b ; :a ; N ; ba" | tac

    The first tac reverses the lines of infile.txt, the sed expression (see https://stackoverflow.com/a/9149155/2467140) replaces the first match of 'a' with 'c' and prints the remaining lines, and the last tac reverses the lines back to their original order.

    0 讨论(0)
  • 2020-12-11 01:57

    Many good answers here; here's a conceptually simple two-pass sed solution assisted by tail that is POSIX-compliant and doesn't read the whole file into memory, similar to Eran Ben-Natan's approach:

    sed "$(sed -n '/a/ =' file | tail -n 1)"' s/a/c/' file
    
    • sed -n '/a/=' file outputs the numbers of the lines (function =) matching regex a, and tail -n 1 extracts the output's last line, i.e. the number of the line in file file containing the last occurrence of the regex.

    • Placing command substitution $(sed -n '/a/=' file | tail -n 1) directly before ' s/a/c' results in an outer sed script such as 3 s/a/c/ (with the sample input), which performs the desired substitution only on the last on which the regex occurred.

    If the pattern is not found in the input file, the whole command is an effective no-op.

    0 讨论(0)
  • 2020-12-11 01:59

    Here's another option:

    sed -e '$ a a' -e '$ d' file 
    

    The first command appends an a and the second deletes the last line. From the sed(1) man page:

    $ Match the last line.

    d Delete pattern space. Start next cycle.

    a text Append text, which has each embedded newline preceded by a backslash.

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

    Another approach:

    sed "`grep -n '^a$' a | cut -d \: -f 1 | tail -1`s/a/c/" a
    

    The advantage of this approach is that you run sequentially on the file twice, and not read it to memory. This can be meaningful in large files.

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

    Given:

    $ cat file
    a
    b
    a
    b
    

    You can use POSIX grep to count the matches:

    $ grep -c '^a' file
    2
    

    Then feed that number into awk to print a replacement:

    $ awk -v last=$(grep -c '^a' file) '/^a/ && ++cnt==last{ print "c"; next } 1' file
    a
    b
    c
    b
    
    0 讨论(0)
  • 2020-12-11 02:03

    It can also be done in perl:

    perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp > your_new_file
    

    Tested:

    > cat temp
    a
    b
    c
    a
    b
    > perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp
    a
    b
    c
    c
    b
    > 
    
    0 讨论(0)
提交回复
热议问题