/usr/bin/env questions regarding shebang line pecularities
First of all, you should very seldom use $*
and you should almost always use "$@"
instead. There are a number of questions here on SO which explain the ins and outs of why.
Second - the env
command has two main uses. One is to print the current environment; the other is to completely control the environment of a command when it is run. The third use, which you are demonstrating, is to modify the environment, but frankly there's no need for that - the shells are quite capable of handling that for you.
Mode 1:
env
Mode 2:
env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args
This version cancels all inherited environment variables and runs command
with precisely the environment set by the ENVVAR=value options.
The third mode - amending the environment - is less important because you can do that fine with regular (civilized) shells. (That means "not C shell" - again, there are other questions on SO with answers that explain that.) For example, you could perfectly well do:
#!/bin/bash
export PATH=${PREPENDPATH:?}:$PATH
exec python "$@"
This insists that $PREPENDPATH
is set to a non-empty string in the environment, and then prepends it to $PATH
, and exports the new PATH setting. Then, using that new PATH, it executes the python
program with the relevant arguments. The exec
replaces the shell script with python
. Note that this is quite different from:
#!/bin/bash
PATH=${PREPENDPATH:?}:$PATH exec python "$@"
Superficially, this is the same. However, this will execute the python
found on the pre-existing PATH, albeit with the new value of PATH in the process's environment. So, in the example, you'd end up executing Python from /usr/bin
and not the one from /home/pi/prepend/bin
.
In your situation, I would probably not use env
and would just use an appropriate variant of the script with the explicit export.
The env
command is unusual because it does not recognize the double-dash to separate options from the rest of the command. This is in part because it does not take many options, and in part because it is not clear whether the ENVVAR=value options should come before or after the double dash.
I actually have a series of scripts for running (different versions of) a database server. These scripts really use env
(and a bunch of home-grown programs) to control the environment of the server:
#!/bin/ksh
#
# @(#)$Id: boot.black_19.sh,v 1.3 2008/06/25 15:44:44 jleffler Exp $
#
# Boot server black_19 - IDS 11.50.FC1
IXD=/usr/informix/11.50.FC1
IXS=black_19
cd $IXD || exit 1
IXF=$IXD/do.not.start.$IXS
if [ -f $IXF ]
then
echo "$0: will not start server $IXS because file $IXF exists" 1>&2
exit 1
fi
ONINIT=$IXD/bin/oninit.$IXS
if [ ! -f $ONINIT ]
then ONINIT=$IXD/bin/oninit
fi
tmpdir=$IXD/tmp
DAEMONIZE=/work1/jleffler/bin/daemonize
stdout=$tmpdir/$IXS.stdout
stderr=$tmpdir/$IXS.stderr
if [ ! -d $tmpdir ]
then asroot -u informix -g informix -C -- mkdir -p $tmpdir
fi
# Specialized programs carried to extremes:
# * asroot sets UID and GID values and then executes
# * env, which sets the environment precisely and then executes
# * daemonize, which makes the process into a daemon and then executes
# * oninit, which is what we really wanted to run in the first place!
# NB: daemonize defaults stdin to /dev/null and could set umask but
# oninit dinks with it all the time so there is no real point.
# NB: daemonize should not be necessary, but oninit doesn't close its
# controlling terminal and therefore causes cron-jobs that restart
# it to hang, and interactive shells that started it to hang, and
# tracing programs.
# ??? Anyone want to integrate truss into this sequence?
asroot -u informix -g informix -C -a dbaao -a dbsso -- \
env -i HOME=$IXD \
INFORMIXDIR=$IXD \
INFORMIXSERVER=$IXS \
INFORMIXCONCSMCFG=$IXD/etc/concsm.$IXS \
IFX_LISTEN_TIMEOUT=3 \
ONCONFIG=onconfig.$IXS \
PATH=/usr/bin:$IXD/bin \
SHELL=/usr/bin/ksh \
TZ=UTC0 \
$DAEMONIZE -act -d $IXD -o $stdout -e $stderr -- \
$ONINIT "$@"
case "$*" in
(*v*) track-oninit-v $stdout;;
esac
using #!/usr/bin/env python3 shebang with Windows
No, Windows does not support shebang lines.
The documentation you've linked relates to the py
launcher installed by Python, which can interpret various shebang lines to choose a Python version to run a script with.
setuptools
is able to generate wrapper .exes for your Python scripts, but it gets a little involved and already assumes you have a package with a setup.py
and so on.
Locally, if you really, really need this, you probably could add .py
to the PATHEXT
environment variable, so the Windows command line looks up .py
s like it looks up .exe
s (and various others; the current modern default is .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
). However, this will naturally not scale for distributing apps, as all of your users would need to set that too.
My recommendation is to stick with just that boring old python testing.py
, really.
Why is #!/usr/bin/env bash superior to #!/bin/bash?
#!/usr/bin/env
searches PATH
for bash
, and bash
is not always in /bin
, particularly on non-Linux systems. For example, on my OpenBSD system, it's in /usr/local/bin
, since it was installed as an optional package.
If you are absolutely sure bash
is in /bin
and will always be, there's no harm in putting it directly in your shebang—but I'd recommend against it because scripts and programs all have lives beyond what we initially believe they will have.
How to use /usr/bin/env perl functionality along with perl arguments?
This is one of those things that Just Doesn't Work™ on some systems, notably those with a GNU env
.
Here's a sneaky workaround mentioned in perlrun that I've (ab)used in the past:
#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
if 0;
print "Hello, world!\n";
This will find perl
on your PATH and you can add whatever other switches you'd like to the command line. You can even set environment variables, etc. before perl
is invoked. The general idea is that sh
runs the eval
, but perl
doesn't, and the extra gnarly bits ensure that Perl finds your program correctly and passes along all the arguments.
#!/bin/sh
FOO=bar; export FOO
#! -*-perl-*-
eval 'exec perl -d:Trace -x -wS $0 ${1+"$@"}'
if 0;
$Devel::Trace::TRACE = 1;
print "Hello, $ENV{FOO}!\n";
If you save the file with a .pl
extension, your editor should detect the correct file syntax, but the initial shebang might throw it off. The other caveat is that if the Perl part of the script throws an error, the line number(s) might be off.
The neat thing about this trick is that it works for Ruby too (and possibly some other languages like Python, with additional modifications):
#!/bin/sh
#! -*-ruby-*-
eval 'exec ruby -x -wS $0 ${1+"$@"}' \
if false
puts "Hello, world!"
Hope that helps!
How do I set the taint mode in a perl script with a '#!/usr/bin/env perl'- shebang?
You can pass the PERL5OPT environment variable on the shebang line:
#!/usr/bin/env PERL5OPT=-T perl
This seems all rather backwards to me.
Another option, is to re-execute the script under taint mode if you detect it's not on:
#!/usr/bin/env perl
warn 'Taint mode is '.(${^TAINT} ? 'on' : 'off'); # For debugging
exec($^X,'-T',$0,@ARGV) unless ${^TAINT};
# do stuff under taint mode here
Obviously, this is a major startup performance hit.
Shebang line parsing problems in Ubuntu
There's no good solution, as different unices treat multi-word #! lines differently. Portable #! use limits you to at most one argument to the interpreter on the #! line, and no whitespace in the interpreter or argument.
If the language allows it, you can make the script a shell script which takes care of loading the interpreter with whatever command line it likes. For example, in Perl, from the perl manual:
#!/bin/sh -- # -*- perl -*- -p
eval 'exec perl -wS "$0" ${1+"$@"}'
if $running_under_some_shell;
The shell stops processing after the second line, and Perl sees lines 2–3 as an instruction that does nothing. Some lisp/scheme dialects make #!
...!#
a comment, allowing you to write
#!/bin/sh
exec guile -s "$0" "$@"
!# ;; scheme code starts here
In general, the only solutions involve two files. You can write #!/usr/bin/env mywrapper
where mywrapper
is a program (it can be a script) that calls the actual interpreter with whatever argument it wants. Or you can make the executable itself the wrapper script and keep the interpreted file separate. The second solution has the advantage of working even if the interpreter doesn't accept a leading #!
line.
Related Topics
List of Files Modified 1 Hour Before
What Are The Lowest Possible Permissions for Typo3
Installing Ffmpeg on Amazon Linux - Cpp, Gcc & Libstdc++ Dependancies
Need Explanation on Pri Standard Format Specifier for Ps - Possible Bug in Documentation
Gui Svn Client for Debian Linux
Elastic Beanstalk: Log Task Customization on Amazon Linux 2 Platforms
Why Does The Linker Modify a -Defsym "Absolute Address"
Jenkins Running at Very High CPU Usage
How to Install Xclip on an Ec2 Instance
Put Command Output into String
Simulate Effect of Select() and Poll() in Kernel Socket Programming
How to Delete All Files Starting with ._ from The Shell in Linux
Efficiently Read The Average Color of The Screen Content Rendered by Xbmc
In Shell, Split a Portion of a String with Dot as Delimiter
How to Check If The Sed Command Replaced Some String