Want to substitute only first occurence with sed

If you are using GNU sed, try:

sed -e '0,/claudio/ s/claudio/claudia/' nomi

sed does not start checking for the regex that ends a range until after the line that starts that range.

From man sed (POSIX manpage, emphasis mine):

An editing command with two addresses shall select the inclusive range from the first pattern space that matches the first address through the next pattern space that matches the second.

The 0 address is not standard though, that's a GNU sed extension not supported by any other sed implementation.

Using awk

Ranges in awk work more as you were expecting:

$ awk 'NR==1,/claudio/{sub(/claudio/, "claudia")} 1' nomi
claudia
antonio
claudio
michele

Explanation:

  • NR==1,/claudio/

    This is a range that starts with line 1 and ends with the first occurrence of claudio.

  • sub(/claudio/, "claudia")

    While we are in the range, this substitute command is executed.

  • 1

    This awk's cryptic shorthand for print the line.


Here are 2 more programmatic efforts with sed: they both read the whole file into a single string, then the search will only replace the first one.

sed -n ':a;N;$bb;ba;:b;s/\(claudi\)o/\1a/;p' file
sed -n '1h;1!H;${g;s/\(claudi\)o/\1a/;p;}' file

With commentary:

sed -n '                # don't implicitly print input
  :a                    # label "a"
  N                     # append next line to pattern space
  $bb                   # at the last line, goto "b"
  ba                    # goto "a"
  :b                    # label "b"
  s/\(claudi\)o/\1a/    # replace
  p                     # and print
' file
sed -n '                # don't implicitly print input
  1h                    # put line 1 in the hold space
  1!H                   # for subsequent lines, append to hold space
  ${                    # on the last line
    g                     # put the hold space in pattern space
    s/\(claudi\)o/\1a/    # replace
    p                     # print
  }
' file

A new version of GNU sed supports the -z option.

Normally, sed reads a line by reading a string of characters up to the end-of-line character (new line or carriage return).
The GNU version of sed added a feature in version 4.2.2 to use the "NULL" character instead. This can be useful if you have files that use the NULL as a record separator. Some GNU utilities can generate output that uses a NULL instead a new line, such as "find . -print0" or "grep -lZ".

You can use this option when you want sed to work over different lines.

echo 'claudio
antonio
claudio
michele' | sed -z 's/claudio/claudia/'

returns

claudia
antonio
claudio
michele

Tags:

Sed