Die Ausgabe der Schleifenvariable zeigt einen anderen Wert als erwartet

Die Ausgabe der Schleifenvariable zeigt einen anderen Wert als erwartet

Stellen Sie sich vor, ich habe zwei Ordner in meinem aktuellen Arbeitsverzeichnis: back/und front/. Ich versuche, in jeden von ihnen zu gelangen und einige Dinge zu tun. Aus dem einfachen Skript unten:

for dir in */ ; do
    echo "$(dir)"
done

Ich habe eine Ausgabe wie die folgende erwartet:

back
front

Was ich jedoch bekomme, ist:

back  front
back  front

Ich verstehe das nicht. Die Konsequenz ist, dass, wenn ich versuche, cdin den Ordner zu gelangen:

for dir in */ ; do
    echo "$(dir)"
    cd $(dir)
    echo "$(pwd)"
    cd ..
done

Ich bekomme:

back  front
/path/to/back
back  front
/path/to/back

D. h. ich gebe nie ein front/. Kann mir bitte jemand helfen zu verstehen, was ich falsch mache?

Antwort1

In POSIX-ähnlichen Shells $(cmd)lautet die Syntax fürBefehlsersetzung, $(cmd)wird auf die Ausgabe cmdabzüglich der nachstehenden Zeilenumbruchzeichen erweitert.

Was Sie stattdessen wollen, istParametererweiterung: $diroder ${dir}(hier mit dem Parameter alsdir Variable).

pwd( print working directory) ist der Befehl, der den Inhalt der $PWDspeziellen Variablen ¹ ausgibt, die selbst einen Pfad zum aktuellen Arbeitsverzeichnis enthält und daher $(pwd)zu demselben Ergebnis erweitert wird wie, $PWDsolange $PWDes nicht mit Zeilenumbruchzeichen endet.

Also du möchtest:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      printf '%s\n' "$PWD"
  )
done

Oder:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      pwd
  )
done

Denken Sie auch daran

  • echokann nicht zur Ausgabe beliebiger Daten verwendet werden, verwenden Sie printfstattdessen
  • Sie sollten grundsätzlich den Beendigungsstatus von Befehlen überprüfen. Wenn Sie hier die Ausgabe nicht überprüfen, würden Sie cdmit dem folgenden Befehl cd ..an die falsche Stelle gelangen, wenn der erste cdfehlgeschlagen ist.
  • besser cdin einer Subshell (mit (...)) statt mit auszuführen, cd ..da Letzteres nicht immer garantiert, dass Sie an die ursprüngliche Stelle zurückkehren (wenn symbolische Links beteiligt sind und/oder einige Pfadkomponenten in der Zwischenzeit umbenannt wurden)
  • --muss verwendet werden, um Optionen von Nicht-Optionen zu trennen, wenn nicht garantiert werden kann, dass die Nicht-Optionen nicht mit beginnen -(oder +was bei einigen Befehlen ebenfalls ein Problem darstellen kann).
  • in POSIX-ähnlichen Shells müssen Parametererweiterungen und Befehlsersetzungen (und arithmetische Erweiterungen) in Anführungszeichen gesetzt werden, da sie sonst (außer in zsh) einem impliziten Split+Glob unterliegen, was Sie im Allgemeinen nicht möchten.

Beachten Sie auch, dass in Bash (und anderen POSIX-Shells außer Zsh) ein Glob, der mit keiner Datei übereinstimmt, nicht erweitert wird.

Wenn das aktuelle Arbeitsverzeichnis also keine nicht versteckte Datei enthält, deren Typ bestimmt werden kannVerzeichnis, das */Glob-Muster wird einfach auf sich selbst erweitert, */anstatt eine leere Liste zu erstellen, und Sie werden wahrscheinlich einen Fehler sehen, cddass es dieses Verzeichnis nicht betreten kann */.

In zshwürden Sie den NGlob-Qualifizierer wie in verwenden for dir in */(N); do...(oder um die Endung auf for dir in *(N-/); do...zu vermeiden ).$dir/

In ksh93: for dir in ~(N)*/; do....

In bashkönnen Sie die nullglobOption temporär global auf „Ein“ setzen:

shopt -s nullglob
for dir in */; do
  ...
done
shopt -u nullglob

Der Vollständigkeit halber $(var)ist die Syntax fürMakroErweiterungen in Makefiles, wie sie vom makeBefehl verwendet werden.

In der awkSprache $ist ein unärer Operator zum Dereferenzieren von Feldern, und Sie verwenden ihn einfach, varum auf eine Variable zu verweisen awk. Daher $(var)wäre dort dasselbe wie $varoder $ varund würde auf das var-te Feld erweitert (oder auf den gesamten Datensatz, wenn varer 0 ist).

In rc-ähnlichen Shells würde $(var)dasselbe wie $var, $ var, $ 'var', $ ( 'var' )auch funktionieren, da es sich dabei um eine Liste mit einem Element handelt $, das an übergeben wird. Sie dereferenzieren also die varVariable, obwohl Sie normalerweise nur verwenden würden $var.

In TCL $(var)würde dies auf den Wert der Array-Variable mit leerem Namen für den varSchlüssel erweitert:

$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1

¹ Ja, Pfor Print macht in diesem Kontext nicht viel Sinn. Wie Sie sich denken können, pwdwar der Befehl (von Unix V5 Mitte der 70er Jahre) der erste, und $PWDderlogischHandhabung des aktuellen Arbeitsverzeichnisses, die beide von POSIX spezifiziert wurden, kam später (in ksh in den frühen 80ern, wo es pwdeigentlich ein Alias ​​für war print - $PWD(ja, mit den fehlenden -rAnführungszeichen und!) und in der Dokumentation mit dem Brakronym resent orking irectory $PWDversehen wurde ). hat eine Variable ( urrent orking irectory) für den gleichen ZweckPWDtcsh$cwdcwd

verwandte Informationen