8

Say I have a file hello:

#!/bin/sh

echo "Hello World!"

Provided the executable bit is set on that file, I can execute it by entering its path on the prompt:

$ ./hello
Hello World!

Is there a more explicit equivalent to the above? Something akin to:

$ execute hello

I know I can pass hello as an argument to /bin/sh, but I'm looking for a solution that automatically uses the interpreter specified in the shebang line

My use case for this is to execute script files that do not have the executable flag set. These files are stored in a git repository, so I would like to avoid setting their executable flag or having to copy them to another location first.

4
  • 3
    How about simply extracting the interpreter from the #! line with INTERPRETER=$(sed -n ' /^#!/s///p;1q' "YOUR_SCRIPT") and then "${INTERPRETER:-/bin/sh}" "YOUR_SCRIPT"? Make a script out of it.
    – Philippos
    Aug 23, 2021 at 13:41
  • 3
  • You can use the command_not_found_handle() feature of bash to catch commands like hello (where hello is not found in PATH), but not for ./hello or path/to/hello.
    – user313992
    Aug 24, 2021 at 13:20
  • 5
    git can hold executable flags. The file system underneath must support it. Aug 24, 2021 at 15:53

2 Answers 2

17

You can use perl:

perl hello

From perl docs:

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.

(via)

6
  • 7
    And I thought I'd never find a use for Perl again! Aug 23, 2021 at 15:13
  • 6
    However, note that this isn't exactly the same as doing it via the kernel: For one, Perl may handle multiple arguments the hashbang line differently than the system. As far as I can see, perl only uses the first, while Linux sticks everything into one argument, and some other systems might support more than one. E.g. #!/bin/bash -x -u gets started with just -x under Perl, while under macOS, Bash gets both options, and on Linux, it gets the single arg -x -u (which produces an error). Also if the system supports setuid scripts, it's impossible to emulate that with perl.
    – ilkkachu
    Aug 23, 2021 at 17:48
  • 3
    To me this wouldn't be a more explicit equivalent to ./hello but rather a source of confusion.
    – Aaron
    Aug 24, 2021 at 10:34
  • 1
    @Aaron. Then you should read the question again. ./hello works only with executable files. OP explicitly doesn't want that.
    – pLumo
    Aug 24, 2021 at 10:57
  • 2
    @Aaron I agree that it's confusing, but it doesn't seem that there is any alternative besides rolling my own (which I want to avoid). Aug 24, 2021 at 11:17
1

The following Perl5 script, with no error checking, grabs the hashbang line and calls it to execute the script passed as an argument: (simplifying and streamlining per ilkkachu's comment), to avoid reading the whole file...

open FH, ("<",$ARGV[0])
$line = <FH>;
close FH;
if ($line =~ /^\#\!(.*)/) {
    $engine = $1;
    exec("$engine $ARGV[0]");
}
2
  • Besides being pointless, this doesn't even work. But if it worked, it would still be worse than perl's built-in shebang parser. First, because it doesn't allow spaces between the ! and the path. And second, because it allows injecting shell code via the filename.
    – user313992
    Aug 26, 2021 at 3:23
  • By the way, it seems that the bash shell, at least, ignores spaces prepended to executables' filenames, so there's no need to make the regexp more complicated to allow for whitespaces... Aug 26, 2021 at 13:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .