1

On Linux, I have a Bash script in which I am using sed. In this script, I am using substitutions with "sed -i".

Unfortunately, on MacOS, this version of sed is not the same as Linux sed.

I am looking for a way to use a compatible sed on Linux and MacOS, i.e having the same script for both OS.

Could you tell me if using gsed for both OS instead of sed allows to have a unique compatible version for this script (where gsed -i works on both)

Update 1

In my case, on MacOS 10.9.5, I would like to replace the line 2 of a file by the value of a variable. I tried :

a=2
sed -i'' -e "2c\$a" file.dat

but the line is replaced by "$a" and not the value of a=2.

What can I try next?

PS: I would like to get a Linux/MacOS portable command line.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • 1
    Could you just not use the `-i` flag and do a `mv -f your_sed_output your_original_file` after your sed? then you don't have to monkey around with installs and prayers. – JNevill Feb 09 '18 at 19:02
  • 1
    You can use `sed -i.bak '....'` and it will work same on both Lunux and MacOS – anubhava Feb 09 '18 at 19:11
  • `gsed` on BSD (Darwin, MacOS) or Solaris for that matter would stand for GNU sed, which is the same as `sed` shipped with most Linux distributions. Unfortunately, I do not think that (all) Linux distributions would also have the g prefixed name (mine doesn't) for it (btw. same story `awk`, `tar` and others). – Ondrej K. Feb 09 '18 at 19:12
  • 1
    `Perl` can do everything `sed` can do, and far more besides, and is more orthogonal and consistent between platforms, including Solaris too. – Mark Setchell Feb 09 '18 at 19:27
  • I did an **update 1** of my question, if anyone could see what's wrong ... thanks –  Feb 11 '18 at 09:26
  • Your "update" is a new/different question. – melpomene Feb 11 '18 at 09:32
  • let say that I precise in this update the substitution command that I speak about into my original post. –  Feb 11 '18 at 09:35
  • 1
    How about `sed -e "2s|.*|$a|" file.dat` – Mark Setchell Feb 11 '18 at 09:51
  • -@MarkSetchell great ! that seems to work on both Linux/MacOS : could you tell me please what the different pipe symbols do ? –  Feb 11 '18 at 09:55
  • The separator after `s` can be any punctuation character; `/` is commonly used as the canonical separator but you can choose any separator you like. – tripleee Feb 11 '18 at 09:58

1 Answers1

2

There is unfortunately a large number of behaviors in sed which are not adequately standardized. It is hard to articulate a general answer which collects all of these.

For your specific question, a simple function wrapper like this should suffice.

sedi () {
    case $(uname -s) in
        *[Dd]arwin* | *BSD* ) sed -i '' "$@";;
        *) sed -i "$@";;
    esac
}

As you discovered, details of the c command are also poorly standardized. I would simply suggest

sedi "2s/.*/$a/" file.dat

where obviously use a different separator if $a could contain a slash; or if you are using Bash, use ${a//[\/]/\\/} to backslash all slashes in the value.

Other behaviors which differ between implementations include extended regex support (-r or -E or not available), the ability to pass in multiple script fragments with -e (the portable solution is to separate commands with newlines), the ability to pass standard input as the argument to -f, and the general syntax of several commands (does the filename argument of the r and w commands extend up to the next whitespace, semicolon, or newline? Can you put a command immediately adjacent to a brace? What are the precise semantics of backslashed newlines and where are they mandatory? Where are backslashes before command arguments mandatory?) and obviously regex flags and features.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • 1
    See also https://stackoverflow.com/questions/24275070/sed-not-giving-me-correct-substitute-operation-for-newline-with-mac-difference/ which has an excellent answer with much more details. – tripleee Feb 12 '18 at 09:04