252

I'd like edit a file with sed on OS X. I'm using the following command:

sed 's/oldword/newword/' file.txt

The output is sent to the terminal. file.txt is not modified. The changes are saved to file2.txt with this command:

sed 's/oldword/newword/' file1.txt > file2.txt

However I don't want another file. I just want to edit file1.txt. How can I do this?

I've tried the -i flag. This results in the following error:

sed: 1: "file1.txt": invalid command code f
kenorb
  • 155,785
  • 88
  • 678
  • 743
SundayMonday
  • 19,147
  • 29
  • 100
  • 154

8 Answers8

432

You can use the -i flag correctly by providing it with a suffix to add to the backed-up file. Extending your example:

sed -i.bu 's/oldword/newword/' file1.txt

Will give you two files: one with the name file1.txt that contains the substitution, and one with the name file1.txt.bu that has the original content.

Mildly dangerous

If you want to destructively overwrite the original file, use something like:

sed -i '' 's/oldword/newword/' file1.txt
      ^ note the space

Because of the way the line gets parsed, a space is required between the option flag and its argument because the argument is zero-length.

Other than possibly trashing your original, I’m not aware of any further dangers of tricking sed this way. It should be noted, however, that if this invocation of sed is part of a script, The Unix Way™ would (IMHO) be to use sed non-destructively, test that it exited cleanly, and only then remove the extraneous file.

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
whittle
  • 4,344
  • 1
  • 15
  • 5
  • You're right I was omitting the extension after the -i flag. Is `-i''` dangerous for any reason other than potentially messing up the original file (and having no back-up)? – SundayMonday Sep 27 '11 at 17:48
  • According to the `sed` man page if you run out of disk space on the device you could corrupt a file mid-stride and have a bad output result. If you are working under local source control `sed -i ""` without backups should be fine most of the time (or just `git init && git add -A . && git commit -m 'backup'` prior to running sed in `-i` mode). – cfeduke May 29 '14 at 14:59
  • 8
    I found the mildly dangerous bit particularly useful. +1 – Andre Dec 22 '14 at 15:37
  • 10
    BSD sed requires that there be no space after the -i flag. So, `-i''` is valid, but `-i ''` is not. – Todd A. Jacobs Apr 19 '15 at 21:05
  • 3
    Argh! BSD sed is ignoring the '\n' character and printing 'n' instead! Im just going to replace BSD sed with GNU sed on my mac! – Nicholas Terry Mar 11 '16 at 23:40
  • How would it be to replace the strings oldword to newword in all files for the whole folder? – Andres Ramos Jan 26 '17 at 20:42
  • I don't think you're tricking sed. Its man page mentions using a zero-length argument to avoid creating a backup file: *If a zero-length extension is given, no backup will be saved. It is not recommended to give a zero-length extension when in-place editing files, as you risk corruption or partial content in situations where disk space is exhausted, etc.* – Rob Grant Jul 13 '22 at 08:32
  • A `g` at the end `s/oldword/newword/g` is required if multiple `oldword` in the same line needs to be replaced. Without `g`, only the first `oldword` in any line is replaced. – Sadman Sakib Apr 09 '23 at 01:42
30

The -i flag probably doesn't work for you, because you followed an example for GNU sed while macOS uses BSD sed and they have a slightly different syntax.

All the other answers tell you how to correct the syntax to work with BSD sed. The alternative is to install GNU sed on your macOS with:

brew install gsed

and then use it instead of the sed version shipped with macOS (note the g prefix), e.g:

gsed -i 's/oldword/newword/' file1.txt

If you want GNU sed commands to be always portable to your macOS, you could prepend "gnubin" directory to your path, by adding something like this to your .bashrc/.zshrc file (run brew info gsed to see what exactly you need to do):

export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

and from then on the GNU sed becomes your default sed and you can simply run:

sed -i 's/oldword/newword/' file1.txt
Jakub Kukul
  • 12,032
  • 3
  • 54
  • 53
26

I've similar problem with MacOS

sed -i '' 's/oldword/newword/' file1.txt

doesn't works, but

sed -i"any_symbol" 's/oldword/newword/' file1.txt

works well.

ksnt
  • 269
  • 3
  • 2
9
sed -i -- "s/https/http/g" file.txt
rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156
jazzed
  • 382
  • 3
  • 6
  • When I try that on mac, I get an error that says "sed: -i may not be used with stdin". – Christopher Bradshaw Aug 12 '20 at 23:38
  • 4
    On my macOS (10.15.6), the `--` seems to work, but in fact it creates a `file.txt--` backup file, which must then be manually removed, so this solution is not better than the accepted one. And the `--` is misleading, as if the option were acting like GNU tools which treat it differently. – anol Apr 30 '21 at 12:42
5

You can use -i'' (--in-place) for sed as already suggested. See: The -i in-place argument, however note that -i option is non-standard FreeBSD extensions and may not be available on other operating systems. Secondly sed is a Stream EDitor, not a file editor.


Alternative way is to use built-in substitution in Vim Ex mode, like:

$ ex +%s/foo/bar/g -scwq file.txt

and for multiple-files:

$ ex +'bufdo!%s/foo/bar/g' -scxa *.*

To edit all files recursively you can use **/*.* if shell supports that (enable by shopt -s globstar).


Another way is to use gawk and its new "inplace" extension such as:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1
kenorb
  • 155,785
  • 88
  • 678
  • 743
4

This creates backup files. E.g. sed -i -e 's/hello/hello world/' testfile for me, creates a backup file, testfile-e, in the same dir.

JustADude
  • 123
  • 9
  • Same here with Mac OS Catalina. Passing both -i and -e separately (NOT -ie together!) creates a backup file with '-e' appended. – Daniel Kesner Apr 20 '20 at 19:27
2

You can use:

sed -i -e 's/<string-to-find>/<string-to-replace>/' <your-file-path>

Example:

sed -i -e 's/Hello/Bye/' file.txt

This works flawless in Mac.

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
Vincent Lal
  • 103
  • 1
  • 7
    This creates a backup file for me in Mac OS Catalina with '-e' appended. – Daniel Kesner Apr 20 '20 at 19:23
  • 8
    This is wrong for macOS. Please take a look at the "man sed" page there. `-i` requires an argument for macOS. Since you don't specify one, it uses `-e`, which results in having a second file with `-e` appended. `-e` doesn't exist in macOS, it is `-E`. So correct command for macOS is `sed -i '' -E 's/Hello/Bye/' file.txt` – Marc J. Schmidt Jun 11 '20 at 14:08
  • A minor variation that works in my case. I replaced two different strings (you can add more I guess) with this command: sed -i '' -e 's/_tools/tools/' -e 's/_static/static/' test.txt – Albert Feb 14 '23 at 10:42
  • @MarcJ.Schmidt it is not _wrong_ but rather is a good way to have identical scripts run on both mac and linux. The backup file is an affordable cost, compared to maintaining different scripts across platforms; it is easy to find and delete such backup files too. – Yijun Blue Yuan May 11 '23 at 00:20
0

If you need to substitute more than one different words:

sed -i '' -e 's/_tools/tools/' -e 's/_static/static/' test.txt
Albert
  • 171
  • 7