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

118

Ich möchte ein Gawk- Skript mit --re-intervaleinem 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 aufgerufen wird "--re-interval -f"(nicht um das Leerzeichen aufgeteilt), das es nicht versteht. Gibt es dafür eine Problemumgehung?

Natürlich können Sie gawk entweder nicht direkt aufrufen, sondern in ein Shell-Skript einbinden, das das erste Argument aufteilt, oder ein Shell-Skript erstellen, das dann gawk aufruft und das Skript in eine andere Datei einfügt, aber ich habe mich gefragt, ob es eine Möglichkeit gibt, dies zu tun dies innerhalb einer Datei.

Das Verhalten von Shebang-Linien unterscheidet sich von System zu System - zumindest in Cygwin werden die Argumente nicht durch Leerzeichen aufgeteilt. Mir ist nur wichtig, wie es auf einem System gemacht wird, das sich so verhält. Das Skript soll nicht portabel sein.

Hans-Peter Störr
quelle
1
Ein dummes Experiment, das ich gerade gemacht habe, war mit einem Skript, das ein anderes Skript in der Shebang-Zeile verwendet, wodurch die Argumente korrekt aufgeteilt wurden.
Hasturkun
@Hasturkun, das wirft ein weiteres Problem auf, dass das Verhalten von Shebang-Zeilen auch von System zu System unterschiedlich ist, unabhängig davon, ob das aufgerufene Programm selbst ein Skript sein kann.
Dubiousjim
Mit neueren Versionen von gawk (> = 4.0) wird --re-intervales nicht mehr benötigt (siehe [ gnu.org/software/gawk/manual/… ).

Antworten:

25

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

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


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

Beachten Sie die #!Läufe /bin/sh, sodass dieses Skript zuerst als Shell-Skript interpretiert wird.

Zuerst habe ich es einfach versucht "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", aber awk hat das als Befehl behandelt und jede Eingabezeile bedingungslos ausgedruckt. Deshalb habe ich das eingegeben arbitrary_long_name==0- es soll die ganze Zeit scheitern. Sie könnten es durch eine Kauderwelschschnur ersetzen. Grundsätzlich suchte ich nach einer falschen Bedingung in awk, die das Shell-Skript nicht beeinträchtigen würde.

Im Shell-Skript arbitrary_long_name==0definiert das eine aufgerufene Variable arbitrary_long_nameund setzt sie gleich =0.

Aaron McDaid
quelle
Dies ist meine Antwort, aber ich frage mich, ob es ausreichend tragbar und robust ist. Kommt es speziell auf das bashan oder funktioniert es mit jedem POSIX sh? Und ich benutze es nicht awkoft, daher bin ich mir nicht sicher, ob mein Trick in der zweiten Zeile ein guter Weg ist, um awkdas Ignorieren der Zeile zu erzwingen .
Aaron McDaid
Genau das, was ich mich gefragt habe, +1, aber wahrscheinlich nicht ratsam (daher die relativen Stimmen).
Aaron Hall
Können Sie erklären, welche Probleme dies haben könnte, @AaronHall? Solange die Variable arbitrary_long_namenicht mit einer im realen awk-Programm verwendeten Variablen kollidiert, kann ich kein Problem erkennen. Fehlt mir etwas?
Aaron McDaid
Verwenden Sie diese #!/bin/sh -Option #!/bin/sh, um das Skript vor möglicherweise gefährlichem Fehlverhalten zu schützen, wenn es mit einem nullten Argument aufgerufen wird, das -das erste Zeichen enthält. Dies kann versehentlich in Programmiersprachen wie C passieren, wo es leicht zu Versehen kommt, wenn man vergisst, den aufgerufenen Programmnamen als Teil des Argumentarrays an execveähnliche Funktionen zu übergeben, und wenn die Leute gewöhnlich vergessen, sich dagegen zu schützen, kann dies auch passieren Dies ist der letzte Schritt in einer böswillig ausnutzbaren Sicherheitsanfälligkeit, mit der ein Angreifer eine interaktive Shell erhalten kann.
mtraceur
161

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

Es besteht ein grober Konsens darüber, was es tut: Nehmen Sie alles zwischen das !und das \nund execes. Die Annahme ist, dass alles zwischen dem !und dem \nein vollständiger absoluter Pfad zum Interpreten ist. Es besteht kein Konsens darüber, was passiert, wenn es Leerzeichen enthält.

  1. Einige Betriebssysteme behandeln einfach das Ganze als Pfad. Schließlich sind in den meisten Betriebssystemen Leerzeichen oder Bindestriche in einem Pfad zulässig.
  2. Einige Betriebssysteme teilen sich im Leerzeichen auf und behandeln den ersten Teil als Pfad zum Interpreter und den Rest als einzelne Argumente.
  3. Einige Betriebssysteme teilen sich beim ersten Leerzeichen und behandeln den vorderen Teil als Pfad zum Interpeter und den Rest als ein einziges Argument (was Sie sehen).
  4. Einige unterstützen Shebang-Linien überhaupt nicht .

Zum Glück scheinen 1. und 4. ausgestorben zu sein, aber 3. ist ziemlich weit verbreitet, sodass Sie sich einfach nicht darauf verlassen können, mehr als ein Argument vorbringen zu können.

Und da die Lage der Befehle ist auch in POSIX oder SUS nicht angegeben ist , verwenden Sie in der Regel , dass einzelne Argument, indem sie die ausführbaren vorbei Namen zu , envso dass es die ausführbare Standort bestimmen können; z.B:

#!/usr/bin/env gawk

[Offensichtlich nimmt dies immer noch einen bestimmten Weg an env, aber es gibt nur sehr wenige Systeme, in denen es lebt /bin, so dass dies im Allgemeinen sicher ist. Der Standort von envist viel standardisierter als der Standort von gawkoder noch schlimmer etwas wie pythonoder rubyoder spidermonkey.]

Das bedeutet, dass Sie überhaupt keine Argumente verwenden können .

Jörg W Mittag
quelle
1
FreeBSDs env hat einen -SSchalter, der hier hilft, aber er ist unter meinem Linux nicht vorhanden env, und ich vermute, dass er auch auf gygwin nicht verfügbar ist. @hstoerr, andere Benutzer mit unterschiedlichen Situationen lesen Ihre Fragen möglicherweise später. Daher sind tragbare Antworten im Allgemeinen vorzuziehen, auch wenn Sie jetzt keine Portabilität benötigen.
Dubiousjim
4
Wir können also keine Argumente in einem Schebang verwenden. Aber was ist, wenn wir Argumente brauchen, die notwendig sind? Ich vermute, dass die Lösung darin besteht, ein Wrapper-Shell-Skript zu schreiben, das #!/bin/shund enthält /usr/bin/env gawk --re-interval -f my-script.awk. Ist das korrekt?
Rory O'Kane
1
Ich stimme nicht zu. Sie können ein Argument ganz portabel verwenden. Jedes System, in dem Sie keine Argumente verwenden können, kann diesen traditionellen Unixismus, der Hash-Bang ist, kläglich nicht implementieren. Wenn Nicht-Implementierungen ein faires Spiel sind, können wir mit Sicherheit sagen, dass sie #!selbst nicht portabel sind. Beispielsweise erkennt Windows diese Konvention "nativ" überhaupt nicht. Unter Unix ist traditionell ein Ein-Argument-Knall erforderlich, um dies zu tun #!/usr/bin/awk -f.
Kaz
7
@Kaz: Ja, aber da die Pfade vieler Binärdateien nicht standardisiert sind, verbrauchen Sie Ihr einziges Argument für #!/usr/bin/env rubyoder dergleichen.
Jörg W Mittag
3
@Pacerier: Ändern Sie die POSIX-Spezifikation und warten Sie 20 bis 30 Jahre, bis alle Systeme aktualisiert wurden, um der Spezifikation zu entsprechen.
Jörg W Mittag
18

Obwohl nicht gerade portabel, können Sie ab Coreutils 8.30 und gemäß der Dokumentation 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, showargsist:

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

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

Ursprüngliche Antwort hier .

unode
quelle
1
Zu Ihrer Information, FreeBSD hat -S seit Jahren (seit 6.0). Dies ist eine willkommene Portabilitätsergänzung zu Coreutils.
Juan
12

Ich bin auf dasselbe Problem gestoßen, ohne offensichtliche Lösung, da die Leerzeichen in einem Shebang behandelt werden (zumindest unter Linux).

Sie können jedoch mehrere Optionen in einem Shebang übergeben, sofern es sich um kurze Optionen handelt und diese verkettet werden können (nach GNU-Art).

Zum Beispiel können Sie nicht haben

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

aber du kannst haben

#!/usr/bin/foo -if

Dies funktioniert natürlich nur, wenn die Optionen kurze Entsprechungen haben und keine Argumente enthalten.

ℝaphink
quelle
11

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

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

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

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

Moritz
quelle
2
Dies wird nicht funktionieren, wenn Sie Argumente an das Skript übergeben haben
Steven Penny
4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

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

user3123730
quelle
Das '' ':' 'ist ein Überbleibsel, da meine ursprüngliche Lösung für ein Python-Skript war. Das' '': 'weist den Python-Interpreter an, den Exec-Teil zu ignorieren.
user3123730
4
Ich denke, Sie werden abgelehnt, weil Ihre Lösung dafür ist python, aber bei dieser Frage geht es um awk.
Aaron McDaid
1
Toller Hack für Python.
Zaar Hai
3

Im Gawk-Handbuch (http://www.gnu.org/manual/gawk/gawk.html) wird am Ende von Abschnitt 1.14 darauf hingewiesen, dass Sie nur ein einziges Argument verwenden sollten, wenn Sie Gawk von einer Shebang-Linie aus ausführen. Es heißt, dass das Betriebssystem alles nach dem Weg zum Gaffen als ein einziges Argument behandeln wird. Vielleicht gibt es eine andere Möglichkeit, die --re-intervalOption anzugeben ? Vielleicht kann Ihr Skript auf Ihre Shell in der Shebang-Zeile verweisen, gawkals Befehl ausgeführt werden und den Text Ihres Skripts als "Hier-Dokument" einfügen.

bta
quelle
Es scheint, dass es keine andere Möglichkeit gibt, die Option anzugeben. Sie haben Recht: gawk -f - << EOF, einige Skriptzeilen, EOF funktioniert, aber es hindert mich daran, die Standardeingabe mit gawk zu lesen.
Hans-Peter Störr
Das hier gezeigte Dokument verbraucht den Standardeingabestream für gawk, aber Sie können möglicherweise immer noch etwas über stderr einleiten (dh stdout zu stderr umleiten, bevor Sie in dieses Skript weiterleiten). Ich habe das noch nie versucht, aber solange der erste Prozess auf stderr nichts aussendet, könnte es funktionieren. Sie können auch eine Named Pipe erstellen ( linuxjournal.com/content/using-named-pipes-fifos-bash ), wenn Sie sicherstellen möchten, dass nichts anderes sie verwendet.
bta
3

Warum nicht bashund sich gawkselbst verwenden, um Shebang zu überspringen, das Skript zu lesen und es als Datei an eine zweite Instanz von zu übergeben 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
}

(-das gleiche könnte natürlich auch mit zB sedoder erreicht werden tail, aber ich denke, es gibt eine Art von Schönheit, die nur von bashund von sich gawkselbst abhängt;)

conny
quelle
0

Nur zum Spaß: Es gibt die folgende ziemlich seltsame Lösung, die stdin und das Programm über 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

Eines ist daran ärgerlich: Die Shell führt eine variable Erweiterung des Skripts durch, sodass Sie jedes $ (wie in der zweiten Zeile des Skripts angegeben) und wahrscheinlich mehr angeben müssen.

Hans-Peter Störr
quelle
-1

Für eine tragbare Lösung verwenden , awkanstatt gawk, rufen Sie den Standard - Bourne - Shell ( /bin/sh) mit Ihrem shebang, und rufen Sie awkdas Programm auf der Kommandozeile vorbei als hier direkt dokumentieren , anstatt über stdin:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

Hinweis: kein -fArgument zu awk. Damit stdinstehen awkEingaben zum Lesen zur Verfügung. Angenommen, Sie haben gawkund auf Ihrem installiert PATH, erreicht dies alles, was Sie meiner Meinung nach mit Ihrem ursprünglichen Beispiel versucht haben (vorausgesetzt, Sie wollten, dass der Dateiinhalt das awk-Skript und nicht die Eingabe ist, was Ihr Shebang-Ansatz meiner Meinung nach behandelt hätte ).

lharper71
quelle
3
Das hat bei mir nicht funktioniert. Der Bash-Mann sagt, <<< blabla setzt blabla auf stdin. Meinten Sie << - EOF? So oder so, das bringt das Programm auch auf stdin.
Hans-Peter Störr