Au lieu d'avoir myprg
détecter par magie s'il est utilisé dans un shebang, pourquoi ne pas le rendre explicite en utilisant un indicateur de ligne de commande (tel que -f
) pour lui passer un fichier en tant que script ?
De votre exemple dans les commentaires :
Dans l'exemple théorique calc ci-dessus.
calc PI + 1
devrait retourner 4.14159... Maintenant, ajouter le support pour le shebang (c'est-à-dire un nom de fichier comme premier paramètre) renverrait le calcul contenu dans le fichier.
Faire calc
prendre un fichier de script à travers -f
puis créez des scripts avec :
#!/usr/local/bin/calc -f
$1 + 1
Disons que vous appelez ce fichier addone.calc
et le rendre exécutable. Ensuite, vous pouvez l'appeler avec :
$ ./addone.calc PI
4.141592...
Cet appel se traduira par une invocation de /usr/local/bin/calc -f ./addone.calc PI
, il est donc assez clair quel argument est un fichier de script et lequel est un paramètre du script.
Ceci est similaire à la façon dont awk
et sed
se comporte.
Une approche similaire (mais opposée) consiste à avoir calc
prendre un argument de fichier de script par défaut (ce qui simplifie son utilisation avec un shebang), mais ajouter un indicateur de ligne de commande pour l'utiliser avec une expression à partir d'un argument. Ceci est similaire à la façon dont sh -c '...'
fonctionne.
Le vrai problème est la façon dont vous avez conçu la syntaxe de ligne de commande de <mypgm>
. Au lieu d'essayer de soutenir deux manières d'interpréter ses arguments, proposez plutôt deux manières de l'appeler.
Les commandes Shebang sont censées être des moteurs de script qui exécutent le contenu de votre script; il peut s'agir de bash
, perl
, ou autre, mais on s'attend à ce qu'il soit appelé avec le nom de fichier d'un script à exécuter. Comment bash
fais le? Il ne devine pas. S'il rencontre un argument qui ne ressemble pas à une option (ou à l'argument d'une option), il le traite comme le script à exécuter; les arguments suivants sont passés au script. Par exemple :
/bin/bash -x -e somename foo bar
Ici, bash cherchera le fichier somename
et essayez de l'exécuter en tant que script avec les arguments foo
et bar
. Vous devriez faire la même chose, car vous pourriez veux écrire <mypgm> <myscript>
sur la ligne de commande un jour.
Si vous voulez l'utilisation sans script de <mypgm>
pour être la valeur par défaut, vous pouvez exiger qu'un script soit passé avec <mypgm> -f <myscript>
. C'est ainsi que sed
le fait. Ensuite, vous l'utiliseriez dans une ligne de shebang comme celle-ci :
#!<mypgm> -f
Si vous voulez que la casse du script soit la valeur par défaut, comme avec bash
et perl
, créez une option indiquant "il n'y a pas de script cette fois". Vous pouvez utiliser --
pour cela, de sorte que <mypgm> -- one two three
n'essaie pas d'exécuter one
(ou toute autre chose) en tant que script. Dans ce cas, la ligne shebang se lirait simplement :
#!<mypgm>
Maintenant, j'ai besoin de savoir quand blabla est appelé en utilisant le shebang, ou non :
En C, vous pouvez obtenir ces informations via getauxval(AT_EXECFN)
, qui vous indiquera le nom de l'exécutable d'origine (c'est-à-dire le premier argument passé à execve(2)
) [1].
Mais cette chaîne est placée dans la mémoire immédiatement après les arguments de ligne de commande et les chaînes d'environnement, à la fin du [stack]
région mémoire, afin qu'il puisse être récupéré directement à partir de là.
Par exemple, le script perl suivant (nommez-le foo.pl
), si rendu exécutable avec chmod 755 foo.pl
, imprimera ./foo.pl
lorsqu'il est exécuté directement et /usr/bin/perl
lorsqu'il est exécuté en tant que 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";
Sur les noyaux Linux plus récents (>=3.5), la fin de l'environnement est également disponible dans /proc/PID/stat
(dans le 51ème champ, comme documenté dans le proc(5)
page de manuel).
#! /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] Les noyaux Linux plus récents que 2.6.26 ont introduit l'entrée de vecteur aux pointant dessus (voir le commit), mais le nom de l'exécutable était disponible à la fin de la pile bien avant cela (depuis linux-2.0 de 1996).