Wie funktioniert dieser Knall, der mit einem doppelten Bindestrich (-) beginnt?

14

Ich habe auf der RosettaCode-Seite die folgende Art von Shebang gefunden:

--() { :; }; exec db2 -txf "$0"

Es funktioniert für Db2 und ähnlich für Postgres. Ich verstehe jedoch nicht die ganze Zeile.

Ich weiß, dass der doppelte Bindestrich ein Kommentar in SQL ist und danach die ausführbare Datei Db2 mit einigen Parametern aufruft, die die Datei selbst als Datei übergeben. Aber was ist mit der Klammer, den geschweiften Klammern, dem Doppelpunkt und dem Semikolon, und wie kann man ein echtes shebang # ersetzen? ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

AngocA
quelle

Antworten:

18

Verwandte: Welcher Shell-Interpreter führt ein Skript ohne Schebang aus?

Das Skript hat kein shebang / hashbang / #!line, einfach weil ein doppelter Gedankenstrich nicht ist #!.

Das Skript wird jedoch von einer Shell ausgeführt (siehe oben verlinkte Fragen und Antworten). Wenn in dieser Shell -ein gültiges Zeichen in einem Funktionsnamen ist, deklariert die Zeile eine Shell-Funktion, die aufgerufen --wird und nichts tut (nun, sie wird ausgeführt :, was nix tut ) und was nie aufgerufen wird.

Die Funktion in der gebräuchlicheren mehrzeiligen Notation (nur um zu verdeutlichen, wie sie aussieht, da ihr seltsamer Name die Tatsache verdeckt, dass es sich tatsächlich um eine Funktion handelt):

-- () {
  :
}

Der einzige Zweck der Funktionsdefinition besteht darin, eine Zeile zu haben, die in einem Shell-Skript gültig ist und gleichzeitig einen gültigen SQL-Befehl (einen Kommentar) enthält. Diese Art von Code wird als Polyglot bezeichnet .

Nachdem die falsche Shell-Funktion deklariert wurde, ersetzt das Skript, wenn es von einem Shell-Skript-Interpreter ausgeführt wird exec, die aktuelle Shell durch den aus der Ausführung resultierenden Prozess. Dies entspricht der db2 -txf "$0"Verwendung db2 -txfdes Pfadnamens des Skripts über die Befehlszeile.

Dieser Trick würde wahrscheinlich nicht zuverlässig auf Systemen funktionieren, auf denen dashoder auf denen andere ashShells, yashdie Bourne-Shell, ksh88oder ksh93verwendet werden /bin/sh, da diese Shell keine Funktionen akzeptiert, deren Name Bindestriche enthält.

Auch verwandt:


Ich nehme an, dass das Folgende auch funktionieren würde (nicht wirklich getestet):

--() { exec db2 -txf "$0"; }; --
Kusalananda
quelle
@ilkkachu Besser jetzt?
Kusalananda
1
Oh ja! Und danke, dass du mich daran erinnert hast, wie so etwas heißt. :)
ilkkachu
6

Wie @Kusalananda bereits sagte, ist dieser Trick gebrochen und funktioniert nicht in allen Shells.

Hier ist meine Meinung dazu:

--/.. 2>/dev/null; exec db2 -txf "$0"

Der erste Befehl sollte auch dann fehlschlagen, wenn eine Datei / ein Verzeichnis mit dem Namen --im aktuellen Verzeichnis vorhanden ist und alle Fehler vom Befehl 2>/dev/null; Die Shell fährt dann mit dem zweiten Befehl fort, dem exec.

Onkel Billy
quelle
Es ist immer noch nicht wirklich portabel. Es ist kein gültiges Skript und Sie verlassen sich weiterhin auf die aufrufende Shell, um die Tatsache zu umgehen, dass der Kernel die Ausführung des Skripts ablehnt und zurückgibt, ENOEXECwenn Sie dies versuchen. Führen Sie das Skript unter aus, um stracezu sehen, was ich meine.
Kasperd
@kasperd, es sollte immer noch portabel sein, die Shell soll das Skript als Shell-Skript ausführen, wenn exec()es nicht funktioniert. "Wenn die execl () - Funktion aufgrund eines Fehlers, der dem [ENOEXEC] -Fehler entspricht, fehlschlägt, muss die Shell einen Befehl ausführen, der dem Aufrufen einer Shell mit dem Befehlsnamen als erstem Operanden entspricht, ..." (siehe pubs.opengroup) .org / onlinepubs / 9699919799.2018edition / utilities /… )
ilkkachu
@ilkkachu Skripte werden jedoch nicht immer über eine Shell ausgeführt. Wenn Sie versuchen, das Skript in einem anderen Kontext zu verwenden, in dem eine ausführbare Datei funktionieren würde, schlägt dies fehl. Außerdem sind sich die Shells nicht einig, welchen Interpreter sie verwenden sollen. Ihr Skript verhält sich jetzt also anders oder schlägt insgesamt fehl, je nachdem aus welchem ​​Kontext es aufgerufen wird.
Kasperd
@kasperd, na klar, es wird nicht funktionieren, wenn du exec()es direkt von etwas anderem als einer Shell ausführst . Aber was wäre das für ein Fall? Möglicherweise möchten Sie das Skript ausführen cron, aber ich denke, es wird sowieso alles durch eine Shell ausgeführt, und selbst wenn dies nicht der db2 -txf /path/to/scriptFall ist, ist es in diesem Fall einfach, es einfach zu formulieren , da Sie es nur einmal ausführen müssen. Die Kurzarbeit ist vor allem in einer interaktiven Shell nützlich. Sicher ist jedoch, dass ein separates Wrapper-Skript möglicherweise robuster ist.
ilkkachu
1
@kasperd Ich werde Sie nicht mit Dokumenten und Standards ärgern. Probier es einfach! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Onkel Billy