
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, cd
in 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 cmd
abzüglich der nachstehenden Zeilenumbruchzeichen erweitert.
Was Sie stattdessen wollen, istParametererweiterung: $dir
oder ${dir}
(hier mit dem Parameter alsdir
Variable).
pwd
( p
rint w
orking d
irectory) ist der Befehl, der den Inhalt der $PWD
speziellen Variablen ¹ ausgibt, die selbst einen Pfad zum aktuellen Arbeitsverzeichnis enthält und daher $(pwd)
zu demselben Ergebnis erweitert wird wie, $PWD
solange $PWD
es 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
echo
kann nicht zur Ausgabe beliebiger Daten verwendet werden, verwenden Sieprintf
stattdessen- Sie sollten grundsätzlich den Beendigungsstatus von Befehlen überprüfen. Wenn Sie hier die Ausgabe nicht überprüfen, würden Sie
cd
mit dem folgenden Befehlcd ..
an die falsche Stelle gelangen, wenn der erstecd
fehlgeschlagen ist. - besser
cd
in 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, cd
dass es dieses Verzeichnis nicht betreten kann */
.
In zsh
würden Sie den N
Glob-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 bash
können Sie die nullglob
Option 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 Makefile
s, wie sie vom make
Befehl verwendet werden.
In der awk
Sprache $
ist ein unärer Operator zum Dereferenzieren von Feldern, und Sie verwenden ihn einfach, var
um auf eine Variable zu verweisen awk
. Daher $(var)
wäre dort dasselbe wie $var
oder $ var
und würde auf das var
-te Feld erweitert (oder auf den gesamten Datensatz, wenn var
er 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 var
Variable, 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 var
Schlüssel erweitert:
$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1
¹ Ja, P
for P
rint macht in diesem Kontext nicht viel Sinn. Wie Sie sich denken können, pwd
war der Befehl (von Unix V5 Mitte der 70er Jahre) der erste, und $PWD
derlogischHandhabung des aktuellen Arbeitsverzeichnisses, die beide von POSIX spezifiziert wurden, kam später (in ksh in den frühen 80ern, wo es pwd
eigentlich ein Alias für war print - $PWD
(ja, mit den fehlenden -r
Anführungszeichen und!) und in der Dokumentation mit dem Brakronym resent orking irectory $PWD
versehen wurde ). hat eine Variable ( urrent orking irectory) für den gleichen ZweckP
W
D
tcsh
$cwd
c
w
d