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