I've successfully used the following sed
command to search/replace text in Linux:
sed -i 's/old_link/new_link/g' *
However, when I try it on my Mac OS X, I get:
"command c expects \ followed by text"
I thought my Mac runs a normal BASH shell. What's up?
EDIT:
According to @High Performance, this is due to Mac sed
being of a different (BSD) flavor, so my question would therefore be how do I replicate this command in BSD sed
?
EDIT:
Here is an actual example that causes this:
sed -i 's/hello/gbye/g' *
sed
sees a "c" in your data as a command. Are you using a variable? Please post something that more closely represents the actual command and some data that you're processing. You can get a simple demonstration of this error by doing echo x | sed c
.
If you use the -i
option you need to provide an extension for your backups.
If you have:
File1.txt
File2.cfg
The command (note the lack of space between -i
and ''
and the -e
to make it work on new versions of Mac and on GNU):
sed -i'.original' -e 's/old_link/new_link/g' *
Create 2 backup files like:
File1.txt.original
File2.cfg.original
There is no portable way to avoid making backup files because it is impossible to find a mix of sed commands that works on all cases:
sed -i -e ... - does not work on OS X as it creates -e backups
sed -i'' -e ... - does not work on OS X 10.6 but works on 10.9+
sed -i '' -e ... - not working on GNU
Note Given that there isn't a sed command working on all platforms, you can try to use another command to achieve the same result.
E.g., perl -i -pe's/old_link/new_link/g' *
I believe on OS X when you use -i an extension for the backup files is required. Try:
sed -i .bak 's/hello/gbye/g' *
Using GNU sed
the extension is optional.
This works with both GNU and BSD versions of sed:
sed -i'' -e 's/old_link/new_link/g' *
or with backup:
sed -i'.bak' -e 's/old_link/new_link/g' *
Note missing space after -i
option! (Necessary for GNU sed)
rm
command right after to delete it.
sed -i'' ...
=> sed -i '' ...
perl -i -pe
from the other answer gave that :)
Had the same problem in Mac and solved it with brew
:
brew install gnu-sed
and use as
gsed SED_COMMAND
you can set as well set sed
as alias to gsed
(if you want):
alias sed=gsed
~/.zshrc
or bashrc file: export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
And then run source ~/.zshrc
Change to your rc file of choice! Then run which sed
to check if it's been changed.
Or, you can install the GNU version of sed in your Mac, called gsed, and use it using the standard Linux syntax.
For that, install gsed
using ports (if you don't have it, get it at http://www.macports.org/) by running sudo port install gsed
. Then, you can run sed -i 's/old_link/new_link/g' *
gnu-sed
Your Mac does indeed run a BASH shell, but this is more a question of which implementation of sed you are dealing with. On a Mac sed comes from BSD and is subtly different from the sed you might find on a typical Linux box. I suggest you man sed
.
Sinetris' answer is right, but I use this with find
command to be more specific about what files I want to change. In general this should work (tested on osx /bin/bash
):
find . -name "*.smth" -exec sed -i '' 's/text1/text2/g' {} \;
In general when using sed
without find
in complex projects is less efficient.
-exec
is very nice! I'm just wondering if the slash in the end is actually need
\
, the ;
got interpreted by the shell when I tried such
Insead of calling sed with sed
, I do ./bin/sed
And this is the wrapper script in my ~/project/bin/sed
#!/bin/bash
if [[ "$OSTYPE" == "darwin"* ]]; then
exec "gsed" "$@"
else
exec "sed" "$@"
fi
Don't forget to chmod 755
the wrapper script.
brew install gnu-sed
beforehand on your Mac
I've created a function to handle sed
difference between MacOS (tested on MacOS 10.12) and other OS:
OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
if [ "$OS" = 'Darwin' ]; then
# for MacOS
sed -i '' -e "$1" "$2"
else
# for Linux and Windows
sed -i'' -e "$1" "$2"
fi
}
Usage:
$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")
Where:
,
is a delimeter
's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"','
is pattern
"./mysql/.env"
is path to file
As the other answers indicate, there is not a way to use sed portably across OS X and Linux without making backup files. So, I instead used this Ruby one-liner to do so:
ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml
In my case, I needed to call it from a rake
task (i.e., inside a Ruby script), so I used this additional level of quoting:
sh %q{ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml}
Here's how to apply environment variables to template file (no backup need).
1. Create template with {{FOO}} for later replace.
echo "Hello {{FOO}}" > foo.conf.tmpl
2. Replace {{FOO}} with FOO variable and output to new foo.conf file
FOO="world" && sed -e "s/{{FOO}}/$FOO/g" foo.conf.tmpl > foo.conf
Working both macOS 10.12.4 and Ubuntu 14.04.5
Here is an option in bash scripts:
#!/bin/bash
GO_OS=${GO_OS:-"linux"}
function detect_os {
# Detect the OS name
case "$(uname -s)" in
Darwin)
host_os=darwin
;;
Linux)
host_os=linux
;;
*)
echo "Unsupported host OS. Must be Linux or Mac OS X." >&2
exit 1
;;
esac
GO_OS="${host_os}"
}
detect_os
if [ "${GO_OS}" == "darwin" ]; then
sed -i '' -e ...
else
sed -i -e ...
fi
gsed
presence and use that as a varaible??
if type gsed &> /dev/null; then; echo "use gsed"; else; echo "use sed"; fi;
sed -ie 's/old_link/new_link/g' *
Works on both BSD & Linux with gnu sed
e
appended
Success story sharing
sed
can tell, you DID supply a backup suffix. The backup suffix iss/old_link/new_link/g
. The next argument after that is supposed to be the editing commands. Because it interpreted the commands as the backup name, it then took the first filename as the editing commands, but they weren't valid.sed -i '' -e ...
?sed -i'' -e
seems to not work as expected on mac 10.14sed -i -- ...
seems to work fine. Also mentioned @stackoverflow.com/a/50245014/619961