shebang or not shebang

Instead of having myprg magically detect whether it is being used in a shebang, why not make that explicit by using a command-line flag (such as -f) to pass it a file as a script?

From your example in the comments:

In the calc theoretical example above. calc PI + 1 should return 4.14159... Now adding the support for the shebang (i.e. a filename as the first parameter) would return the calculation contained into the file.

Make calc take a script file through -f and then create scripts with:

#!/usr/local/bin/calc -f
$1 + 1

Let's say you call this file addone.calc and make it executable. Then you can call it with:

$ ./addone.calc PI
4.141592...

That call will translate into an invocation of /usr/local/bin/calc -f ./addone.calc PI, so it's pretty clear which argument is a script file and which is a parameter to the script.

This is similar to how awk and sed behaves.

A similar (but opposite) approach is to have calc take a script file argument by default (which simplifies its use with a shebang), but add a command-line flag to use it with an expression from an argument. This is similar to how sh -c '...' works.


The real problem is the way you designed the commandline syntax of <mypgm>. Instead of trying to support two ways of interpreting its arguments, provide two ways of calling it instead.

Shebang commands are meant to be script engines that execute the content of your script; it might be bash, perl, or whatever, but the expectation is that it is called with the file name of a script to execute. How does bash do it? It does not guess. If it encounters any argument that does not look like an option (or an option's argument), it treats it as the script to execute; arguments after that are passed to the script. For example:

/bin/bash -x -e somename foo bar

Here, bash will look for the file somename and try to run it as a script with arguments foo and bar. You should do the same thing, because you might want to write <mypgm> <myscript> on the command line some day.

If you want the script-less use of <mypgm> to be the default, you can require a script to be passed with <mypgm> -f <myscript>. This is how sed does it. Then you'd use it in a shebang line like this:

#!<mypgm> -f

If you want the script case to be the default, like with bash and perl, create an option that says "there is no script this time". You could use -- for this, so that <mypgm> -- one two three does not try to run one (or anything else) as a script. In that case the shebang line would just read:

#!<mypgm>

Now, I need to know when blabla is called using the shebang, or not:

In C, you can obtain that info via getauxval(AT_EXECFN), which will tell you the name of the original executable (ie the first argument passed to execve(2)) [1].

But that string is placed in the memory immediately after the command line arguments and environment strings, at the end of the [stack] memory region, so it can be fetched directly from there.

For instance, the following perl script (name it foo.pl), if made executable with chmod 755 foo.pl, will print ./foo.pl when run directly and /usr/bin/perl when run as perl ./foo.pl:

#! /usr/bin/perl

open my $maps, "/proc/self/maps" or die "open /proc/self/maps: $!";
my $se;
while(<$maps>){ $se = hex($1), last if /^\w+-(\w+).*\[stack\]$/ }
open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!";
sysseek $mem, $se - 512, 0;
sysread $mem, $d, 512 or die "sysread: $!";
print $d =~ /([^\0]+)\0+$/, "\n";

On newer (>=3.5) linux kernels the end of the environment is also available in /proc/PID/stat (in the 51th field, as documented in the proc(5) manpage).

#! /usr/bin/perl

open my $sh, "/proc/self/stat" or die "open /proc/self/stat: $!";
my @s = <$sh> =~ /\(.*\)|\S+/g;
open my $mem, "/proc/self/mem" or die "open /proc/self/mem: $!";
seek $mem, $s[50], 0;
$/ = "\0";
my $pn = <$mem> or die "readline: $!"; chomp $pn; print "$pn\n";

[1] Linux kernels newer than 2.6.26 introduced the aux vector entry pointing to it (see the commit), but the executable name was available at the end of the stack long before that (since linux-2.0 from 1996).