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.)