2

While appending some text after a matched pattern, I wanted to add an empty line with a comment to isolate my additions from the rest. However, I have noticed that for the very first \n, I need to escape the \ with an additional \ (makes it \\n) but this is apparently not required for rest of the appended text. Why is this so?

An example that I produced on a SUSE 11 server (bash v3.2.57(2) and GNU sed version 4.1.5) is:

edwdev@hdpedge1:/edw> sed '/test/a \\nNot a test\nJust Playing' <<< test
test

Not a test
Just Playing

If I do not escape the first \n (right after the a sed command, I get an n printed instead of a newline.

edwdev@hdpedge1:/edw> sed '/test/a \nNot a test\nJust Playing' <<< test
test
nNot a test
Just Playing

Notice that I have intentionally added a space after a command so that it doesn't conflict accidentally with alternate syntax.

Same behavior is observed if I replace \n with \t.Number of spaces after a command do not seem to matter; the first backslash needs to be escaped to print the special character even after multiple spaces.


Same is the case on another environment, where I originally observed this; a Centos 7 x64 VM, bash version is 4.2.46(2) and sed version is GNU sed 4.2.2

sed -i '/\[mysqld\]/a \\n# <<INSERTED MANUALLY>>\ntransaction-isolation=READ-COMMITTED\ninnodb_flush_method=O_DIRECT\nmax_connections=550\n# <<END OF MANUAL INSERTIONS >>\n' /etc/my.cnf

Output:

...
[mysqld]

# <<INSERTED MANUALLY>>
transaction-isolation=READ-COMMITTED
innodb_flush_method=O_DIRECT
max_connections=550
# <<END OF MANUAL INSERTIONS >>

...

Output Without Escaping first \n:

[mysqld]
n# <<INSERTED MANUALLY>>
transaction-isolation=READ-COMMITTED
innodb_flush_method=O_DIRECT
max_connections=550
# <<END OF MANUAL INSERTIONS >>

Is there any particular reason for this behavior? I am unable to find any details while skimming through the docs (here), through, admittedly, I haven't read the entire documentation thoroughly.

2 Answers 2

4

The standard form of the a command in sed is

a\
text to be appended

The c and i command follow the same format.

If further lines are to be appended, the newline of each has to be escaped:

a\
text to\
be appended

Standard sed does not know to interpret \n as newline as GNU sed does, so your single line command with a non-GNU sed would be written (in bash) as

sed $'/test/a\\\n\\\nNot a test\\\nJust Playing\n' <<<test

Here, we use $'...' to make the shell replace each \n with an actual newline and \\ with \.

The command could be written as the following without the $'...' feature of bash:

sed '/test/a\
\
Not a test\
Just Playing
' <<<test

Both of these commands would work with GNU sed too.

GNU sed additionally allows the appended text to occur without an intervening newline between it and the preceding a, and allows for dropping the \ completely, but will interpret the \ immediately following the a command as the backslash in the standard a\ form of the command (this is the basic issue in your question). GNU sed also adds a newline at the end of the appended text in the script. So with GNU sed, you could write

sed '/test/a\ \
Not a test\
Just Playing' <<<test

or, using the fact that GNU sed knows \n,

sed '/test/a\\nNot a test\nJust Playing' <<<test
1
  • It's also possible to to use multiple expressions to get the same effect as multiple lines. Looks somewhat nicer that way too sed -e '/test/a\' -e 'Not a test\nJust Playing' <<<test
    – Dabombber
    Jul 22, 2019 at 7:53
2

It's not exactly documented behaviour, but then the entire thing is an extension. All three commands that were extended similarly (a, i and c) behave the same way: leading whitespace is ignored, and if the text begins with \, then it is discarded. Essentially, it doesn't matter to sed which of these forms are used:

i foo
i \foo
i \
foo

They're all identical. (I know I used i here, but check the the descriptions of a, i and c - it's surreal.)

And the code shows as much:

1111         case 'a':
1112         case 'i':
1113         case 'c':
1114           ch = in_nonblank ();
1115 
1116         read_text_to_slash:
1117           if (ch == EOF)
1118             bad_prog (_(EXPECTED_SLASH));
1119 
1120           if (ch == '\\')
1121             ch = inchar ();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.