47

If I want to execute a bash script which doesn't have its execution permission set, I can do:

bash script.sh

What should I use instead of bash if the script isn't executable and I don't know the correct interpreter? Is there a command that looks up the interpreter from shebang line and executes the script with it?

edwinksl
  • 23,789
Aivar
  • 635
  • What's wrong wit bash? – steffen Nov 19 '16 at 07:06
  • @steffen how do you know the file in question is a bash script? – muru Nov 19 '16 at 10:07
  • @muru quote from Question: "If I want to execute a bash script..." Moreover (even if it is not a bash script), if bash whatever works, why use something different? bash is available on virtually every *ix system, so why bother... – steffen Nov 19 '16 at 12:05
  • 2
    @steffen did you read the rest of the question? They say: "If ... then I can do: ..." and "What should I use instead of bash if ... I don't know the correct interpreter?" – muru Nov 19 '16 at 12:07
  • @muru: maybe I don't see the obvious. But if the file /has/ a shebang line, as stated in the question, bash will do exactly what is asked for. As will perl, according to the answer below. So what is the advantage of not using bash? – steffen Nov 21 '16 at 20:02
  • @steffen Indeed, you don't see the obvious. (1) Suppose a file has a shebang line. (2) As per the original question, suppose it's for a non-bash binary - SBCL lisp interpreter, say. (3) In such a case, you are wrong: bash will not do what is asked for. (Nor, in fact, does any answer say it will.) (4) perl will do the right thing. (5) So that (item (4)) is the advantage of perl over bash - it will work; bash won't. – phlummox Mar 30 '22 at 16:38

3 Answers3

76

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.

Kudu
  • 103
Ole Tange
  • 1,710
  • 47
    Well, that's just dirty. – slim Nov 17 '16 at 19:47
  • @OleTange surprising. Obviously Perl is smart enough to read the shebang and not pushes itself upfront without looking. Without the shebang, also Perl is helpless however. – Jacob Vlijm Nov 17 '16 at 20:48
  • @JacobVlijm that's true, but then again the question specifically says there is always a shebang – simbabque Nov 18 '16 at 10:39
  • 1
    Does the file extension .js also work? – Pysis Nov 18 '16 at 19:08
  • 1
    Also, what machines don't do #!. I've seem a few to more now, and have not experienced this problem. – Pysis Nov 18 '16 at 19:08
  • @Pysis the file extension does not matter. It is only the #! in the first line that matters. I just included it as a short way of explaining all the script languages that I tested. – Ole Tange Nov 18 '16 at 22:05
  • 1
    Ok, it's just your example seemed to highlight a lot of file extensions, rather than showcase several shebang lines from files, and I guess I assumed that's how its prediction worked. – Pysis Nov 19 '16 at 03:06
  • @Pysis No modern OSes. But I seem to remember the early versions of Ultrix had the problem with #!. – Ole Tange Nov 19 '16 at 10:00
  • 4
    I like how man perlrun sheepishly admits that it's "slightly bizarre" :). I think this should be treated as a curiosity aimed at non-UNIX environments and very very old versions of UNIX. – slim Nov 19 '16 at 17:31
  • 1
    @OleTange: I'm pretty sure Windows doesn't do #! out of the box. But YMMV on whether Windows is a "modern" OS. – Kevin Nov 19 '16 at 20:49
26

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:

  1. When you run the script, calling the interpreter always overrules possible shebangs, executable or not, shebang or not.
  2. If not executable and run from the interpreter, the script needs no shebang.
  3. 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 and bash 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 a FileNotFoundError.

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

Jacob Vlijm
  • 83,767
  • OK, so it means although Linux has shebang extraction implemented somewhere (so that it can select correct interpreter for executable srcipts), it's not provided as a standalone standard command. – Aivar Nov 17 '16 at 12:01
  • @Aivar extracting the shebang isn' t the issue, but running code without it is perfectly possible. – Jacob Vlijm Nov 17 '16 at 13:46
  • @Aivar Ah, I see what you mean. If the script is executable and run without language in the command, the script calls the interpreter, not the other way around. – Jacob Vlijm Nov 17 '16 at 13:55
  • 1
    @JacobVlijm I wouldn't say "the script calls the interpreter", more like "the Linux kernel takes the shebang line for figuring out which interpreter to call when executing the script". – Paŭlo Ebermann Nov 17 '16 at 20:37
  • @PaŭloEbermann Thanks! True of course. The kernel takes care of the whole procedure either way, but figuratively, and I think better for understanding, is to say the script is "allowed" to take care of what interpreter to call (and actually do it). Not sure about the wording, but I' d like to describe it as if the initiative is on the script, while the kernel actually does the job. – Jacob Vlijm Nov 17 '16 at 20:47
6

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.

slim
  • 169
  • 2
    I had always assumed that was a function of the shell, NOT the kernel. Learned something new today. – boatcoder Nov 18 '16 at 20:54
  • 1
    @boatcoder -- Since you're interested, added a link to where the Linux source code handles it. – slim Nov 19 '16 at 16:50