45

I am using a bison parser in my project. When I run the following command:

sed -i y.tab.c -e "s/  __attribute__ ((__unused__))$/# ifndef __cplusplus\n  __attribute__ ((__unused__));\n# endif/"

I get this error sed: -i may not be used with stdin

The command works fine in linux machines. I am using Mac OS X 10.9. It throws an error only on mac os x. I am not sure why. Can anyone help?

Thanks

sarghau
  • 552
  • 1
  • 6
  • 14
  • Try removing the "-e" flag, and put "y.tab.c" at the end of the line. Like this: `sed -i "s/.../" y.tab.c – Trenin Jan 20 '14 at 20:02

5 Answers5

59

The problem is that Mac OS X uses the BSD version of sed, which treats the -i option slightly differently. The GNU version used in Linux takes an optional argument with -i: if present, sed makes a backup file whose name consists of the input file plus the argument. Without an argument, sed simply modifies the input file without saving a backup of the original.

In BSD sed, the argument to -i is required. To avoid making a backup, you need to provide a zero-length argument, e.g. sed -i '' y.tab.c ....

Your command, which simply edits y.tab.c with no backup in Linux, would attempt to save a backup file using 'y.tab.c' as an extension. But now, with no other file in the command line, sed thinks you want to edit standard input in-place, something that is not allowed.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 3
    `sed -i '' file` works on OSX, but it's not portable. It fails on at least Cygwin and probably Linux. `sed -i'.bak' file` is the most portable syntax I could find, but then you need to do `rm file.bak` at the end. – Tomi Aarnio May 15 '14 at 07:42
  • 2
    Neither form is portable, because the POSIX standard doesn't specify a `-i` option. For true portability, you need to handle the temporary file yourself: `sed '...' file > file.new; mv file file.bak; mv file.new file`. – chepner May 15 '14 at 12:49
31

From the sed manpage:

-i extension Edit files in-place, saving backups with the specified extension. 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 situ- ations where disk space is exhausted, etc.

The solution is to send a zero-length extension like this:

sed -i '' 's/apples/oranges/' file.txt
KunMing Xie
  • 1,613
  • 17
  • 15
  • Worked as intended and replaced all strings on a json file. Even with '['or ']' didn't need to escape. My expression: sed -i '' 's/wow/][/' wow.json – Kunami Jul 05 '19 at 13:02
15

You need to put the input file as the last parameter.

sed -i -e "s/  __attribute__ ((__unused__))$/# ifndef __cplusplus\n  __attribute__ ((__unused__));\n# endif/" y.tab.c
Trenin
  • 2,041
  • 1
  • 14
  • 20
  • For me the -e flag is adding a literal "-e" to the end of the file extension of the output file which is a copy of the input file. – John Feb 24 '21 at 16:28
  • @John That is not at all what the -e does. It basically says use the following as a script. – Trenin Feb 27 '21 at 00:48
  • I am not using GNU sed. Maybe that's the reason? I ran that command three times and every time the file was named `file.txt-e` – John Feb 27 '21 at 08:18
  • @John https://ss64.com/osx/sed.html. What OS are you using? What do you get for the '-e' option when you type 'man sed'? – Trenin Mar 11 '21 at 16:34
  • I am on macOS "-e command Append the editing commands specified by the command argument to the list of commands." – John Mar 11 '21 at 17:50
  • @John From sed docs, "Because -i takes an optional argument, it should not be followed by other short options". What's happening is that `-i -e` is causing `-i` to treat `-e` as the input to `-i`. You need to either explicitly supply an argument to `-i` (`-i '' -e`) or specify the `-e` flag before the `-i` flag (`-e -i` or `-ei`). – Abion47 Mar 15 '22 at 14:33
10

Piggy-backing off of @chepner's explanation for a quick-and-dirty solution:

Install the version of sed that'll get the job done with brew install gnu-sed, then replace usages of sed in your script with gsed.

(The homebrew community is fairly cognizant of issues that can arise of OS X built-ins are overridden unexpectedly and has worked to not do that for most alternate-distro commands.)

Slipp D. Thompson
  • 33,165
  • 3
  • 43
  • 43
0

On a side note, I wanted to

As Slipp D. Thompson suggested, I used gsed like below, in macOS, which is similar in linux (remove the g from gsed):

find . -name "*.tex" -print0 | xargs -0 gsed -i '1s/^/% !TEX spellcheck = en-US \n/'

Works as intended

massisenergy
  • 1,764
  • 3
  • 14
  • 25