web-dev-qa-db-fra.com

Comment utiliser plusieurs arguments pour awk avec un Shebang (c'est-à-dire #!)?

Je voudrais exécuter un gawk script avec --re-interval en utilisant un Shebang. L'approche "naïve" de 

#!/usr/bin/gawk --re-interval -f
... awk script goes here

ne fonctionne pas car gawk est appelé avec le premier argument "--re-interval -f" (non divisé autour de l'espace), qu'il ne comprend pas. Y at-il une solution de contournement pour cela?

Bien sûr, vous pouvez non seulement appeler directement gawk mais l'envelopper dans un script shell qui divise le premier argument, ou créer un script shell qui appelle ensuite gawk et le placer dans un autre fichier, mais je me demandais s'il y avait un moyen de le faire. ceci dans un fichier.

Le comportement des lignes Shebang diffère d’un système à l’autre - du moins dans Cygwin , il ne scinde pas les arguments par des espaces. Je me soucie juste de la façon de le faire sur un système qui se comporte comme ça; le script n'est pas conçu pour être portable.

106
Hans-Peter Störr

Cela semble fonctionner pour moi avec (g) awk.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

Notez que #! exécute /bin/sh, de sorte que ce script est d'abord interprété comme un script Shell.

Au début, j’ai simplement essayé "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", mais awk a traité cela comme une commande et a imprimé chaque ligne d’entrée sans condition. C'est pourquoi j'ai mis le arbitrary_long_name==0 - il est supposé échouer tout le temps. Vous pouvez le remplacer par une chaîne de charabia. En gros, je recherchais une fausse condition dans awk qui n’affecterait pas le script Shell.

Dans le script Shell, le arbitrary_long_name==0 définit une variable appelée arbitrary_long_name et la définit égale à =0.

21
Aaron McDaid

La ligne Shebang n'a jamais été spécifiée dans le cadre de POSIX, SUS, LSB ou de toute autre spécification. Autant que je sache, il n'a même pas été correctement documenté.

Il y a un consensus approximatif sur ce qu'il fait: prend tout entre le ! et le \n et le exec le. L'hypothèse est que tout ce qui se situe entre ! et \n est un chemin absolu complet vers l'interpréteur. Il n'y a pas de consensus sur ce qui se passe s'il contient des espaces.

  1. Certains systèmes d'exploitation traitent simplement le tout comme le chemin. Après tout, dans la plupart des systèmes d'exploitation, les espaces ou les tirets sont légaux dans un chemin.
  2. Certains systèmes d'exploitation se divisent en espaces et traitent la première partie comme le chemin de l'interpréteur et les autres comme des arguments individuels.
  3. Certains systèmes d'exploitation se divisent à l'espace blanc first et traitent la partie avant comme le chemin d'accès à l'interpètre et le reste sous la forme d'un argument single (ce que vous voyez).
  4. Certains ne supportent même pas les lignes Shebang du tout.

Heureusement, 1. et 4. semblent avoir disparu, mais 3. est assez répandu, vous ne pouvez donc pas simplement compter sur la capacité de passer plus d'un argument.

De plus, l'emplacement des commandes n'étant pas spécifié dans POSIX ni dans SUS, vous utilisez généralement cet argument unique en transmettant le name de l'exécutable à env afin que it puisse déterminer l'emplacement de l'exécutable; par exemple.:

#!/usr/bin/env gawk

[Évidemment, ceci still suppose un chemin particulier pour env, mais il n’ya que très peu de systèmes dans lesquels il se trouve dans /bin, ce qui est donc généralement sûr. L'emplacement de env est beaucoup plus standardisé que l'emplacement de gawk ou même pire, quelque chose comme python ou Ruby ou spidermonkey.]

Ce qui signifie que vous ne pouvez pas utiliser aucun arguments du tout.

148
Jörg W Mittag

Je suis tombé sur le même problème, sans solution apparente à cause de la façon dont les espaces blancs sont traités dans un Shebang (au moins sous Linux).

Cependant, vous pouvez passer plusieurs options dans un Shebang, à condition qu'elles soient options courtes et qu'elles puissent être concaténées (la manière GNU).

Par exemple, vous ne pouvez pas avoir

#!/usr/bin/foo -i -f

mais vous pouvez avoir

#!/usr/bin/foo -if

Évidemment, cela ne fonctionne que lorsque les options ont des équivalents courts et ne prennent aucun argument.

11
ℝaphink

Sous Cygwin et Linux, tout ce qui suit le chemin du Shebang est analysé comme un argument au programme.

Il est possible de contourner cela en utilisant un autre script awk à l'intérieur du Shebang:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

Ceci exécutera {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} dans awk.
Et ceci exécutera /usr/bin/gawk --re-interval -f path/to/your/script.awk dans votre système Shell.

11
Moritz
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Le tour ci-dessus de Shell Shebang est plus portable que /usr/bin/env.

5
user3123730

Dans le manuel de gawk (http://www.gnu.org/manual/gawk/gawk.html), à la fin de la section 1.14, vous devez utiliser un seul argument lorsque vous exécutez gawk à partir d'une ligne Shebang. Il dit que le système d'exploitation traitera tout ce qui suit le chemin de gawk comme un argument simple. Peut-être existe-t-il un autre moyen de spécifier l'option --re-interval? Peut-être que votre script peut faire référence à votre shell dans la ligne Shebang, exécuter gawk en tant que commande et inclure le texte de votre script en tant que "document ici".

3
bta

Pourquoi ne pas utiliser bash et gawk lui-même, pour ignorer Shebang, lire le script et le transmettre en tant que fichier à une seconde instance de gawk [--with-whatever-number-of-params-you-need]

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(-la même chose pourrait naturellement aussi être accomplie avec, par exemple, sed ou tail, mais je pense qu’il ya une sorte de beauté qui dépend uniquement de bash et gawk elle-même;)

3
conny

Bien que n'étant pas exactement portable, à partir de coreutils 8.30 et selon sa documentation vous pourrez utiliser:

#!/usr/bin/env -S command arg1 arg2 ...

Donc, étant donné:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

tu auras:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

et si vous êtes curieux, showargs est:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Réponse originale ici .

3
unode

Juste pour le plaisir: il existe la solution assez étrange suivante qui redirige stdin et le programme à travers les descripteurs de fichier 3 et 4. Vous pouvez également créer un fichier temporaire pour le script.

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

Une chose est agaçante à ce sujet: le shell développe des variables variables sur le script. Vous devez donc indiquer chaque $ (comme dans la deuxième ligne du script) et probablement plus que cela.

0
Hans-Peter Störr