So „erweitern“ Sie eine Bash-Variable (der enthaltene Code funktioniert für Bash, aber nicht für Zsh)

So „erweitern“ Sie eine Bash-Variable (der enthaltene Code funktioniert für Bash, aber nicht für Zsh)

Eine Antwort wieDaserklärt gut, wie man das Passspiel kontrolliertalleVariablen bis hin zu einem Befehl.

Ich wollte herausfinden, wie dies für jedes Argument einzeln durchgeführt werden kann. Beachten Sie (dies wurde in getestet zsh):

$ program() { echo 1: $1 2: $2 3: $3; }

$ run() { program "$@"; }

$ run2() { echo `run $1`; }

$ run2 'a b' c
1: a b 2: 3:

Ich möchte einen Weg finden, ein erstes Argument zu übergeben, das eine Zeichenfolge mit Leerzeichen ist, und diesen gespleißten $@Stil in einen anderen Befehl zu übernehmen. run2 "a b" csollte beispielsweise Folgendes erzeugen :1: a 2: b 3:

An diesem Punkt habe ich mein unmittelbares Problem gelöst, denn obwohl mein Testcode beim Testen in zsh abbricht, funktioniert er, sobald er in ein echtes Bash-Skript implementiert wurde.

Dies deutet darauf hin, dass dies möglicherweise auf einigen Feinheiten bei der Verarbeitung von Zeichenfolgen und Argumenten beruht, die nicht „sicher“ sind. Dies ist also eher eine Bitte um Kommentare zu einer robusteren Methode, um dieses Verhalten zuverlässig zu erreichen.

Antwort1

Der Unterschied zwischen Bash und Zsh, der hier von Bedeutung ist, liegt in der Art und Weise run2der Aufrufe run, insbesondere in der Auswirkung, wenn $1Anführungszeichen weggelassen werden.

  • In zsh run $1wird der Operator „remove-if-empty“ auf angewendet $1, d. h. ruft runmit dem ersten Argument auf, das an übergeben wurde run2, außer dass, wenn das erste Argument an run2leer war (oder wenn run2ohne Argument aufgerufen wurde), dann runohne Argument aufgerufen wird.
  • In anderen Shells im Bourne-Stil, wie z. B. Bash, run $1wird der Operator „split+glob“ auf angewendet $1, d. h., es wird das erste Argument run2in durch Leerzeichen getrennte Blöcke¹ aufgeteilt, jedes Stück wird als Platzhaltermuster² interpretiert und jedes Platzhaltermuster, das mit einer oder mehreren Dateien übereinstimmt, durch die Liste der Übereinstimmungen ersetzt.

Daher run2 'a b' cwerden Aufrufe runmit dem Argument a bin zsh durchgeführt (das Argument wird unverändert übergeben), Aufrufe runmit den beiden Argumenten aund bin bash jedoch (aufgeteilt in durch Leerzeichen getrennte Teile).

Ich möchte einen Weg finden, ein erstes Argument zu übergeben, das eine Zeichenfolge mit Leerzeichen ist, und diesen gespleißten $@Stil in einen anderen Befehl zu übernehmen. run2 "a b" csollte beispielsweise Folgendes erzeugen :1: a 2: b 3:

Ihre Beschreibung und Ihr Beispiel sagen etwas anderes aus. Wenn Sie das erste Argument an den anderen Befehl übergeben möchten, verwenden Sie , run "$1"um sicherzustellen, dass das Argument nicht aufgeteilt wird. Das Übergeben unveränderter Argumente ist der ganze Sinn von "$@".

Es scheint, dass Sie eigentlich das erste Argument in durch Leerzeichen getrennte Blöcke aufteilen möchten run2. In Bash können Sie dies tun, indem Sie die Platzhaltererweiterung deaktivieren und dann (vorausgesetzt, dies IFShat sich gegenüber der Standardeinstellung nicht geändert) eine Erweiterung ohne Anführungszeichen verwenden.

run2 () ( set -f; run $1; )

( echo "$(somecommand)"ist im Wesentlichen gleichbedeutend mit der Ausführung somecommandin einer Subshell, und es scheint, dass Sie dies gemeint haben und nicht „ echo $(somecommand)which wendet split+glob auf die Ausgabe des Befehls an, daher habe ich die redundante Echo-Befehlsersetzung entfernt.)

In zsh können Sie das =Zeichen in einer Parameterersetzung verwenden, um eine Weltaufteilung (und kein Globbing) für den Wert durchzuführen.

run2 () { run $=1; }

Die Syntax von zsh ist nicht mit der von einfachem sh kompatibel. Wenn Sie ein sh-Skript aus zsh beziehen möchten, können Sie das emulatefolgende Build verwenden:

emulate sh -c '. myscript.sh'

Wird verwendet emulate ksh, um (einige Funktionen von) ksh zu emulieren. Dies emuliert nicht alle Bash-Funktionen, ermöglicht Ihnen jedoch die Verwendung von Arrays.

¹ Allgemeiner gesagt, basierend auf dem Wert von IFS.
² Sofern dies nicht mit deaktiviert wurde set -f.

Antwort2

Willkommen in der Welt des Zitierens und der Zitatüberraschungen.

Das Hauptproblem besteht darin, dass zshstandardmäßig keine Aufteilung bei IFS-Zeichen erfolgt.
Darin unterscheidet es sich von allen anderen Shells.

Um zu testen, wie Anführungszeichen die Funktionsweise von Shells ändern, benötigen wir Code, der sowohl die in Anführungszeichen gesetzten als auch die nicht in Anführungszeichen gesetzten Versionen Ihres Codes testet.

Ihr Code (mit Hinzufügen einiger Variablen):

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa()  { program  "$@" ; }
run1()  { echo "`runa  $1 $2 $3 `"; }
run1    'a b' c

Lassen Sie mich näher auf die Einzelheiten eingehen.

Nehmen wir an, Sie erstellen einen lokalen shLink, der auf den zshWohnort verweist:

ln -s "/usr/bin/zsh" ./sh

Und nehmen wir außerdem an, Sie kopieren das folgende Skript in eine soDatei.

Ein Skript, das jede Funktion mit in Anführungszeichen gesetzten und nicht in Anführungszeichen gesetzten Variablen wiederholt:

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program  $@ ; }

run1() { echo "`runa  "$1" "$2" "$3"`"; }
run2() { echo "`runb  "$1" "$2" "$3"`"; }
run3() { echo "`runa  $1 $2 $3`"; }
run4() { echo "`runb  $1 $2 $3`"; }

for i in `seq 4`; do
    run"$i" 'a b' c
done

Bei der Ausführung wird dann Folgendes ausgedruckt:

# Any shell (except zsh) results.
01-1:    a b   2:      c    3:
02-1:      a   2:      b    3:      c
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

Nur der erste Lauf (run1), bei dem alles in Anführungszeichen steht, bleibt 'a b'verbunden.

Allerdings verhält sich zsh immer so, als ob alles in Anführungszeichen gesetzt worden wäre:

# ZSH results.
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:    a b   2:      c    3:
04-1:    a b   2:      c    3:

zsh in der Emulation.

Es wird angenommen, dass zshältere Shells emuliert werden, wenn es als shoder aufgerufen wird ksh.
In der Praxis trifft dies jedoch nicht immer zu:

$ ./sh ./so   # emulated sh
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

Die zweite Zeile unterscheidet sich von der zweiten Zeile jeder anderen Shell.

Es ist eine gute Idee,Lesen Sie die Antworten auf diese Frage

verwandte Informationen