Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Typing /usr/bin/env sed -f in terminal works.

But if use it as a shebang,

#!/usr/bin/env sed -f 
s/a/b/

The script will be fail to execute:

/usr/bin/env: sed -f: No such file or directory

I kind of believe that it's related with the -f. But how to resolve this?

share|improve this question
add comment

3 Answers 3

You can't, portably, put more than one argument on a #! line. That means only a full path and one argument (e.g. #!/bin/sed -f or #!/usr/bin/sed -f), or #!/usr/bin/env and no argument to the interpreter.

A workaround to get a portable script is to use #!/bin/sh and a shell wrapper, passing the sed script as a command-line argument. Note that this is not sanctioned by POSIX (multi-instruction scripts must be written with a separate -e argument for each instruction for portability), but it works with many implementations.

#!/bin/sh
exec sed '
s/a/b/
' "$@"

For a long script, it may be more convenient to use a heredoc. An advantage of a heredoc is that you don't need to quote the single quotes inside, if any. A major downside is that the script is fed to sed on its standard input, with two annoying consequences. Some versions of sed require -f /dev/stdin instead of -f -, which is a problem for portability. Worse, the script can't act as a filter, because the standard input is the script and can't be the data.

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

The downside of the heredoc can be remedied by a useful use of cat. Since this puts the whole script on the command line again, it's not POSIX-compliant, but largely portable in practice.

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

Another workaround is to write a script that can be parsed both by sh and by sed. This is portable, reasonably efficient, just a little ugly.

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

Explanations:

  • Under sh: define a function called b; the contents don't matter as long as the function is syntactically well-formed (in particular, you can't have an empty function). Then if true (i.e. always), execute sed on the script.
  • Under sed: branch to the () label, then some well-formed input. Then an i command, which has no effect because it's always skipped. Finally the () label followed by the useful part of the script.
  • Tested under GNU sed, BusyBox and OpenBSD. (You can get away with something simpler on GNU sed, but OpenBSD sed is picky about the parts it skips.)
share|improve this answer
    
"or #!/usr/bin/env and no argument." isn't phrased very well. Perhaps "or #!/usr/bin/env sed and no argument to sed." –  cjm Jun 13 '11 at 9:03
    
+1 for "just a little ugly" and the dual-language script :) –  retracile May 8 '12 at 18:42
add comment

There are various incompatible implementations of the shebang (#!) depending on the OS. Some are building a full argument list, some are preserving the command path and put all remaining arguments as a single one, some are ignoring all of the arguments and pass only the command path, and finally, some are passing the whole string as a single command. You seem to be in the latter case.

share|improve this answer
add comment

env is trying to find a file with name "sed -f". You can try "#!/usr/bin/sed -f" as your shebang line.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.