Suppose below is a text file
abcd
abc:
I want to search for abc:
and add #
in front of abcd
OUTPUT
#abcd
abc:
I tried using
sed -e '/abc/ s/^#*/#/' -i file
but unable to do the above action.
Suppose below is a text file
abcd
abc:
I want to search for abc:
and add #
in front of abcd
OUTPUT
#abcd
abc:
I tried using
sed -e '/abc/ s/^#*/#/' -i file
but unable to do the above action.
A tac | awk + tac
solution:
tac file | awk 'p {$0 = "#" $0} {p = (/^abc:/ ? 1 : 0)} 1' | tac
#abcd
abc:
Since you are editing in-place, you can use ed
:
printf '%s\n' '/abc:/-1 s/^/#/' w q | ed -s file
With POSIX sed
it is more tricky. One possibility is:
sed -i '
N
/abc:/ {
s/^/#/
:a
n
ba
}
P
D
' file
Both scripts assume that abc:
cannot appear on the first line of the file, and that you wish to change only the line above the first occurrence of the pattern even if it appears multiple times.
If you are using GNU sed
, you can use -z
option to parse whole file at once and not line-by-line.
You final command would look something like this:
sed -zE 's/[^\n]*\nabc:/#\0/g' file
Here [\n]*\nabc:
matches anything, newline and abc:
and replaces it with #
and previously matched data. \0
in replacement string means full match of regex.
With sed
(tested with GNU sed
and the sed
that comes with macOS):
sed -n '1{h;d;};/abc:/{x;s/^/#/;x;};{x;p;};${g;p;}' file
Explanations: sed
manages two data buffers: the hold space and the pattern space. When a new line is read it is stored in the pattern space. The script uses both spaces.
-n
option to suppress the automatic printing. We print explicitly with the p
command.1{h;d;}
: if the current line is the first (address 1
) we copy it to the hold space (h
) and start a new cycle (d
)./abc:/{x;s/^/#/;x;}
: if a line (other than the first) matches abc:
we swap hold and pattern spaces (x
), prepend a #
(s/^/#/
) and swap again (x
).{x;p;}
: we swap hold and pattern spaces (x
) and print (p
).${g;p;}
: after reading the last line ($
) we copy the hold space to pattern space (g
) and print (p
).Note: this is a bit more complicated than using the -z
option but works even if your source file contains NUL characters, and avoids slurping the whole file in memory which can be an issue with large files.
Note: if your file contains consecutive lines matching abc:
they will be commented too. Example:
#foo
#abc:
#abc:
abc:
bar
If this is what you want add the -i
flag to edit in place.
Using any awk:
$ awk 'NR>1{print (/^abc:/ ? "#" : "") prev} {prev=$0} END{print prev}' file
#abcd
abc:
Use this Perl one-liner, as in the following example:
# Prepare test input:
cat > infile <<EOF
abcd
abc:
abc:
efgh
ijhk
abc:
EOF
perl -i.bak -lne '$prev = $curr; $curr = $_; if ( $curr =~ m{^abc:} ) { $prev = "#$prev"; } if ( $. > 1 ) { print $prev; } if ( eof ) { print $curr; } ' infile
# Print the result:
cat infile
#abcd
#abc:
abc:
efgh
#ijhk
abc:
Note that the previous line is always commented if the current line starts with abc:
.
The Perl one-liner uses these command line flags:
-e
: Tells Perl to look for code in-line, instead of in a file.
-n
: Loop over the input one line at a time, assigning it to $_
by default.
-l
: Strip the input line separator ("\n"
on *NIX by default) before executing the code in-line, and append it when printing.
-i.bak
: Edit input files in-place (overwrite the input file). Before overwriting, save a backup copy of the original file by appending to its name the extension .bak
. If you want to skip writing a backup file, just use -i
and skip the extension.
$.
: Current input line number.
eof
: Returns 1 (true) if the end of file is found.
See also:
This might work for you (GNU sed):
sed 'N;s/.*\n.*abc:/#&/;P;D' file
Append the next line and if that line contains abc:
, insert #
before the first line. Then print/delete the first line and repeat.
N.B. This might comment a line containing abc:
if that line is above another containing abc:
.