it-swarm.com.de

Wie verwende ich mehrere Argumente für awk mit einem Shebang (d. H. #!)?

Ich möchte ein gawk script mit --re-interval mit einem Shebang ausführen. Der "naive" Ansatz von 

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

funktioniert nicht, da gawk mit dem ersten Argument "--re-interval -f" (nicht um den Whitespace getrennt) aufgerufen wird, das es nicht versteht. Gibt es eine Problemumgehung dafür?

Natürlich können Sie gawk nicht direkt aufrufen, sondern in ein Shell-Skript einbetten, das das erste Argument aufteilt, oder ein Shell-Skript erstellen, das dann gawk aufruft und das Skript in eine andere Datei schreibt dies innerhalb einer Datei.

Das Verhalten von Shebang-Linien unterscheidet sich von System zu System - zumindest in Cygwin es werden die Argumente nicht nach Whitespaces aufgeteilt. Ich kümmere mich nur darum, wie man dies auf einem System macht, das sich so verhält. Das Skript soll nicht portabel sein.

106

Das scheint bei mir mit (g) awk zu funktionieren.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "[email protected]"


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

Beachten Sie, dass #! ausgeführt wird /bin/sh, daher wird dieses Skript zuerst als Shell-Skript interpretiert.

Zuerst versuchte ich einfach "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "[email protected]", aber awk behandelte das als Befehl und druckte jede Zeile der Eingabe unbedingt aus. Deshalb füge ich den arbitrary_long_name==0 ein - er soll immer scheitern. Sie könnten es durch einige Kauderwelsche ersetzen. Grundsätzlich suchte ich nach einer falschen Bedingung in awk, die das Shell-Skript nicht beeinträchtigen würde.

Im Shell-Skript definiert arbitrary_long_name==0 eine Variable mit dem Namen arbitrary_long_name und setzt sie gleich =0.

21
Aaron McDaid

Die Shebang-Linie wurde nie als Teil von POSIX, SUS, LSB oder einer anderen Spezifikation angegeben. AFAIK, es wurde nicht einmal richtig dokumentiert.

Es gibt einen groben Konsens darüber, was es tut: Nimm alles zwischen dem ! und dem \n und der exec. Die Annahme ist, dass alles zwischen dem ! und dem \n ein vollständiger absoluter Pfad zum Interpreter ist. Es besteht kein Konsens darüber, was passiert, wenn es Leerzeichen enthält.

  1. Einige Betriebssysteme behandeln das Ganze einfach als Pfad. In den meisten Betriebssystemen sind Whitespace oder Bindestriche in einem Pfad zulässig.
  2. Einige Betriebssysteme teilen sich bei Whitespace auf und behandeln den ersten Teil als Pfad zum Interpreter und den Rest als individuelle Argumente.
  3. Einige Betriebssysteme teilen sich im first whitespace auf und behandeln den vorderen Teil als Pfad zum Interpeter und den Rest als ein single -Argument (das ist das, was Sie sehen).
  4. Einige unterstützen sogar keine Shebang-Linien überhaupt .

Glücklicherweise scheinen 1. und 4. ausgestorben zu sein, aber 3. ist ziemlich weit verbreitet, so dass Sie sich einfach nicht darauf verlassen können, dass Sie mehr als ein Argument führen können.

Und da der Ort von Befehlen auch nicht in POSIX oder SUS angegeben ist, verbrauchen Sie dieses einzelne Argument im Allgemeinen, indem Sie die ausführbare Datei name an env übergeben, sodass it die Position der ausführbaren Datei bestimmen kann. z.B.:

#!/usr/bin/env gawk

[Offensichtlich nimmt dieses noch einen bestimmten Pfad für env an, aber es gibt nur sehr wenige Systeme, in denen es in /bin lebt, daher ist dies im Allgemeinen sicher. Die Position von env ist viel standardisierter als die Position von gawk oder noch schlimmer, etwa python oder Ruby oder spidermonkey.]

Was bedeutet, dass Sie any arguments überhaupt nicht verwenden können .

148
Jörg W Mittag

Ich bin auf das gleiche Problem gestoßen, ohne eine offensichtliche Lösung zu finden, da die Whitespaces in einem Shebang (zumindest unter Linux) behandelt werden.

Sie können jedoch mehrere Optionen in einem Shebang übergeben, sofern es sich um short-Optionen handelt und sie verkettet sein können (der Weg GNU).

Zum Beispiel können Sie nicht haben

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

aber du kannst haben

#!/usr/bin/foo -if

Das funktioniert natürlich nur, wenn die Optionen kurze Äquivalente haben und keine Argumente enthalten.

11
ℝaphink

Unter Cygwin und Linux wird alles nach dem Pfad des Shebang als ein Argument in das Programm geparst.

Es ist möglich, dies zu umgehen, indem Sie ein anderes awk-Skript im Shebang verwenden:

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

Dies führt {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} in awk aus.
Und dies führt /usr/bin/gawk --re-interval -f path/to/your/script.awk in Ihrer System-Shell aus.

11
Moritz
#!/bin/sh
''':'
exec YourProg -some_options "$0" "[email protected]"
'''

Der obige Shell Shebang-Trick ist tragbarer als /usr/bin/env.

5
user3123730

Im gawk-Handbuch (http://www.gnu.org/manual/gawk/gawk.html) wird am Ende von Abschnitt 1.14 darauf hingewiesen, dass Sie nur ein einzelnes Argument verwenden sollten, wenn Sie gawk von einer Shebang-Linie aus ausführen. Es besagt, dass das Betriebssystem alles nach dem Pfad zum Gawk als ein einziges Argument behandeln wird. Vielleicht gibt es eine andere Möglichkeit, die --re-interval-Option anzugeben? Vielleicht kann Ihr Skript Ihre Shell in der Shebang-Zeile referenzieren, gawk als Befehl ausführen und den Text Ihres Skripts als "Here document" einfügen.

3
bta

Verwenden Sie bash und gawk selbst, um Shebang zu überspringen, das Skript zu lesen und es als Datei an eine zweite Instanz von gawk [--with-whatever-number-of-params-you-need] zu übergeben. 

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

(Das gleiche könnte natürlich auch mit sed oder tail durchgeführt werden, aber ich denke, es gibt eine Art Schönheit, die nur von bash und gawk selbst abhängt;).

3
conny

Obwohl nicht genau portabel, beginnend mit coreutils 8.30 und laut Dokumentation können Sie Folgendes verwenden:

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

So gegeben:

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

sie erhalten:

% ./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'

und falls Sie neugierig sind, showargs ist:

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

i=1
for arg in "[email protected]"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Ursprüngliche Antwort hier .

3
unode

Nur zum Spaß: Es gibt die folgende ziemlich seltsame Lösung, die stdin und das Programm durch die Dateideskriptoren 3 und 4 umleitet. Sie können auch eine temporäre Datei für das Skript erstellen.

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

Ärgerlich dabei ist: Die Shell erweitert das Skript variabel, so dass Sie jedes $ (wie in der zweiten Zeile des Skripts) und wahrscheinlich mehr als das zitieren müssen.

0