0

I have a file like

public class Constants {
    public static final String PACKAGE_VERSION = '1.24.0.1';
}

I am executing the script

sed -E "/PACKAGE_VERSION/s/\'([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\'/\'1.24.0.2\'/g" force-app/main/default/classes/Constants.cls > temp.cls
mv temp.cls force-app/main/default/classes/Constants.cls

to replace the string '1.24.0.1' with '1.24.0.2'

Is this possible to do this with one line? Should I use awk instead of sed to inline edit the file?

When I try to execute a command

sed -iE "/PACKAGE_VERSION/s/\'([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\'/\'1.24.0.3\'/g" force-app/main/default/classes/Constants.cls 

file Constants.clsE is created instead of inline modification to the current file and the new file doesn't have substitution.

When I try to execute a command

sed -Ei "/PACKAGE_VERSION/s/\'([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\'/\'1.24.0.3\'/g" force-app/main/default/classes/Constants.cls 

I receive an error

sed: 1: "force-app/main/default/ ...": invalid command code f

I want the script to work both in MacOS and Linux without changing the script.

I tried to use awk, but I don't understand how can I make it inline editing the file?

When I try to execute the following

awk -v v="$version" '/PACKAGE_VERSION/{gsub(/[0-9]+(.[0-9]+){3}/,v)}1' force-app/main/default/classes/Constants.cls > force-app/main/default/classes/Constants.cls

this makes my file blank

edmz
  • 103
Patlatus
  • 105
  • I am not sure, I want this to work both on my machine (MacOS) and CI server (Linux). – Patlatus May 31 '22 at 15:49
  • @steeldriver, note that it's not BSD sed, its FreeBSD sed (as found on macos as well). Other BSDs (NetBSD and OpenBSD at least), eventually went the GNU way in this instance when they added a -i option to their sed years after FreeBSD. – Stéphane Chazelas May 31 '22 at 15:59

4 Answers4

4

In some seds (at least FreeBSD and the one you get on Macs), the -i option takes a mandatory argument that sets a suffix to use for backup files. You'd write -i.suffix, or -i .suffix in the shell, or -i "" to give no suffix. In those, either -iE or -i -E would take E or -E as the argument to -i and not an option.

In some other seds (at least GNU and Busybox), the -i option takes an optional argument for the suffix, such that anything after it in the same command line argument string is the suffix. It's usually presented as -i[suffix] in documentation. You'd write -i.suffix in the shell, or just -i to give no suffix. In those -iE would still make E the suffix, but -i -E would work as two options.

In both, -iE means to use the suffix E, and in the FreeBSD/Mac one, -Ei ... takes the following argument as the suffix (the one where you put your sed program in the question).

Those two ways are incompatible if you want to give the empty suffix for no backup file. In that case you'll have to know the difference and use -i "" -E with the FreeBSD/Mac one, or -i -E with the GNU one.

Or, if you want a backup file or don't care, just use -i.bak -E in either. (you can always just delete the backup file later.)

See also How can I achieve portability with sed -i (in-place editing)?

ilkkachu
  • 138,973
4

Different implementations of sed copied the -i option of perl for in-place editing, but unfortunately with a different API between themselves.

Some also added a -E or -r for support to the (superior) extended regexps (POSIX ones or variants thereof), some -R or -P for the even (immensely) superior perl-like regexps (PCRE or similar), but again (apart for -E that will make it to the next version of the POSIX standard and is found on more and more implementations these days), nothing universal.

Here, you might as well use the real thing and avoid all those portability problems:

perl -i -pe "s{\bPACKAGE_VERSION\s*=\s*\K'\d+(\.\d+){3}'}{'1.24.0.3'}g" file
1

-iE means to backup the file using the extension "E". You have to use:

sed -i.old -E ...

to backup file filename to filename.old.


After reading comments and the update with your environment (macOS), I add a couple of links where you can find useful information about sed usage with -i:


Also if you want to replace the version, probably it's more readable with awk:

awk -v v='1.24.0.2' '
  /PACKAGE_VERSION/{gsub(/[0-9]+(\.[0-9]+){3}/,v)}1
' force-app/main/default/classes/Constants.cls > file.tmp &&
  mv file.tmp force-app/main/default/classes/Constants.cls

Note: You cannot use the input file as the destination as it would be truncated, that's what you mention at the end of your post. A temporary file is used for help.

In general, I prefer using a temporary file, when using awk or sed rather than their in-place option (for GNU awk). The above command is expected to work for both your environments.

thanasisp
  • 8,122
  • can I make sed edit inline this file without making backup or suffix but using extended regex so far the substitution takes place? – Patlatus May 31 '22 at 15:50
  • In FreeBSD / macos, to not backup, you need sed -Ei '' 'sed-code' file.... While it's sed -Ei 'sed-code' file... with GNU/busybox/netbsd/openbsd sed. You can't conciliate them, but you could use perl -pi -e ... where those seds borrowed -i from. – Stéphane Chazelas May 31 '22 at 15:51
  • The point is that for GNU-like sed, the -i option takes an optional argument (which has to be affixed to the option) while for FreeBSD / macOS, the argument is required and may or may not be affixed (except you can't affix an empty string) – Stéphane Chazelas May 31 '22 at 15:53
1

You can do like this as follows. For macOS the variable is set to nonempty value & for others an empty value. Then the shell expansion will expand to a quoted empty value in case of macOS and nothing otherwise (Linux machines)

os=$(case $(uname) in Darwin*)  echo x;; esac)
sed -Ei ${os:+""} -e "..." file

guest_7
  • 5,728
  • 1
  • 7
  • 13