sed command in Jenkins script - bash

I have a multiline sed command that works fine in a script that I run locally, but in a Jenkins build script gives me an error.
This is the command:
sed \
-i -r -e "s/(project\(.* VERSION\s+)[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}\s*\)/\1$RELEASE_MAJOR\.$RELEASE_MINOR\.$RELEASE_MICRO \)/" \
-i -r -e "s/(set\(.* APPMANAGER_MAJOR_VERSION\s+)[0-9]{1,2}\s*\)/\1$RELEASE_MAJOR \)/" \
-i -r -e "s/(set\(.* APPMANAGER_MINOR_VERSION\s+)[0-9]{1,2}\s*\)/\1$RELEASE_MINOR \)/" \
-i -r -e "s/(set\(.* APPMANAGER_MICRO_VERSION\s+)[0-9]{1,2}\s*\)/\1$RELEASE_MICRO \)/" \
${CMAKE_FILE}
I keep getting this error message:
+ sed -i -r -e 's/(project\(.* VERSION\s+)[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}\s*\)/\114\.0\.0 \)/' -i -r -e 's/(set\(.* APPMANAGER_MAJOR_VERSION\s+)[0-9]{1,2}\s*\)/\114 \)/' -i -r -e 's/(set\(.* APPMANAGER_MINOR_VERSION\s+)[0-9]{1,2}\s*\)/\10 \)/' -i -r -e 's/(set\(.* APPMANAGER_MICRO_VERSION\s+)[0-9]{1,2}\s*\)/\10 \)/' /apps/artefacts/jenkins_workspace/AI/Release/RDK-AI-Branch_OFF/asappsserviced/asappsserviced/appinfrastructure/RDK/AppManager/CMakeLists.txt
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
-n, --quiet, --silent
suppress automatic printing of pattern space
-e script, --expression=script
add the script to the commands to be executed
-f script-file, --file=script-file
add the contents of script-file to the commands to be executed
--follow-symlinks
follow symlinks when processing in place
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
-c, --copy
use copy instead of rename when shuffling files in -i mode
-b, --binary
does nothing; for compatibility with WIN32/CYGWIN/MSDOS/EMX (
open files in binary mode (CR+LFs are not treated specially))
-l N, --line-length=N
specify the desired line-wrap length for the `l' command
--posix
disable all GNU extensions.
-r, --regexp-extended
use extended regular expressions in the script.
-s, --separate
consider files as separate rather than as a single continuous
long stream.
-u, --unbuffered
load minimal amounts of data from the input files and flush
the output buffers more often
-z, --null-data
separate lines by NUL characters
--help
display this help and exit
--version
output version information and exit
If no -e, --expression, -f, or --file option is given, then the first
non-option argument is taken as the sed script to interpret. All
remaining arguments are names of input files; if no input files are
specified, then the standard input is read.
My local bash version is GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu) and sed is version 4.4.
On Jenkins the version of bash is GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu) and sed is version 4.2.2.
The variable CMAKE_FILE contains a path to a file containing text like this:
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required( VERSION 3.10.0 )
# Project setup
project( MyApp LANGUAGES C CXX VERSION 14.0.0 )
# Set the major and minor version numbers (also used by plugins)
set( APPMANAGER_MAJOR_VERSION 14 )
set( APPMANAGER_MINOR_VERSION 0 )
set( APPMANAGER_MICRO_VERSION 0 )
I am using sed to update the version numbers.
Any idea how to fix this?

I finally worked it out! Since I had an old version of sed that didn't support extended regexes, I had to do the following to use basic regular expressions (BRE):
escape groups, i.e. the open ( and close ) brackets
un-escape literal brackets
escape + for special meaning
escape the parentheses, i.e. { and }
I found this reference useful but it didn't mention anything about groups. Apparently in the BRE format, brackets for groups have to be escaped while literal brackets do not.
So this is what worked for me:
sed \
-i -e "s/\(project.*VERSION\)\s\+[0-9]\{1,2\}\.[0-9]\{1,2\}\.[0-9]\{1,2\}\s\+)/\1 $major\.$minor\.$micro\ )/" \
-e "s/\(set.*APPMANAGER_MAJOR_VERSION\)\s\+[0-9]\{1,2\}\s*)/\1 $major )/" \
-e "s/\(set.*APPMANAGER_MINOR_VERSION\)\s\+[0-9]\{1,2\}\s*)/\1 $minor )/" \
-e "s/\(set.*APPMANAGER_MICRO_VERSION\)\s\+[0-9]\{1,2\}\s*)/\1 $micro )/" ${FILE}

Related

MacOS Bash Script using sed and grep doesn't work because of grep: repetition-operator operand invalid error

I tried to update this line in pubspec.yaml on macOS
version: 0.2.6-alpha+26
#!/bin/bash
version=$(grep -oE '(?\<=version: )\[^ \]+' pubspec.yaml)
version=$(echo $version | sed 's/(\[0-9\].\[0-9\].\[0-9\])-alpha+(\[0-9\])/\\1-alpha+\\1/')
sed -i '' "s/version:.\*/version: $version/" pubspec.yaml
This script is intended to increment the patch number of the version number in the file pubspec.yaml. However, the script contains errors that cause it to throw the following error message:
grep: repetition-operator operand invalid
sed: 1: "s/([0-9].[0-9].[0-9])-a ...": \1 not defined in the RE
I'd like it to simply work
It looks like your grep statement derives from this bump_version.sh.
But the macOS grep does not recognize the GNU-grep -P option.
(Aside: The ?<= syntax comes from Perl regular expressions. See "Lookbehind assertions" here.)
But you can drop the regex gymnastics by translating dots & dashes into spaces, which are easily digested by the Bash "read" command:
#!/usr/bin/env bash
grep version pubspec.yaml | (
IFS=' .-' read label major minor patch build;
sed -i '' "s/version:.*/version: $major.$minor.$((patch+1))-$build/" pubspec.yaml
)
Notes:
This script favors piped output over variables & redirection.
I didn't know that IFS can take multiple characters & applied C.Duffy's tip (to omit tr)!
Reusing variables through Process Substitution is left as an exercise for the reader.

How to treat files as huge string with macos grep?

I'm using the following command on Ubuntu to list all files containing a given pattern:
for f in *; do if grep -zoPq "foo\nbar" $f; then echo $f; fi; done
But on macos, I'm geting the following error:
grep: invalid option -- z
There's no -z option to treat files as a big string with macos grep, unlike gnu grep.
Is there another option on macos grep equivalent to `-z ? If not, what alternative can I use to get the same result ?
-P (PERL regex) is only supported in gnu grep but not on BSD grep found on Mac OS.
You can either use home brew to install gnu grep or else use this equivalent awk command:
awk 'p ~ /foo$/ && /^bar/ {print FILENAME; nextfile}; {p=$0}' *
Please note that this eliminates the need to use shell for loop.
You can install pcregrep via home brew, and then use it with the -M option:
By default, each line that matches a pattern is copied to the
standard output, and if there is more than one file, the file name is
output at the start of each line, followed by a colon. However, there
are options that can change how pcregrep behaves. In particular, the
-M option makes it possible to search for patterns that span line
boundaries. What defines a line boundary is controlled by the -N
(--newline) option.
With ripgrep
rg -lU 'foo\nbar'
This will list all filenames containing foo\nbar in the current directory. -U option allows to match multiple lines. Unlike grep -z, whole file isn't read in one-shot, so this is safe to use even for larger input files.
ripgrep recursively searches by default. Use rg -lU --max-depth 1 'foo\nbar' if you don't want to search sub-directories.
However, note that by default, rigprep ignores
files and directories that match rules specified by ignore files like .gitignore
hidden files and directories
binary files
You can change that by using:
-u or --no-ignore
-uu or --no-ignore --hidden
-uuu or --no-ignore --hidden --binary
It seems you are searching for files which have the sequence foo\nbar. With GNU awk (brew install gawk), you can set the record separatorRS to this sequence and check if the record matches:
gawk 'BEGIN{RS="foo\nbar"}{exit (RT!=RS)}' file
This will try to split your files in records which are separated by the record separator RS, if so, it will terminate with exit code 0, otherwise with exit code 1. The behaviour is the same as the proposed grep
If you just want the files listed, you can do:
gawk 'BEGIN{RS="foo\nbar"}(RT==RS){print FILENAME}{nextfile}' *

How do I replace a string that doesn't contain another string with `sed`?

I'm trying to replace paths in a directory.
So far, I've tried using this on every single file in the directory, however, I'm getting a syntax error:
sed -i \"/'#{CREW_PREFIX}'/\\\!s,/usr,#{CREW_PREFIX},g\" \${file};
Here's the syntax error:
sed: -e expression #1, char 3: unknown command: `u'
Example input:
prop.ppd_search_path = '/usr/share;/usr/local/share;/usr/lib;/usr/local/lib;/usr/libexec;/opt;/usr/lib64'
Example output:
prop.ppd_search_path = '/usr/local/share;/usr/local/share;/usr/local/lib;/usr/local/lib;/usr/local/libexec;/opt;/usr/local/lib64'
What am I doing wrong?
The line needs to match /usr, but it needs to not match /usr/local, otherwise the outputted path will be something like /usr/local/local.
On top of that, the replacement needs to be read from a Ruby variable,
so it could be /usr/local (by default), or some totally different
path like /foo/bar.
Use case:
There are thousands of hardcoded paths in a directory (HP Linux Printing System source code) that point to a read only path (/usr, in the Chrome OS case). I'm trying to replace all of the instances of /usr with the user-defined prefix (by default, /usr/local), while ignoring the line if it contains the user-defined prefix. The user-defined prefix is in a Ruby script, and it has to be contained in a Ruby variable (#{CREW_PREFIX}). This is what I'm trying to do:
def self.patch
# Fix hardcoded file directories
system "for file in \$(find . -type f); do
sed -i 's,/etc,#{CREW_PREFIX}&,g' \${file};
sed -i 's,/usr/share,#{CREW_PREFIX}/share,g' \${file};
sed -i 's,/usr/include,#{CREW_PREFIX}/include,g' \${file};
sed -i 's,/usr/lib,#{CREW_LIB_PREFIX},g' \${file};
sed -i 's,/var,#{CREW_PREFIX}&,g' \${file};
sed -i 's,/usr/bin,#{CREW_PREFIX}/bin,g' \${file};
sed -i 's,#{CREW_PREFIX}/bin/env,/usr/bin/env,g' \${file};
sed -i 's,#{CREW_PREFIX}/etc/os-release,/etc/os-release,g' \${file};
sed -i \"/'#{CREW_PREFIX}'/\\\!s,/usr,#{CREW_PREFIX},g\" \${file};
done"
end
However, the path contains slashes, and that makes sed give an unknown command error:
sed: -e expression #1, char 3: unknown command: `u'
And I can't find another delimiter that contains the functionality that / does.
This is what I'm dealing with:
chronos#localhost ~/Downloads/tarballs/hplip-3.19.1 $ grep -R /usr ./*/ > file; du -sh file
40K file
I need all those instances of /usr to become the value #{CREW_PREFIX} (variable) if the line where the instance of /usr occurs does not also match the value of #{CREW_PREFIX}.
$ cat file
./configure --prefix=/usr --enable-foo --disable-bar
./configure --prefix=/usr/local --enable-foo --disable-bar
$ var='/foo/bar'; sed 's:/usr/local:\n:g; s:/usr:'"$var"':; s:\n:/usr/local:g' file
./configure --prefix=/foo/bar --enable-foo --disable-bar
./configure --prefix=/usr/local --enable-foo --disable-bar

Using sed to mass rename files

Objective
Change these filenames:
F00001-0708-RG-biasliuyda
F00001-0708-CS-akgdlaul
F00001-0708-VF-hioulgigl
to these filenames:
F0001-0708-RG-biasliuyda
F0001-0708-CS-akgdlaul
F0001-0708-VF-hioulgigl
Shell Code
To test:
ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'
To perform:
ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh
My Question
I don't understand the sed code. I understand what the substitution
command
$ sed 's/something/mv'
means. And I understand regular expressions somewhat. But I don't
understand what's happening here:
\(.\).\(.*\)
or here:
& \1\2/
The former, to me, just looks like it means: "a single character,
followed by a single character, followed by any length sequence of a
single character"--but surely there's more to it than that. As far as
the latter part:
& \1\2/
I have no idea.
First, I should say that the easiest way to do this is to use the
prename or rename commands.
On Ubuntu, OSX (Homebrew package rename, MacPorts package p5-file-rename), or other systems with perl rename (prename):
rename s/0000/000/ F0000*
or on systems with rename from util-linux-ng, such as RHEL:
rename 0000 000 F0000*
That's a lot more understandable than the equivalent sed command.
But as for understanding the sed command, the sed manpage is helpful. If
you run man sed and search for & (using the / command to search),
you'll find it's a special character in s/foo/bar/ replacements.
s/regexp/replacement/
Attempt to match regexp against the pattern space. If success‐
ful, replace that portion matched with replacement. The
replacement may contain the special character & to refer to that
portion of the pattern space which matched, and the special
escapes \1 through \9 to refer to the corresponding matching
sub-expressions in the regexp.
Therefore, \(.\) matches the first character, which can be referenced by \1.
Then . matches the next character, which is always 0.
Then \(.*\) matches the rest of the filename, which can be referenced by \2.
The replacement string puts it all together using & (the original
filename) and \1\2 which is every part of the filename except the 2nd
character, which was a 0.
This is a pretty cryptic way to do this, IMHO. If for
some reason the rename command was not available and you wanted to use
sed to do the rename (or perhaps you were doing something too complex
for rename?), being more explicit in your regex would make it much
more readable. Perhaps something like:
ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh
Being able to see what's actually changing in the
s/search/replacement/ makes it much more readable. Also it won't keep
sucking characters out of your filename if you accidentally run it
twice or something.
you've had your sed explanation, now you can use just the shell, no need external commands
for file in F0000*
do
echo mv "$file" "${file/#F0000/F000}"
# ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done
I wrote a small post with examples on batch renaming using sed couple of years ago:
http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/
For example:
for i in *; do
mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done
If the regex contains groups (e.g. \(subregex\) then you can use them in the replacement text as \1\,\2 etc.
The easiest way would be:
for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done
or, portably,
for i in F00001*; do mv "$i" "F0001${i#F00001}"; done
This replaces the F00001 prefix in the filenames with F0001.
credits to mahesh here: http://www.debian-administration.org/articles/150
The sed command
s/\(.\).\(.*\)/mv & \1\2/
means to replace:
\(.\).\(.*\)
with:
mv & \1\2
just like a regular sed command. However, the parentheses, & and \n markers change it a little.
The search string matches (and remembers as pattern 1) the single character at the start, followed by a single character, follwed by the rest of the string (remembered as pattern 2).
In the replacement string, you can refer to these matched patterns to use them as part of the replacement. You can also refer to the whole matched portion as &.
So what that sed command is doing is creating a mv command based on the original file (for the source) and character 1 and 3 onwards, effectively removing character 2 (for the destination). It will give you a series of lines along the following format:
mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef
and so on.
Using perl rename (a must have in the toolbox):
rename -n 's/0000/000/' F0000*
Remove -n switch when the output looks good to rename for real.
There are other tools with the same name which may or may not be able to do this, so be careful.
The rename command that is part of the util-linux package, won't.
If you run the following command (GNU)
$ rename
and you see perlexpr, then this seems to be the right tool.
If not, to make it the default (usually already the case) on Debian and derivative like Ubuntu :
$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename
For archlinux:
pacman -S perl-rename
For RedHat-family distros:
yum install prename
The 'prename' package is in the EPEL repository.
For Gentoo:
emerge dev-perl/rename
For *BSD:
pkg install gprename
or p5-File-Rename
For Mac users:
brew install rename
If you don't have this command with another distro, search your package manager to install it or do it manually:
cpan -i File::Rename
Old standalone version can be found here
man rename
This tool was originally written by Larry Wall, the Perl's dad.
The backslash-paren stuff means, "while matching the pattern, hold on to the stuff that matches in here." Later, on the replacement text side, you can get those remembered fragments back with "\1" (first parenthesized block), "\2" (second block), and so on.
If all you're really doing is removing the second character, regardless of what it is, you can do this:
s/.//2
but your command is building a mv command and piping it to the shell for execution.
This is no more readable than your version:
find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh
The fourth character is removed because find is prepending each filename with "./".
Here's what I would do:
for file in *.[Jj][Pp][Gg] ;do
echo mv -vi \"$file\" `jhead $file|
grep Date|
cut -b 16-|
sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done
Then if that looks ok, add | sh to the end. So:
for file in *.[Jj][Pp][Gg] ;do
echo mv -vi \"$file\" `jhead $file|
grep Date|
cut -b 16-|
sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done
The parentheses capture particular strings for use by the backslashed numbers.
ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash
Some examples that work for me:
$ tree -L 1 -F .
.
├── A.Show.2020.1400MB.txt
└── Some Show S01E01 the Loreming.txt
0 directories, 2 files
## remove "1400MB" (I: ignore case) ...
$ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/.[0-9]{1,}mb//I'`"; done;
renamed 'A.Show.2020.1400MB.txt' -> 'A.Show.2020.txt'
## change "S01E01 the" to "S01E01 The"
## \U& : change (here: regex-selected) text to uppercase;
## note also: no need here for `\1` in that regex expression
$ for f in *; do mv 2>/dev/null "$f" "`echo $f | sed -r "s/([0-9] [a-z])/\U&/"`"; done
$ tree -L 1 -F .
.
├── A.Show.2020.txt
└── Some Show S01E01 The Loreming.txt
0 directories, 2 files
$
2>/dev/null suppresses extraneous output (warnings ...)
reference [this thread]: https://stackoverflow.com/a/2372808/1904943
change case: https://www.networkworld.com/article/3529409/converting-between-uppercase-and-lowercase-on-the-linux-command-line.html

sed -i command for in-place editing to work with both GNU sed and BSD/OSX

I've got a makefile (developed for gmake on Linux) that I'm attempting to port to MacOS, but it seems like sed doesn't want to cooperate. What I do is use GCC to autogenerate dependency files, and then tweak them a bit using sed. The relevant portion of the makefile:
$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
$(CPPC) -MM -MD $< -o $#
sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $#
While this runs with no trouble under GNU/Linux, I get errors like the following when attempting to build on MacOS:
sed: 1: "test/obj/equipmentConta ...": undefined label 'est/obj/equipmentContainer_utest.d'
sed: 1: "test/obj/dice_utest.d": undefined label 'est/obj/dice_utest.d'
sed: 1: "test/obj/color-string_u ...": undefined label 'est/obj/color-string_utest.d'
It would seem like sed is chopping off a character, but I can't see the solution.
OS X sed handles the -i argument differently to the Linux version.
You can generate a command that might "work" for both by adding -e in this way:
# vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $#
OS X sed -i interprets the next thing after the -i as a file extension for a backup copy of the in-place edit. (The Linux version only does this if there is no space between the -i and the extension.) Obviously a side affect of using this is that you will get a backup file with -e as an extension, which you may not want. Please refer to other answers to this question for more details, and cleaner approaches that can be used instead.
The behaviour you see is because OS X sed consumes the s||| as the extension (!) then interprets the next argument as a command - in this case it begins with t, which sed recognizes as a branch-to-label command expecting the target label as an argument - hence the error you see.
If you create a file test you can reproduce the error:
$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'
Actually, doing
sed -i -e "s/blah/blah/" files
doesn't do what you expect in MacOS either. Instead it creates backup files with -e extension.
The proper command for MacOS is
sed -i "" -e "s/blah/blah/" files
On Linux, remove the space between -i and "" (see related answer)
sed -i"" -e "s/blah/blah/" files
The currently accepted answer is flawed in two very important ways.
With BSD sed (the OSX version), the -e option is interpreted as
a file extension and therefore creates a backup file with a -e
extension.
Testing for the darwin kernel as suggested is not a reliable
approach to a cross platform solution since GNU or BSD sed could
be present on any number of systems.
A much more reliable test would be to simply test for the --version option which is only found in the GNU version of sed.
sed --version >/dev/null 2>&1
Once the correct version of sed is determined, we can then execute the command in its proper syntax.
GNU sed syntax for -i option:
sed -i -- "$#"
BSD sed syntax for -i option:
sed -i "" "$#"
Finally put it all together in a cross platform function to execute an in place edit sed commend:
sedi () {
sed --version >/dev/null 2>&1 && sed -i -- "$#" || sed -i "" "$#"
}
Example usage:
sedi 's/old/new/g' 'some_file.txt'
This solution has been tested on OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise, & Msys.
martin clayton's helpful answer provides a good explanation of the problem[1], but his solution - as he states - has a potentially unwanted side effect.
Here are side-effect-free solutions:
Caveat: Solving the -i syntax problem alone, as below, may not be enough, because there are many other differences between GNU sed and BSD/macOS sed (for a comprehensive discussion, see this answer of mine).
Workaround with -i: Create a backup file temporarily, then clean it up:
With a non-empty suffix (backup-file filename extension) option-argument (a value that is not the empty string), you can use -i in a way that works with both BSD/macOS sed and GNU sed, by directly appending the suffix to the -i option.
This can be utilized to create a backup file temporarily that you can clean up right away:
sed -i.bak 's/foo/bar/' file && rm file.bak
Obviously, if you do want to keep the backup, simply omit the && rm file.bak part.
Workaround that is POSIX-compliant, using a temporary file and mv:
If only a single file is to be edited in-place, the -i option can be bypassed to avoid the incompatibility.
If you restrict your sed script and other options to POSIX-compliant features, the following is a fully portable solution (note that -i is not POSIX-compliant).
sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
This command simply writes the modifications to a temporary file and, if the sed command succeeds (&&), replaces the original file with the temporary one.
If you do want to keep the original file as a backup, add another mv command that renames the original first.
Caveat: Fundamentally, this is what -i does too, except that it tries to preserve permissions and extended attributes (macOS) of the original file; however, if the original file is a symlink, both this solution and -i will replace the symlink with a regular file.
See the bottom half of this answer of mine for details on how -i works.
[1] For a more in-depth explanation, see this answer of mine.
This isn't quite an answer to the question, but one can get linux-equivalent behavior through
brew install gnu-sed
# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
(previously there was a --with-default-names option to brew install gnu-sed but that has recently been removed)
I came across this issue as well and thought of the following solution:
darwin=false;
case "`uname`" in
Darwin*) darwin=true ;;
esac
if $darwin; then
sedi="/usr/bin/sed -i ''"
else
sedi="sed -i"
fi
$sedi 's/foo/bar/' /home/foobar/bar
Works for me ;-), YMMV
I work in a multi-OS team where ppl build on Windows, Linux and OS X. Some OS X users complained because they got another error - they had the GNU port of sed installed so I had to specify the full path.
I've corrected the solution posted by #thecarpy:
Here's a proper cross-platform solution for sed -i:
sedi() {
case $(uname) in
Darwin*) sedi=('-i' '') ;;
*) sedi='-i' ;;
esac
LC_ALL=C sed "${sedi[#]}" "$#"
}
I avoid using sed -i when writing scripts and i came up with simple solution:
printf '%s' "$(sed 's/foo/bar' file)" > file
much compatible and is POSIX-compliant. It is doing pretty much the same as sed -i, but this one does not create temp files, it directly redirect the changes to file.
As a noob idk what's the cons of doing this, the only matters is "It works"

Resources