Is there a command for running a script according to its shebang line?
Yep. It is called perl
. Some examples, with the corresponding interpreter in the shebang line of the file (the actual file extension doesn't matter):
perl foo.bash # works
perl foo.lua # works
perl foo.clisp # works
perl foo.csh # works
perl foo.php # works
perl foo.gnuplot # works (no arguments)
perl foo.pl # works (obviously)
perl foo.py # works
perl foo.sh # works
perl foo.tcl # works
perl foo.rb # works
perl foo.nodejs # works
perl foo.r # works
perl foo.oct # works
perl foo.csharp # works (no arguments)
This is mentioned in Perl's documentation:
If the
#!
line does not contain the word "perl" nor the word "indir", the program named after the#!
is executed instead of the Perl interpreter. This is slightly bizarre, but it helps people on machines that don't do#!
, because they can tell a program that their SHELL is /usr/bin/perl, and Perl will then dispatch the program to the correct interpreter for them.
Scripts do not necessarily have a shebang
If the script was run from the interpreter, You cannot be sure it has the shebang at all. Scripts, run from the interpreter do not need the shebang, if you call the interpreter to run the code.
The answer is therefore no, there is no command that will find out for sure what is the language (interpreter) to run the script with. You can however always look inside the script and see if it has the shebang to find out.
The rules in short:
- When you run the script, calling the interpreter always overrules possible shebangs, executable or not, shebang or not.
- If not executable and run from the interpreter, the script needs no shebang.
- If the script is run without calling the interpreter first, it needs (and uses) the shebang to find out what interpreter to call, and it needs to be executable to have the "permission" to call the interpreter from its shebang.
If the script has no shebang however, there is no (direct*) information inside the script to tell what interpreter to use.
Having said that
You could of course always write a wrapper script to try to find out if the script has the shebang and read the interpreter from that, subsequently run it from the found interpreter.
An example
#!/usr/bin/env python3
import subprocess
import sys
args = sys.argv[1:]; script = args[0]
try:
lang = open(script).readlines()[0].replace("#!", "").strip().split()[-1]
cmd = [lang, script]+args[1:]
subprocess.call(cmd)
except (PermissionError, FileNotFoundError, IndexError):
print("No valid shebang found")
Save it as
tryrun
in$PATH
(e.g.~/bin
, make the directory if it does not exist, log out and back in), make it executable. Then running:tryrun /path/to/nonexecutablescript
calls (tested) the correct interpreter on my non-executable
python
andbash
scripts.
Explanation
- The script simply reads the first line of the script, removes the
#!
and uses the rest to call the interpreter. - If it fails to call a valid interpreter, it will raise either a
PermissionError
or aFileNotFoundError
.
Note
The extension (.sh
, .py
etc) plays no role whatsoever in determining the appropriate interpreter on Linux.
(*It is of course possible to develop a "smart" guess- algorithm to determine the syntax from the code.)
You can achieve this with a script like this:
#!/bin/bash
copy=/tmp/runner.$$
cp $1 ${copy}
chmod u+x ${copy}
${copy}
rm ${copy}
Thus:
$ echo "echo hello" > myscript
$ ./myscript
bash: ./myscript: Permission denied
$ ./runscript myscript
hello
I recommend against doing this. Permissions are there for a reason. This is a program for subverting permissions.
Note that shebang handling is a kernel function (in the Linux source code - fs/binfmt_script.c
). Fundamentally the process invoking a script directly doesn't know about the #!
-- the kernel uses it to work out that it needs to launch an interpreter.