Invece di avere myprg
rilevare magicamente se viene utilizzato in uno shebang, perché non renderlo esplicito utilizzando un flag della riga di comando (come -f
) per passargli un file come script?
Dal tuo esempio nei commenti:
Nell'esempio teorico di calcolo sopra.
calc PI + 1
dovrebbe restituire 4.14159... Ora aggiungendo il supporto per lo shebang (cioè un nome file come primo parametro) restituirebbe il calcolo contenuto nel file.
Crea calc
prendi un file di script attraverso -f
e quindi creare script con:
#!/usr/local/bin/calc -f
$1 + 1
Diciamo che chiami questo file addone.calc
e renderlo eseguibile. Quindi puoi chiamarlo con:
$ ./addone.calc PI
4.141592...
Quella chiamata si tradurrà in un'invocazione di /usr/local/bin/calc -f ./addone.calc PI
, quindi è abbastanza chiaro quale argomento è un file di script e quale è un parametro dello script.
Questo è simile a come awk
e sed
si comporta.
Un approccio simile (ma opposto) è avere calc
accetta un argomento del file di script per impostazione predefinita (che ne semplifica l'uso con uno shebang), ma aggiungi un flag della riga di comando per usarlo con un'espressione da un argomento. Questo è simile a come sh -c '...'
funziona.
Il vero problema è il modo in cui hai progettato la sintassi della riga di comando di <mypgm>
. Invece di cercare di supportare due modi di interpretare i suoi argomenti, fornisci invece due modi di chiamarlo.
I comandi Shebang sono pensati per essere motori di script che eseguono il contenuto del tuo script; potrebbe essere bash
, perl
, o qualsiasi altra cosa, ma l'aspettativa è che venga chiamato con il nome file di uno script da eseguire. Come funziona bash
fallo? Non indovina. Se incontra un argomento che non sembra un'opzione (o l'argomento di un'opzione), lo tratta come lo script da eseguire; gli argomenti successivi vengono passati allo script. Ad esempio:
/bin/bash -x -e somename foo bar
Qui, bash cercherà il file somename
e prova a eseguirlo come uno script con argomenti foo
e bar
. Dovresti fare la stessa cosa, perché potresti vuoi scrivere <mypgm> <myscript>
un giorno sulla riga di comando.
Se vuoi l'uso senza script di <mypgm>
per essere l'impostazione predefinita, puoi richiedere che uno script venga passato con <mypgm> -f <myscript>
. Ecco come sed
lo fa. Quindi lo useresti in una riga shebang come questa:
#!<mypgm> -f
Se vuoi che lo script case sia l'impostazione predefinita, come con bash
e perl
, crea un'opzione che dica "questa volta non ci sono script". Potresti usare --
per questo, in modo che <mypgm> -- one two three
non tenta di eseguire one
(o qualsiasi altra cosa) come sceneggiatura. In tal caso la riga shebang leggerebbe semplicemente:
#!<mypgm>
Ora, ho bisogno di sapere quando blabla viene chiamato usando lo shebang, oppure no:
In C, puoi ottenere queste informazioni tramite getauxval(AT_EXECFN)
, che ti dirà il nome dell'eseguibile originale (cioè il primo argomento passato a execve(2)
) [1].
Ma quella stringa viene inserita nella memoria immediatamente dopo gli argomenti della riga di comando e le stringhe di ambiente, alla fine del [stack]
regione di memoria, in modo che possa essere recuperato direttamente da lì.
Ad esempio, il seguente script perl (chiamalo foo.pl
), se reso eseguibile con chmod 755 foo.pl
, stamperà ./foo.pl
quando eseguito direttamente e /usr/bin/perl
quando eseguito come 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";
Sui kernel Linux più recenti (>=3.5) la fine dell'ambiente è disponibile anche in /proc/PID/stat
(nel 51° campo, come documentato nel 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] I kernel Linux più recenti di 2.6.26 hanno introdotto la voce del vettore aux che punta ad esso (vedi il commit), ma il nome dell'eseguibile era disponibile alla fine dello stack molto prima (da linux-2.0 del 1996).