1

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.

enghyd1
  • 40
  • 5
  • 2
    And what if your file contains two consecutive `abc:`? Do you want to comment the first one? If yes, do you want to comment the line before it? – Renaud Pacalet Apr 24 '23 at 09:31

7 Answers7

2

A tac | awk + tac solution:

tac file | awk 'p {$0 = "#" $0} {p = (/^abc:/ ? 1 : 0)} 1'  | tac

#abcd
abc:
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

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.

jhnc
  • 11,310
  • 1
  • 9
  • 26
0

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.

markalex
  • 8,623
  • 2
  • 7
  • 32
  • It outputs abc#d abc: However I need #abcd abc: – enghyd1 Apr 24 '23 at 06:09
  • 1
    @enghyd1, it shouldn't. Look at this [demo](https://ideone.com/p7TR59) – markalex Apr 24 '23 at 06:16
  • @enghyd1 your input file probably contains DOS line endings, see [why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it](https://stackoverflow.com/questions/45772525/why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it) – Ed Morton Apr 24 '23 at 11:27
0

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.

  • We use the -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.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • 1
    `sed 'N; /\nabc:/s/^/#/; P; D'` – jhnc Apr 24 '23 at 16:58
  • Excellent! And so simpler. You should make it an answer, I'd upvote twice if I could. Note: as the OP did not specify that the `abc:` string is at the beginning of a line we should maybe use `\n.*abc:` instead of `\nabc:`. – Renaud Pacalet Apr 25 '23 at 04:40
  • It's based on my existing answer but this code can be simpler because your answer makes different assumptions about what behaviour is desired. The question is unclear. An additional possiblity that I don't think any current answers support is that `abc:` on the first line should cause a new blank comment line to be prepended. – jhnc Apr 25 '23 at 05:00
0

Using any awk:

$ awk 'NR>1{print (/^abc:/ ? "#" : "") prev} {prev=$0} END{print prev}' file
#abcd
abc:
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

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:

Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47
0

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:.

potong
  • 55,640
  • 6
  • 51
  • 83