Ist es Shell-portabel, einen Befehl nach der Variablenzuweisung in derselben Zeile auszuführen?

7

Gibt es einen Standard, der die Portabilität der Ausführung eines Befehls nach der Variablenzuweisung in derselben Zeile abdeckt?

APPLE="cider" echo hi

Wie tragbar ist so etwas? Wo wird es funktionieren und wo nicht?

Außerdem: Meine Shell-Skripte beginnen mit #! / Bin / sh, wenn dies einen Unterschied macht.

Prüfung
quelle

Antworten:

6

Solange Sie eine POSIX-kompatible Shell verwenden, ja.

Aus der POSIX-Definition der Shell-Befehlssprache : (relevante Punkte in Fettdruck)

Ein "einfacher Befehl" ist eine Folge von optionalen Variablenzuweisungen und -umleitungen in beliebiger Reihenfolge, optional gefolgt von Wörtern und Umleitungen, die von einem Steuerungsoperator beendet werden.

Wenn ein bestimmter einfacher Befehl ausgeführt werden muss (dh wenn ein bedingtes Konstrukt wie eine UND-ODER-Liste oder eine case-Anweisung den einfachen Befehl nicht umgangen hat), werden alle folgenden Erweiterungen, Zuweisungen und Umleitungen ausgeführt der Anfang des Befehlstextes bis zum Ende:

  1. Die Wörter, die gemäß den Shell-Grammatikregeln als Variablenzuweisungen oder -umleitungen erkannt werden, werden zur Verarbeitung in den Schritten 3 und 4 gespeichert.

  2. Die Wörter, die keine variablen Zuweisungen oder Umleitungen sind, werden erweitert. Wenn nach ihrer Erweiterung noch Felder vorhanden sind, wird das erste Feld als Befehlsname betrachtet, und die verbleibenden Felder sind die Argumente für den Befehl.

  3. Umleitungen müssen wie unter Umleitung beschrieben durchgeführt werden .

  4. Jede Variablenzuweisung muss vor dem Zuweisen des Werts für Tildeerweiterung, Parametererweiterung, Befehlssubstitution, arithmetische Erweiterung und Anführungszeichenentfernung erweitert werden.

In der vorhergehenden Liste kann die Reihenfolge der Schritte 3 und 4 für die Verarbeitung spezieller integrierter Dienstprogramme umgekehrt werden. Siehe Spezielle integrierte Dienstprogramme .

Wenn kein Befehlsname angezeigt wird, wirken sich Variablenzuweisungen auf die aktuelle Ausführungsumgebung aus. Andernfalls werden die Variablenzuweisungen für die Ausführungsumgebung des Befehls exportiert und wirken sich nicht auf die aktuelle Ausführungsumgebung aus (außer für spezielle integrierte Funktionen).


Auch ja ist #!/bin/shwichtig. Aus der POSIX-Definition vonsh :

Das Dienstprogramm sh ist ein Befehlsspracheninterpreter, der Befehle ausführen soll, die aus einer Befehlszeilenzeichenfolge, der Standardeingabe oder einer angegebenen Datei gelesen werden. Die Anwendung muss sicherstellen, dass die auszuführenden Befehle in der in Shell Command Language beschriebenen Sprache ausgedrückt werden .

Im Grunde heißt es also, dass shdie oben genannten Regeln eingehalten werden müssen.

Solange Sie ein POSIX-kompatibles Betriebssystem verwenden, sind Sie gut.

Patrick
quelle
Ein kleiner Kommentar zu Ihrem letzten Punkt. Während POSIX shdie Einhaltung des Standards vorschreibt, gibt es nicht an, welcher Pfad shvorhanden sein muss. Dies /bin/shist nicht unbedingt die POSIX-Shell und auf einigen Plattformen (heutzutage im Wesentlichen Solaris 10 und älter) die alte Bourne-Shell. Dies wird in diesem speziellen Fall der Variablenzuweisung nicht schaden, da diese Syntax lange vor dem POSIX-Standard liegt.
Jlliagre
Es schreibt nicht vor, dass es bei /bin/shNr. Aber es schreibt vor, wie shman sich verhalten muss. Das ist alles was zählt. Sie können dort nichts anderes anbringen shund POSIX-konform sein.
Patrick
Das ist falsch. Ein POSIX-kompatibles Betriebssystem kann ein nicht POSIX-kompatibles Betriebssystem bereitstellen /bin/sh. Bei Verwendung eines kompatiblen Pfads, dh beim PATH=$(getconf PATH)Ausführen, shwird die POSIX-kompatible Shell aufgerufen.
Jlliagre
Danke Patrick. Um auf das einzugehen, was @jlliagre gesagt hat, habe ich gelesen, dass / bin / sh möglicherweise eine bourne-kompatible Shell ist, aber nicht unbedingt POSIX-kompatibel? Ist das wahr? Ich habe die Erbstück-Shell (eine Portierung der ursprünglichen Bourne-Shell) installiert und sie mit meinem Beispiel einwandfrei getestet. Ich habe zsh auch unter Ubuntu getestet, um zu sehen, was passieren würde, und ich habe keinen Fehler wie in lluas Beispiel erhalten.
Test
1
@test Wie ich in meinem früheren Kommentar geschrieben habe, ist die var=x commandSyntax älter als POSIX, sodass es in Ihrem Fall kein Problem gibt, eine nicht POSIX-basierte Bourne-basierte Shell zu verwenden.
Jlliagre
3

Um die zweite Frage unvollständig zu beantworten (die Anzahl der Muscheln ist umwerfend )

% for sh in dash ksh93 mksh rc fish bash zsh csh tcsh jsh; do
printf '%s\n' "$sh: $($sh -c 'var=foo echo hi')"
done

dash: hi
ksh93: hi
mksh: hi
rc: hi
fish: Unknown command var=foo”. Did you mean set var foo”? For information on assigning values to variables, see the help section on the set command by typing help set
Standard input: var=foo echo hi
                ^
fish:
bash: hi
zsh: hi
var=foo: Command not found.
csh:
var=foo: Command not found.
tcsh:
jsh: hi

In den meisten modernen Korn-ähnlichen Muscheln wird es funktionieren. Aber ich denke nicht, dass es ein Standard ist, ich bin sicher, dass jemand wie Stephane Chazelas wissen würde, wie häufig es ist.

llua
quelle
Diese Syntax wird seit den Anfängen der Bourne Shell im Jahr 1976 verwendet. Beachten Sie, dass dies cshkein Standard und zshstandardmäßig kein Standard ist.
schily
2

Mit dem Befehl in Ihrem Beispiel echowird ausgeführt, aber was passiert, $APPLEist etwas komplizierter.

Es ist wahr, wie in der Antwort von @ Patrick hier angegeben, dass, wenn die Shell einen Prozess aufruft, alle Variablen, die in der Befehlszeile vor ihrem Aufruf deklariert wurden, angegeben werden, um in ihre Umgebung exportiert zu werden. Außerdem werden diese Variablen so angegeben, dass sie mit dem aufgerufenen Prozess ablaufen - also ...

unset var; var=val cmd; echo ${var-unset.}

... sollte drucken unset.

Aber wo dieses ganze Konzept etwas komplizierter wird und wie Ihr eigenes Beispiel zeigt, ist es an dem Punkt, an dem cmdes sich nicht um einen aufgerufenen Prozess handelt, sondern entweder um ein Shell-Builtin, eine Shell-Funktion oder ein spezielles Shell-Builtin-Dienstprogramm. In jedem dieser drei Fälle führt die Shell höchstwahrscheinlich nur einige ihrer eigenen Routinen im Speicher aus und ruft überhaupt nichts auf.

Zum Beispiel echoist es fast definitiv ein Shell-eingebautes Dienstprogramm - wie die meisten mir bekannten Shells es als solches bereitstellen -, aber es ist kein POSIX-spezifiziertes spezielles eingebautes Dienstprogramm. Auf diese Weise ist es im Grunde eine Shell-Funktion, die eine externe ausführbare Datei emulieren muss. Dies wird hier wahrscheinlich etwas deutlicher ausgedrückt:

Der Begriff "integriert" impliziert, dass die Shell das Dienstprogramm direkt ausführen kann und nicht danach suchen muss. Eine Implementierung kann sich dafür entscheiden, jedes Dienstprogramm zu einem integrierten Dienstprogramm zu machen. Die hier beschriebenen speziellen integrierten Dienstprogramme unterscheiden sich jedoch von den regulären integrierten Dienstprogrammen ...

Mit speziellen integrierten Dienstprogrammen angegebene Variablenzuweisungen bleiben auch nach Abschluss des integrierten Dienstprogramms wirksam. Dies ist bei einem regulären eingebauten oder anderen Dienstprogramm nicht der Fall.

Die speziellen integrierten Dienstprogramme in diesem Abschnitt müssen nicht auf eine Weise bereitgestellt werden, auf die über die im System Interfaces-Volume von POSIX.1-2008 definierte exec-Funktionsfamilie zugegriffen werden kann.

Eine in echoder Befehlszeile deklarierte Variable geht also verloren echo, aber eine in setder Befehlszeile deklarierte Variable bleibt bestehen - obwohl sie bashstandardmäßig gegen diese Regel verstößt) . Gleiches gilt, wenn cmdes sich um eine Shell- Funktion handelt :

Wenn eine Funktion ausgeführt wird, muss sie die Eigenschaften für Syntaxfehler und Variablenzuweisung aufweisen, die für spezielle integrierte Dienstprogramme in der Aufzählungsliste am Anfang der speziellen integrierten Dienstprogramme beschrieben sind.

mikeserv
quelle