I have a shell script that I currently use for some build related stuff for a mobile application.

Due to the the subtle differences between BSD & GNU one of build scripts originally written on a Mac (BSD)


if [[ -z $environment ]]; then 
if ! [[ $environment =~ (live|beta) ]]; then
  echo "Invalid environment: $environment"
  exit 1


cp app/index.html.mob MobileApp/www/index.html

sed -i'' "s#MOBILE_APP_API_URL#\"$mobile_app_api_url\"#g" MobileApp/www/index.html

The sed command has been written on BSD (Mac) but as builds may take place on both Mac or Ubuntu (GNU) I need to modify this to work with on both flavours, what is the best approach for this?

  • 3
    Use ed, it can edit the files in-place and it's fully portable. Mar 3 '17 at 14:12
  • 1
    Use perl -pi -e '...'. Both BSD and GNU sed derived their -i from perl's one. perl will be available on most GNU and BSD systems and most other systems. Mar 3 '17 at 15:09
  • -i'' is actually -i (-i concatenated with the empty string) so would only work with GNU sed. You'd need -i '' for BSDs. Mar 3 '17 at 15:10
  • @StéphaneChazelas There are Unices out there without Perl in the base system. NetBSD is one (but it may well be alone in that group).
    – Kusalananda
    Mar 3 '17 at 15:21
  • 1
    @Kusalananda, true. In my experience, one is more likely to find perl than bash though. For instance here, many of the BSDs would not have bash or any shell that supports those [[...]] or =~ (which can easily be replaced by a standard and more legible sh case construct). Mar 3 '17 at 15:43

Do this to circumvent the problematic portability issues with the -i flag of sed:

sed 'sed-editing-commands' thefile >tmpfile && mv tmpfile thefile

I.e., write to a temporary file, and then replace the input file with the temporary file if the sed command didn't fail.

This is portable to all implementations of sed that I know of.

To create a temporary filename safely, use mktemp. Although this isn't a standard utility, it is available on all Unices that I have access to (OpenBSD, NetBSD, Solaris, macOS, Linux):

sed 'sed-editing-commands' thefile >"$tmpfile" && mv "$tmpfile" thefile
  • However, that doesn't make any attempt to preserve the ownership, permissions and other metadata of the original file like GNU or BSD seds would. Mar 3 '17 at 15:17
  • @StéphaneChazelas Good point. To do that, one would have to cp thefile "$tmpfile" first, I think. Does that sound about right?
    – Kusalananda
    Mar 3 '17 at 15:19
  • Some good related reading in Scott's answer here: unix.stackexchange.com/a/186126/117549
    – Jeff Schaller
    Mar 3 '17 at 15:28

You need to realize that the $mobile_app_api_url should not contain the chars that are meaningful to sed on the RHS of s///. Specifically, speaking, you need to make sure that this variable has the following properly escaped away: viz., & # \ newline before they are plugged in.

  • It looks as if that is already taken care of. The URL will not cause problems. The issue is with the non-portable -i flag for sed (it work very differently with different implementations of sed).
    – Kusalananda
    Mar 3 '17 at 14:44
  • From the OP's code shown there is nowhere any escaping the characters that I have mentioned above in my post. Just try placing an & in $1 to see what I mean.
    – user218374
    Mar 3 '17 at 14:50
  • 1
    The URL in $mobile_app_api_url will be either https://apibeta.mysite.com or https://apilive.mysite.com, no other values will be allowed. The escaping of characters in the substitution is furthermore an issue in all implementations of sed on all Unices. This question is about portability.
    – Kusalananda
    Mar 3 '17 at 14:55
  • 1
    @Kusalananda, any value of $environment that contains either live or beta would be allowed (though it doesn't look like it was the intention). Mar 3 '17 at 15:53
  • @StéphaneChazelas Right you are. Any URL with & # \ newline in the domain part would be a bit broken anyway though.
    – Kusalananda
    Mar 3 '17 at 15:56

Instead of:

cp app/index.html.mob MobileApp/www/index.html
sed -i'' "s#MOBILE_APP_API_URL#\"$mobile_app_api_url\"#g" MobileApp/www/index.html

Just do:

sed "s#MOBILE_APP_API_URL#\"$mobile_app_api_url\"#g" \
  < app/index.html.mob >  MobileApp/www/index.html

You can also remove your dependency on ksh93/bash by changing the top part to:


case $environment in
  "") environment=beta;;
  live|beta) ;;
    printf >&2 'Invalid environment: "%s"\n' "$environment"
    exit 1;;

See also environment=${1:-beta}.

BTW, [[ $environment =~ (live|beta) ]] in ksh93 and bash tests whether $environment contains live or beta which doesn't sound like what you want. You'd need [[ $environment =~ ^(live|beta)$ ]] to test whether it is live or beta.

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.