Nice easy sed question

1 view
Skip to first unread message

Frank Leonhardt

unread,
Sep 11, 2025, 6:15:16 PMSep 11
to questions
I'm going quietly crazy here. BSD sed is it's own thing, but I can't see
what I'm doing wrong...

I've got a file called example.txt:

Line 1
Line 2
Line 3

I'm trying to add "New Line" after "Line 2"

Both of these should work as far as I know:

sed  -i.bak  '/Line 2/a\New Line' example.txt
sed  -i.bak  -e '/Line 2/a\" -e "New Line' example.txt

(BSD requires a backup specification for -i IIRC, or '')

If I run it with the -i and have a genuine newline after \ it does write
the correct stuff to stdout:

# sed  '/Line 2/a\
> New Line' example.txt
Line 1
Line 2
New Line
Line 3


With an -i in any variations of the first two (single command or two -e
-e) I just get errors like:

": invalid command code e
": extra characters at the end of N command
": extra characters after \ at the end of a command

(All starting with " - why?)

The only way I can make it work is with -i.bak and on two lines (as above).

The Fine Manual says of 'a':

  [1addr]a\
     text    Write text to standard output immediately before each
attempt to
             read a line of input, whether by executing the “N”
function or by
             beginning a new cycle.

This implies the newline is required but I'm struggling with finding a
sane syntax here. A pointer to some better documentation would be welcome!

Or is there a better utility for editing (non-system) configuration
files by script I just don't know about?

Thanks, Frank.



Kurt Hackenberg

unread,
Sep 11, 2025, 7:04:50 PMSep 11
to ques...@freebsd.org
On Thu, Sep 11, 2025 at 11:14:50PM +0100, Frank Leonhardt wrote:

>With an -i in any variations of the first two (single command or two
>-e -e) I just get errors like:
>
>": invalid command code e
>": extra characters at the end of N command
>": extra characters after \ at the end of a command

Because you put the command line in the wrong order? Just looking at
the man page, apparently most one-letter flags come before the editing
command, but -I and -i come after it. Peculiar.

SYNOPSIS
sed [-Ealnru] command [-I extension] [-i extension] [file ...]
sed [-Ealnru] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]

I haven't tested this. Haven't looked at the newline problem, either.

Kyle Evans

unread,
Sep 11, 2025, 8:19:43 PMSep 11
to ques...@freebsd.org
The synopsis and usage string don't seem to be based in reality all the way back to BSD 4.4 Lite,
the original usage here is technically fine.

Thanks,

Kyle Evans

Bob Proulx

unread,
Sep 12, 2025, 2:04:48 AMSep 12
to ques...@freebsd.org
Frank Leonhardt wrote:
> I've got a file called example.txt:
>
> Line 1
> Line 2
> Line 3
>
> I'm trying to add "New Line" after "Line 2"

Okay.

> Both of these should work as far as I know:
>
> sed -i.bak '/Line 2/a\New Line' example.txt

That \N is not valid syntax.

> sed -i.bak -e '/Line 2/a\" -e "New Line' example.txt

That \" is not valid syntax.

> (BSD requires a backup specification for -i IIRC, or '')

Yes. An annoying choice! Perl pioneered the syntax and being first
at doing so and it being adequate everyone else should have followed
for consistency.

> If I run it with the -i and have a genuine newline after \ it does write the
> correct stuff to stdout:
>
> # sed '/Line 2/a\
> > New Line' example.txt
> Line 1
> Line 2
> New Line
> Line 3

Yes. The \ must be followed by a newline. You have the correct syntax.

> With an -i in any variations of the first two (single command or two -e -e)
> I just get errors like:
>
> ": invalid command code e
> ": extra characters at the end of N command
> ": extra characters after \ at the end of a command
>
> (All starting with " - why?)

Works for me. Here is an example.

rwp@mayhem:~$ sed -i.bak '/Line 2/a\
New Line' foo1
rwp@mayhem:~$ cat foo1
Line 1
Line 2
New Line
Line 3

And with use of -e for two different expressions works too.

rwp@mayhem:~$ sed -i.bak -e '/^Line 2/a\
New Line' -e 's/3/4/' foo1
rwp@mayhem:~$ cat foo1
Line 1
Line 2
New Line
Line 4

What is the exact command you are having trouble with?

> The only way I can make it work is with -i.bak and on two lines (as above).

The newline after the \ backslash is required. That is the correct syntax.

> The Fine Manual says of 'a':
>
> [1addr]a\
> text Write text to standard output immediately before each attempt to
> read a line of input, whether by executing the “N” function or by
> beginning a new cycle.

The manual was written by people who already knew how it worked. The
newline after the backslash is as far as I know always required. The
behavior is based upon the ed feature.

man ed
(.)a Append text to the buffer after the addressed line. Text is
entered in input mode. The current address is set to last line
entered.

Here is an ed example.

rwp@mayhem:~$ ed -s foo1 <<EOF
/^Line 2/a
New line
.
w
q
EOF
rwp@mayhem:~$ cat foo1
Line 1
Line 2
New line
Line 3

> This implies the newline is required but I'm struggling with finding a sane
> syntax here. A pointer to some better documentation would be welcome!

This confuses me because you have a working example using sed and it works.

> Or is there a better utility for editing (non-system) configuration files by
> script I just don't know about?

That depends upon many things. What file is being edited? Is it an
INI file? A CSV? Would a tool like Puppet, Chef, Salt, Ansible be
better? Perl is powerful and a good general purpose batch editing
choice. Perhaps ed is the best answer. Any recommendation without
information would just be a guess in the dark.

I personally like sed and most often use sed to automate edit files.
I always use it in combination with grep to look to see if the file
needs to be edited first in order to make the sequence idempotent such
that files are not always being touched if there is no reason to touch
them. Because having files updated all of the time falsely points
fingers at them when there are problems.

Bob

Sad Clouds

unread,
Sep 12, 2025, 2:37:40 AMSep 12
to Frank Leonhardt, questions
On Thu, 11 Sep 2025 23:14:50 +0100
Frank Leonhardt <freeb...@fjl.co.uk> wrote:

> Or is there a better utility for editing (non-system) configuration
> files by script I just don't know about?

I tend to find awk is more flexible for these things, but I avoid any
GNU and BSD extensions so the scripts remain portable.

$ cat << 'EOF' | awk '{print} $0=="Line 2" {print "New Line"}'
Line 1
Line 2
Line 3
EOF

ax disroo

unread,
Sep 12, 2025, 5:45:41 AMSep 12
to Frank Leonhardt, questions
> On Sep 11, 2025, at 12:15 PM, Frank Leonhardt wrote:
>
> I'm going quietly crazy here. BSD sed is it's own thing, but I can't see what I'm doing wrong...
>
> I've got a file called example.txt:
>
> Line 1
> Line 2
> Line 3
>
> I'm trying to add "New Line" after "Line 2"
>
> Both of these should work as far as I know:
>
> sed -i.bak '/Line 2/a\New Line' example.txt
> sed -i.bak -e '/Line 2/a\" -e "New Line' example.txt
>
> (BSD requires a backup specification for -i IIRC, or '')
>
> If I run it with the -i and have a genuine newline after \ it does write the correct stuff to stdout:
>
> # sed '/Line 2/a\
> > New Line' example.txt
> Line 1
> Line 2
> New Line
> Line 3

> The Fine Manual says of 'a':
>
> [1addr]a\
> text Write text to standard output immediately before each attempt to
> read a line of input, whether by executing the “N” function or by
> beginning a new cycle.
>
> This implies the newline is required but I'm struggling with finding a sane syntax here. A pointer to some better documentation would be welcome!
>
> Or is there a better utility for editing (non-system) configuration files by script I just don't know about?

Use GNU sed instead; your first syntax certainly worked with v4.{5,9} (did not bother to try the second with two “-e”) …

cat <<X | gsed -e '/A/a\-C 8'
A 2
B 5
X

- ax


Frank Leonhardt

unread,
Sep 12, 2025, 5:59:14 AMSep 12
to freebsd-...@freebsd.org
This is a reply to several including Bob, Sad and Kyle.

TLDR: The methods and syntax I was using are fine on GNU sed but
BSD sed appears to require a newline and there's no way around it. And
we all seem to agree that the documentation isn't the best.


On 12/09/2025 07:04, Bob Proulx wrote:
Thanks Bob, et al.

I was suspecting the newline was the issue. However the following do work perfectly correctly on GNU sed.
sed  -i.bak  '/Line 2/a\New Line' example.txt
sed  -i.bak  -e '/Line 2/a\' -e 'New Line' example.txt

(I used mismatched quotes in the email when typing originally, but the correct way around on the command line).
I think I've finally found something that Linux does better - it took long enough :-)
If BSD sed really is that particular about there being a newline, which IMHO is really clunky in a script, then that's what I'll have to do - and put a wrapper around it so it doesn't offend my sensibilities!

@ Sad Clouds

Yes, awk. I've been meaning to make friends with awk since System V. I've just found other ways of doing things in the mean time.


On 12/09/2025 01:19, Kyle Evans wrote:
The synopsis and usage string don't seem to be based in reality all the way back to BSD 4.4 Lite,
the original usage here is technically fine.

Thanks,

Kyle Evans

Yes, exactly!

I've sanity checked things in Kernighan and Pike, which is a lot better than the man page but all their examples centre on the 's' command.

Thanks, Frank.

Arthur Chance

unread,
Sep 12, 2025, 7:23:08 AMSep 12
to Frank Leonhardt, freebsd-...@freebsd.org
On 12/09/2025 08:58, Frank Leonhardt wrote:
> This is a reply to several including Bob, Sad and Kyle.
>
> TLDR: The methods and syntax I was using are fine on GNU sed but
> BSD sed appears to require a newline and there's no way around it.
Presuming you're working with sh or bash (as opposed to {t,}csh) and
it's having an actual newline that's annoying, you can always use sh's
dollar single quote strings. Then you get the alternate annoyance of
backslash doubling instead.

sed -i.bak -e $'/Line 2/a\\\nNew Line' example.txt

--
Vibe coding - the technical debt of tomorrow.

Bob Proulx

unread,
Sep 12, 2025, 6:19:37 PMSep 12
to ques...@freebsd.org
Frank Leonhardt wrote:
> TLDR: The methods and syntax I was using are fine on GNU sed but
> BSD sed appears to require a newline and there's no way around it.

Since this is the FreeBSD questions mailing list I don't think I can
be faulted for assuming we are talking about FreeBSD sed by default.
And you specifically asked about BSD sed. If you want to talk about
GNU sed then of course it's GNU and a different source code tree and
will have differences.

> And we all seem to agree that the documentation isn't the best.

It's really not that bad though. And there are many examples
available on the web. For an excellent treatment of sed I suggest the
O'Reilly book "sed & awk" by Dale Dougherty & Arnold Robbins.

> I was suspecting the newline was the issue. However the following do
> work perfectly correctly on GNU sed.

GNU sed is a different code base. It has different features added to
it. This is sometimes good and sometimes bad. Differences create
portability problems. But extensions are sometimes useful. But
FreeBSD sed is a traditional Unix sed and expects a traditional syntax.

> sed -i.bak '/Line 2/a\New Line' example.txt
> sed -i.bak -e '/Line 2/a\' -e 'New Line' example.txt

Those are not GNU sed syntax either.

GNU sed has extended sed to allow single line append, insert, change
commands. It's not portable. But GNU sed allows this.

GNU$ printf "%s\n" "Line 1" "Line 2" "Line 3" |
sed -e '/^Line 2/iLine before 2' -e '/^Line 2/aLine after 2'

Outputs:

Line 1
Line before
Line 2
Line after
Line 3

Note that the backslash after the a, i, c is not there when using the
GNU sed extension syntax. But it must be there for portable sed for
use with the traditional Unix BSD sed.

BSD$ printf "%s\n" "Line 1" "Line 2" "Line 3" |
> sed -e '/^Line 2/i\
> Line before 2' -e '/^Line 2/a\
> Line after 2'

Outputs:

Line 1
Line before 2
Line 2
Line after 2
Line 3

Using the traditional syntax one can list multiple commands together.

$ printf "%s\n" "Line 1" "Line 2" "Line 3" | sed -e '
/^Line 2/i\
Line before 2
/^Line 2/a\
Line after 2'

Outputs:

Line 1
Line before 2
Line 2
Line after 2
Line 3

Notice that the insert and append commands are terminated by a
newline. The newline, or end of input, is part of the syntax.

> I think I've finally found something that Linux does better - it took long
> enough :-)

:-)

GNU is a newer rewrite and there are various extensions there that BSD
could benefit from implementing as well.

> If BSD sed really is that particular about there being a newline, which IMHO
> is really clunky in a script, then that's what I'll have to do

Yes. It needs a newline. But you can insert that newline in more
ways than using an extra line there.

> - and put a wrapper around it so it doesn't offend my sensibilities!

I don't find the line break to be that terrible when used in a script.
It annoys me more on the command line. But in a script it is fine.

Arthur's posting suggests using the shell's $'...' quoting which
enables escape sequences which enables embedding newlines. That's a
reasonable option. I'll make a follow-up comment there.

> Kyle Evans wrote:
> > The synopsis and usage string don't seem to be based in reality
> > all the way back to BSD 4.4 Lite, the original usage here is
> > technically fine.
...
> Yes, exactly! I've sanity checked things in Kernighan and Pike, which is a
> lot better than the man page but all their examples centre on the 's'
> command. Thanks, Frank.

I think the synopsis is about as good as it can get in the document
format of a manpage. It is missing a little explanation that says how
to terminate a, i, c commands and how to chain in multiple commands
with one following the other. It wasn't designed to squeeze an entire
sed program all on one line, at least not all of the time. There are
some quite long and pretty gnarly multiline sed programs that are
unreadable on multiple lines without jamming them all together onto
one line.

Bob

Kyle Evans

unread,
Sep 12, 2025, 6:30:43 PMSep 12
to Bob Proulx, ques...@freebsd.org
On 9/12/25 17:19, Bob Proulx wrote:
> Frank Leonhardt wrote:
>> Kyle Evans wrote:
>>> The synopsis and usage string don't seem to be based in reality
>>> all the way back to BSD 4.4 Lite, the original usage here is
>>> technically fine.
> ...
>> Yes, exactly! I've sanity checked things in Kernighan and Pike, which is a
>> lot better than the man page but all their examples centre on the 's'
>> command. Thanks, Frank.
>
> I think the synopsis is about as good as it can get in the document
> format of a manpage. It is missing a little explanation that says how
> to terminate a, i, c commands and how to chain in multiple commands
> with one following the other. It wasn't designed to squeeze an entire
> sed program all on one line, at least not all of the time. There are
> some quite long and pretty gnarly multiline sed programs that are
> unreadable on multiple lines without jamming them all together onto
> one line.
>

The SYNOPSIS is factually incorrect, though (see https://reviews.freebsd.org/D52495).

We don't do GNU-style permutation of non-option arguments and, as far as I'm aware,
have never done so in getopt(3)[0]. Stating that any options can come after the
script/command will just lead to unexpected behavior, because we treat those as
files rather than restarting option parsing.

Thanks,

Kyle Evans

[0] getopt_long(3) does, though, which is why it recognizes a leading '+' style to
get back to the historical behavior

Bob Proulx

unread,
Sep 12, 2025, 6:46:35 PMSep 12
to ques...@freebsd.org
Arthur Chance wrote:
> Presuming you're working with sh or bash (as opposed to {t,}csh) and
> it's having an actual newline that's annoying, you can always use sh's
> dollar single quote strings. Then you get the alternate annoyance of
> backslash doubling instead.
>
> sed -i.bak -e $'/Line 2/a\\\nNew Line' example.txt

This is a good suggestion. However it does turn on escape sequence
interpretation.

Using $'...' to enable escape sequences does work in both BSD and GNU
shells but then you must also be aware that escape sequences are
enabled and deal with escaping the escapes when using them with sed
commands.

Dollar-Single Quotes
Enclosing characters between $' and ' preserves the literal
meaning of all characters except backslashes and single quotes.
A backslash introduces a C-style escape sequence:
\a Alert (ring the terminal bell)
\b Backspace
\cc The control character denoted by ^c in stty(1). If c
is a backslash, it must be doubled.
\e The ESC character (ASCII 0x1b)
\f Formfeed
\n Newline
\r Carriage return
\t Horizontal tab
\v Vertical tab
\\ Literal backslash
\' Literal single-quote
\" Literal double-quote
\nnn The byte whose octal value is nnn (one to three
digits)
\xnn The byte whose hexadecimal value is nn (one or more
digits only the last two of which are used)
\unnnn The Unicode code point nnnn (four hexadecimal digits)
\Unnnnnnnn The Unicode code point nnnnnnnn (eight hexadecimal
digits)

Dealing with the escape sequences above when I am also using
backslashes in sed is what keeps me from doing it this way. But it is
a good suggestion and does work.

The FreeBSD sh shell is not a strict POSIX shell and implements this
ksh-ism so strictly speaking that is not portable but it does work.
It will work on both BSD and GNU systems though so maybe least
objectionable to you.

This is what POSIX has to say about it.

The '$' character is used to introduce parameter expansion, command
substitution, or arithmetic evaluation. If an unquoted '$' is followed
by a character that is not one of the following:

A numeric character

The name of one of the special parameters (see Special Parameters)

A valid first character of a variable name

A <left-curly-bracket> ( '{' )

A <left-parenthesis>

the result is unspecified.

The result of $'...' such as $'\n' is strictly speaking "unspecified"
behavior by the standard.

Bob

Bob Proulx

unread,
Sep 12, 2025, 7:05:08 PMSep 12
to ques...@freebsd.org
Kyle Evans wrote:
> Bob Proulx wrote:
> > I think the synopsis is about as good as it can get in the document
> > format of a manpage. It is missing a little explanation that says how
> > to terminate a, i, c commands and how to chain in multiple commands
> > with one following the other. It wasn't designed to squeeze an entire
> > sed program all on one line, at least not all of the time. There are
> > some quite long and pretty gnarly multiline sed programs that are
> > unreadable on multiple lines without jamming them all together onto
> > one line.
>
> The SYNOPSIS is factually incorrect, though (see https://reviews.freebsd.org/D52495).

Oh! I thought we were talking about this part.

[1addr]a\
text Write text to standard output immediately before each attempt to
read a line of input, whether by executing the “N” function or by
beginning a new cycle.
[2addr]c\
text Delete the pattern space. With 0 or 1 address or at the end of a
2-address range, text is written to the standard output.
[1addr]i\
text Write text to the standard output.

That's the documentation I thought I was referring to. It's not
really clear that a newline terminates the command. An example or
three would help to make that more apparent.

> We don't do GNU-style permutation of non-option arguments and, as far as I'm aware,

And I would not have thought to try it with placing options in any
other order either.

> have never done so in getopt(3)[0]. Stating that any options can come after the
> script/command will just lead to unexpected behavior, because we treat those as
> files rather than restarting option parsing.

sed [-Ealnru] command [-I extension] [-i extension] [file ...]

Right. That's wrong. At least something like this.

sed [-Ealnru] [-I extension] [-i extension] command [file ...]

Or whatever you wrote in the bug report that is being reviewed.

Bob

P.S. I have this belief that option permutation is a style that came
to GNU from MS-DOS which always uses /options at the end of the option
arguments rather than before them.

dir *.* /p

I think a generation of people grew up with that syntax that options
go at the end which caused them to want to type it in that way with
Unix commands too. I am sure it was at least an influence anyway.

Thomas Dickey

unread,
Sep 12, 2025, 8:06:10 PMSep 12
to Bob Proulx, ques...@freebsd.org
On Fri, Sep 12, 2025 at 05:04:45PM -0600, Bob Proulx wrote:
...
> P.S. I have this belief that option permutation is a style that came
> to GNU from MS-DOS which always uses /options at the end of the option
> arguments rather than before them.
>
> dir *.* /p

that's possible (if you're talking about the people who got involved
with programming around 1990 -- before that, I've not found much influence),
but other systems have done that too.

For example, IBM VM/CMS put options at the end -- after a delimiting "("

https://invisible-island.net/personal/oldprogs.html#y1983

> I think a generation of people grew up with that syntax that options
> go at the end which caused them to want to type it in that way with
> Unix commands too. I am sure it was at least an influence anyway.

...but AT&T getopt came earlier than 1990, anyway

--
Thomas E. Dickey <dic...@invisible-island.net>
https://invisible-island.net
signature.asc

Frank Leonhardt

unread,
Sep 13, 2025, 4:42:27 AMSep 13
to ques...@freebsd.org

Interesting discussion guys - thanks for posting. "Nice easy sed question" was 8v)

I actually solved the problem by using wrapper functions in sh to make the i and a commands (in particular) easy to read. I only wanted to add/remove/edit lines from configuration files while writing an easy-to-follow installation script. I discovered by quoting the newline you can add several lines in one hit. I'm now going to check its portable enough to work in a penguin colony.

And Bob - agree there's a lot of MS-DOS in Linux - look at the calling convention for a start!

Thanks, Frank.

Dag-Erling Smørgrav

unread,
Sep 15, 2025, 1:45:48 PMSep 15
to Bob Proulx, ques...@freebsd.org
Bob Proulx <b...@proulx.com> writes:
> The FreeBSD sh shell is not a strict POSIX shell

Technically correct.

> and implements this ksh-ism so strictly speaking that is not portable
> but it does work.

Incorrect, dollar-quoted strings are part of POSIX.

> This is what POSIX has to say about it.
>
> The '$' character is used to introduce parameter expansion, command
> substitution, or arithmetic evaluation. If an unquoted '$' is followed
> by a character that is not one of the following:

You are looking in the wrong place. See XCU 2.2.4:

https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_02_04

> The result of $'...' such as $'\n' is strictly speaking "unspecified"
> behavior by the standard.

On the contrary, it is perfectly well-defined.

DES
--
Dag-Erling Smørgrav - d...@FreeBSD.org

Erichans

unread,
Sep 18, 2025, 4:06:20 AMSep 18
to freeb...@fjl.co.uk, freebsd-...@freebsd.org
Frank Leonhardt wrote in his OP:
> I've got a file called example.txt:
>
> Line 1
> Line 2
> Line 3
>
> I'm trying to add "New Line" after "Line 2"

> This implies the newline is required but I'm struggling with finding a
> sane syntax here. A pointer to some better documentation would be welcome!

Frank Leonhardt also wrote:
> TLDR: The methods and syntax I was using are fine on GNU sed but
> BSD sed appears to require a newline and there's no way around it.

Various factors come into play:
(A) Using sed completely on the command line combines a shell
language with a programming language (i.e. scripting language).
(B) GNU sed is not equal to FreeBSD sed, is not equal to POSIX sed.
(I’m not aware if there’s a POSIX sed reference implementation.)
(C) Man page or standardisation texts (i.e. POSIX) are geared for
reference. Their inherent terseness often requires very careful
reading; subtleties can easily be overlooked.

In addition to the last item, sometimes an option is absent from its
man page, as is the case with gsed(1). A portable solution across
various OS-es is often needed.

Below, you'll find:
- a different portable sed solution;
- discussion of the two GNU sed formats of the 'a' command,
the problem of the --posix option;
- another command line solution, not using sed,
POSIX 'certified'.

A small improvement suggestion for the FreeBSD sed(1)
man page concludes this e-mail.

Various solutions exist that present "in-place editing", however,
under the hood, this is likely defeated everywhere. Using any
other language that uses a temporary file explicitly without
in-place editing can work just as well. I'll use FreeBSD sed, and
gsed (GNU sed) as installed from textproc/gsed, running on 14.3-R.

sed -E -i.bak '/2/{H;g;s/(\n)(.*)/\2\1New Line/;}' example.txt

There is no shell continuation and it works for sed and GNU sed;
example run:
$ seq 3|sed 's/./Line &/' > i; sed -E -i.bak
'/2/{H;g;s/(\n)(.*)/\2\1New Line/;}' i; cat i
Line 1
Line 2
New Line
Line 3


=== gsed's 'a' command – two formats

Bob Proulx in response to Frank's posting:
> Both of these should work as far as I know:
>
> sed -i.bak '/Line 2/a\New Line' example.txt

That \N is not valid syntax.

> sed -i.bak -e '/Line 2/a\" -e "New Line' example.txt

That \" is not valid syntax.
QUOTE_END

Taking the following as starting point:
gsed -i.bak '/Line 2/a\New Line' example.txt
gsed -i.bak -e '/Line 2/a\" -e "New Line' example.txt

Both are valid GNU syntax, but they may work differently
then expected. I find the GNU syntax description not very precise
and, as we'lll see, --posix isn't all that strict.

GNU sed has two formats of the a command:
(1) a text
(2) a\
text

Remarkably, format (1), a GNU extension, is absent from the gsed(1) man page:
https://man.freebsd.org/cgi/man.cgi?query=gsed&apropos=0&sektion=1&manpath=FreeBSD+Ports+14.3&arch=default&format=html

The GNU html web page and its examples describes both formats:
https://www.gnu.org/software/sed/manual/sed.html#Less-Frequently_002dUsed-Commands
Furthermore:
As a GNU extension, the a command and text can be separated into two
-e parameters, enabling easier scripting:
[...]
QUOTE_END

> sed -i.bak '/Line 2/a\New Line' example.txt
In Frank's referenced command, the '\' however, designates
the use of a literal character immediately following. Thus,
equivalent to:
gsed -i.bak '/Line 2/a New Line' example.txt

I don't see the format without the space after the 'a'
spelled out in its syntax description html web page though.

With format (1) and using weak quotes, it can easily be
shown that the '\' does not designate anything related
to the append command syntax in a strict sense.

Forcing the literal use of a dollar character ($):
# seq 3|sed 's/./Line &/' > i; gsed -i.bak "/Line 2/a\${USER}New Line" i; cat i
Line 1
Line 2
${USER}New Line
Line 3

Removing the '\' enables the variable reference mechanism:
# seq 3|sed 's/./Line &/' > i; gsed -i.bak "/Line 2/a${USER}New Line" i; cat i
Line 1
Line 2
ericNew Line
Line 3

Gsed can enforce POSIX behavior; gsed(1) excels in being brief.
https://www.gnu.org/software/sed/manual/sed.html#Command_002dLine-Options-1
The html web page offers a lengthier explanation:
--posix

GNU sed includes several extensions to POSIX sed. In order to simplify
writing portable scripts, this option disables all the extensions that
this manual documents, including additional commands. Most of the
extensions accept sed programs that are outside the syntax mandated by
POSIX, but some of them (such as the behavior of the N command described
in Reporting Bugs) actually violate the standard. If you want to disable
only the latter kind of extension, you can set the POSIXLY_CORRECT
variable to a non-empty value.
QUOTE_END

The following GNU sed works as expected:
$ seq 3|sed 's/./Line &/' > i; gsed -i.bak "/Line 2/a New Line" i; cat i
Line 1
Line 2
New Line
Line 3

Here, --posix is enforcing posix behavior, as expected:
$ seq 3|sed 's/./Line &/' > i; gsed --posix -i.bak "/Line 2/a New Line" i; cat i
gsed: -e expression #1, char 11: expected \ after `a', `c' or `i'
Line 1
Line 2
Line 3

Of course FreeBSD sed is in agreement:
$ seq 3|sed 's/./Line &/' > i; sed -i.bak "/Line 2/a New Line" i; cat i
sed: 1: "/Line 2/a New Line
": command a expects \ followed by text
Line 1
Line 2
Line 3

gsed seems to be fooled easily, not as expected:
$ seq 3|sed 's/./Line &/' > i; gsed --posix -i.bak "/Line 2/a\New Line" i; cat i
Line 1
Line 2
New Line
Line 3

Also, not as expected:
$ seq 3|sed 's/./Line &/' > i; gsed --posix -i.bak "/Line 2/a\ New
Line" i; cat i
Line 1
Line 2
New Line
Line 3

A well-placed backslash defeats enforcement of POSIX behavior.


=== ex (vi) solution
As mentioned previously by Bob, ed(1) can be used too; however,
that makes use of a here document, spanning multiple lines. From
a portability standpoint this could work but, I imagine ed isn't
that widely available.

Sed, though likely more widely available, 'suffers' from multiple
implementations, that are not equally available on different OS-es.
Another contender for solving this dire problem is the venerable vi(1).
Although multiple variations of vi exist as well, I'm inclined to think
(haven't taken an in-depth look) that all support the following solution:

ex -sc ':3y | :3pu | :s/.*/blup/ | :w | :q' example.txt

When using the ex editor of textproc/vim on FreeBSD, where vi
is already installed as part of base, you'll need:
vim -es -c ':/2/y | :/2/pu | :s/.*/New Line/ | :w | :q' example.txt

Example run:
$ seq 3|sed 's/./Line &/' > i; ex -sc ':/2/y|:/2/pu|:s/.*/New
Line/|:w|:q' i; cat i
Line 1
Line 2
New Line
Line 3

In-place editing of sed is not part of POSIX; ex(1), however,
is defined in POSIX:
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ex.html

Generally, I just find sed properties more flexible and usable
then the above ex script. For example, using a non-successful
sed match gives:
$ seq 3|sed 's/./Line &/' > i; sed -E -i.bak
'/22/{H;g;s/(\n)(.*)/\2\1New Line/;}' i ; cat i
Line 1
Line 2
Line 3

With ex, one is thrown into an active edit session:
$ seq 3|sed 's/./Line &/' > i; ex -sc ':/22/y|:/22/pu|:s/.*/New
Line/|:w|:q' i; cat i
-c option, 1: Pattern not found
-c option, 1: Ex command failed: pending commands discarded
Press Enter to continue:


=== FreeBSD sed(1) man page
FreeBSD sed(1) states:
QUOTE
[1addr]a\
text Write text to standard output immediately before
each attempt
to read a line of input, whether by executing the
"N" function
or by beginning a new cycle.
QUOTE_END

There is a very subtle difference with, for example, the p command:
QUOTE
[2addr]p
Write the pattern space to standard output.
QUOTE_END

With the 'a' command the textual part start on the second line because
'text' is small enough to leave enough white space between it and the
first words 'Write text'. With the 'p' command, however, the textual
part starts on the second line because '[2addr]p' is too wide for the
textual part to start on the same line with as the first words 'Write the'.

https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.html#tag_20_109_13_03
POSIX, however, has a slightly different layout, that exemplifies the
mandatory new line more clearly:
QUOTE
[1addr]a\
text
Write text to standard output as described previously.
QUOTE_END

Similar layout is being used as shown in my hardcopy of "sed & awk"
first edition by Dale Dougherty.
QUOTE
a [address]a\
text

Append text following each line matched by address. [...]
QUOTE_END

I suggest a small layout change for the textual part of the
description of the 'a' command,
insert an extra line:

[1addr]a\
text
Write text to standard output immediately before
each attempt
to read a line of input, whether by executing the
"N" function
or by beginning a new cycle.


The same change applies to the 'c' and 'i' command.


Eric

Bob Proulx

unread,
Sep 25, 2025, 3:04:50 PMSep 25
to ques...@freebsd.org
Dag-Erling Smørgrav wrote:
> Bob Proulx writes:
> > and implements this ksh-ism so strictly speaking that is not portable
> > but it does work.
>
> Incorrect, dollar-quoted strings are part of POSIX.
>
> > This is what POSIX has to say about it.
> >
> > The '$' character is used to introduce parameter expansion, command
> > substitution, or arithmetic evaluation. If an unquoted '$' is followed
> > by a character that is not one of the following:
>
> You are looking in the wrong place. See XCU 2.2.4:
>
> https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_02_04
>
> > The result of $'...' such as $'\n' is strictly speaking "unspecified"
> > behavior by the standard.
>
> On the contrary, it is perfectly well-defined.

Ah! I always thought that was in the extensions. I wonder if it was
at one time and then it was promoted?

In any case it is good to know that it can be counted upon now to
always be a supported feature of a standard shell.

Thanks for the correction!

Bob

Frank Leonhardt

unread,
Oct 4, 2025, 8:11:37 AMOct 4
to ques...@freebsd.org
I second Bob! You've linked to a very useful page there. I note it's
2024, so I'd be a bit careful of using all of it but at least I can
point to it and say "It's POSIX so it's not my fault" :-) It says
${#parameter} is definitely POSIX, which is another thing I've avoided
until now.

Regards, Frank.



Dag-Erling Smørgrav

unread,
Oct 4, 2025, 4:34:12 PMOct 4
to Frank Leonhardt, ques...@freebsd.org
Frank Leonhardt <freeb...@fjl.co.uk> writes:
> Bob Proulx <b...@proulx.com> writes:
> > Dag-Erling Smørgrav <d...@FreeBSD.org> writes:
> > > Incorrect, dollar-quoted strings are part of POSIX.
> > Ah! I always thought that was in the extensions. I wonder if it was
> > at one time and then it was promoted? [...]
> I second Bob! You've linked to a very useful page there. I note it's
> 2024, so I'd be a bit careful of using all of it but at least I can
> point to it and say "It's POSIX so it's not my fault" :-) It says
> ${#parameter} is definitely POSIX, which is another thing I've avoided
> until now.

Parameter length existed in Issue 6 (2001), and probably much earlier,
but I didn't bother going further back in time. Dollar-single-quotes
were added in Issue 8 (2024) but the decision to do so was made around
2010 or 2011 (https://www.austingroupbugs.net/view.php?id=249) and
support for them was added to FreeBSD in 2011 (svn r221513 / git
a62ab0274a1a). They are, perhaps unsurprisingly, a Kornism.

Frank Leonhardt

unread,
Oct 5, 2025, 6:59:07 AMOct 5
to ques...@freebsd.org
That would figure. I'm still running 8.2 on some systems and $'' doesn't work there.
--------------------
# echo $'One\ntwo'
$One\ntwo
--------------------
Not actually what I was expecting! I'll stick with...

echo 'One
two'

...for a few years yet. Anything that wasn't in SVR2 is a non-standard extension to me! A surprising number of extensions to the Bourne Shell were already present by then, from what I remember; and on FreeBSD we're basically running that the Almquist shell which is a reimplementation of the System V shell if I understand it correctly?!?

Regards, Frank.


Reply all
Reply to author
Forward
0 new messages