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?
Best Answer
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.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.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.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.
Explanations:
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), executesed
on the script.()
label, then some well-formed input. Then ani
command, which has no effect because it's always skipped. Finally the()
label followed by the useful part of the script.