Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Mar 2021 14:43:34 -0700
From:      Bob Proulx <bob@proulx.com>
To:        freebsd-questions@freebsd.org
Subject:   Re: sed -i empty argument compatibility issue
Message-ID:  <20210305142352297368817@bob.proulx.com>
In-Reply-To: <9178f6c5-631a-c2c2-c6b1-8def94a3397b@dreamchaser.org>
References:  <9178f6c5-631a-c2c2-c6b1-8def94a3397b@dreamchaser.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Gary Aitken wrote:
> I'm trying to come up with a fix for a script in a port which invokes sed.
> The port comes from a linux environment, and the offending line looks like this:
> (This is in a cMake file.)
> 
> COMMAND sed -i "/^# /d" "${outfile}"

Unfortunately that is a GNU sed extension and use of it is
intrinsically not portable usage.  And the FreeBSD use of -i is a
similar but slightly incompatible usage.  The -i option is not
specified by POSIX and so any use of it is non-standard by
definition.

> The issue is that linux sed expects the -I or -i extension modifier to
> immediately follow the -i.  In the above line, the extension is deliberately
> missing to provide in-place editing.
> 
> fbsd expects the extension to be separated from the -i by whitespace, or
> doesn't work properly when it is empty or immediately follows the -i:

The pedantic person that I am disagrees with this detail.  FreeBSD sed
is okay with an empty extension.

    "If a zero-length extension is given, no backup will be saved."

A zero length extension would be this following.

    sed -i "" "/^# /d" "${outfile}"

But that isn't what is happening here.  Instead it is getting confused
by seeing the "/^# /d" as the extension.  And then it will be taking
the "${outfile}" part and trying to use it as the sed edit command.
What happens depends upon the file name.  It's data dependent and
depends upon the incoming data.  Which can't end well.

> So fbsd works with '-i ""' but linux requires '-i""'

Right.  It is a basic incompatibility.  It's non-portable use of sed
in the case you show.

> Does anyone know a work-around for this problem?

In order to port this then _something_ must be changed.  As to what
must be changed that is up to you and judegement and the larger view
of everything in your environment.  But off the top of my head these
different possibilities come to mind.

1. Edit the script and change this to that.  This makes it run on
FreeBSD sed.  Of course then this does not work the other direction.

    -COMMAND sed -i "/^# /d" "${outfile}"
    +COMMAND sed -i "" "/^# /d" "${outfile}"

2. Use perl.  Change that edit command to be this.  This should work
the same everywhere that has perl.  It does create a dependency upon
perl that might not have been present before.

    perl -i -lpe 'next if /^# /' "${outfile}"

3. Avoid -i and use a temporary file, just like in the old days.
However this introduces the need for temporary file handling.  But
maybe that isn't important here.

    sed "/^# /d" "${outfile}" > "$tmpfile" && mv "$tmpfile" "${outfile}"

4. Introduce a "sed" wrapper in PATH that intercepts the call to the
real sed, detects this problematic usage case, and then DTRT does the
right thing with it.  This wrapper could have all of the correct
temporary file handling needed to make this work.  More involved to do
this but if I were faced with a large unknown code base and expected
to see a lot of this then I would consider it a good global workaround
for portability.

5. Hack a local FreeBSD sed version to detect this case and DTRT with
the option.

6. Compile GNU sed locally and install it in PATH earlier.  This would
probably be a generally good option globally.  However it's possible
you might run into portability problems in the reverse case.

Good luck!  I'll be interested in hearing how things work out for you.

Bob



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20210305142352297368817>