Das Wechseln in ein Verzeichnis mit Bash-Variablen funktioniert nicht, wenn Verzeichnisnamen Leerzeichen enthalten

11

Angenommen, ich möchte den folgenden Befehl in einer Variablen speichern

cd "/cygdrive/c/Program Files/"

Also mache ich das

dir="cd \"/cygdrive/c/Program Files/\""

Das sollte den Befehl zum Navigieren zum Programmdateiverzeichnis speichern. Wenn ich also $ dir eingebe, gehe ich in dieses Verzeichnis. Um zu überprüfen, ob die Zitate ordnungsgemäß maskiert wurden, tippe ich

echo $dir

das gibt mir

cd "/cygdrive/c/Program Files/"

Also sollte alles gut funktionieren. Wenn ich jedoch tippe,

$dir

Ich bekomme

bash: cd: "/cygdrive/c/Program: No such file or directory

Was mache ich falsch? Ich verwende Cygwin, aber ich gehe davon aus, dass dieses Problem generell für Bash gilt.

gsingh2011
quelle
Entfernen Sie die Anführungszeichen um den Verzeichnisnamen und schließen Sie das Leerzeichen stattdessen mit einem Backslash an.
Mike Fitzpatrick

Antworten:

8

Kurze Antwort: siehe BashFAQ # 050 ("Ich versuche, einen Befehl in eine Variable einzufügen , aber die komplexen Fälle schlagen immer fehl!").

Lange Antwort: Wenn bash einen Befehl analysiert, werden Anführungszeichen analysiert, bevor Variablen ersetzt werden. Es geht nie zurück und analysiert Anführungszeichen in den Werten von Variablen neu, sodass sie nichts Nützliches tun. Die Verwendung von echo zur Überprüfung des Befehls ist völlig irreführend, da der Befehl nach dem Analysieren angezeigt wird. Wenn Sie sehen möchten, was wirklich ausgeführt wird, verwenden Sie entweder set -xdie Shell-Druckbefehle, während sie ausgeführt werden, oder verwenden Sie printf "%q " $dir; echoanstelle des einfachen Echos.

Wenn Sie einen komplexen Befehl speichern möchten (dh einen mit Leerzeichen oder anderen Sonderzeichen in den "Wörtern"), müssen Sie ihn anstelle einer einfachen Textvariablen in ein Array einfügen und ihn dann mit der Redewendung "$ {" erweitern Array [@]} ", wie folgt:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Wenn der Zweck darin besteht, eine einfach zu tippende Kurzschrift zu erstellen, ist dies eindeutig nicht der richtige Weg. Verwenden Sie stattdessen einen Shell-Alias ​​oder eine Shell-Funktion:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

oder

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
quelle
7

Wenn bash erweitert wird $dir, wird nur eine Wortaufteilung und ein Globbing durchgeführt. Word - Spaltung erzeugt drei Worte: cd, "/cygdrive/c/Programund Files/". Dann ist der auszuführende Befehl cdmit zwei Argumenten; cdbetrachtet nur das erste Argument "/cygdrive/c/Program, das kein vorhandenes Verzeichnis ist.

Wenn Sie eine vollständige Shell-Bewertung des Inhalts einer Variablen durchführen möchten, verwenden Sie eval:

eval "$dir"

Beachten Sie, dass Sie doppelte Anführungszeichen benötigen $dir, da sonst zuerst eine Wortaufteilung durchgeführt und dann evaldie Argumente mit Leerzeichen verknüpft werden. Dies würde hier zwar funktionieren, aber im Allgemeinen schlecht werden (z. B. wenn ein Dateiname zwei aufeinanderfolgende Leerzeichen enthält).

Eine Zeichenfolge ist jedoch nicht der richtige Weg, um einen Shell-Befehl zu speichern, den Sie ausführen möchten. Sofern Sie keine ungewöhnlichen Anforderungen haben, sollten Sie stattdessen eine Funktion verwenden:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Wenn Sie wirklich daran interessiert sind, zu tippen $dir, um zu einem bestimmten Verzeichnis zu wechseln, und dies nicht nur ein einfaches Beispiel ist, geben Sie in bash 4 shopt -s autocdIhr .bashrcund set ein

dir="/cygdrive/c/Program Files/"

Danach können Sie einfach "/cygdrive/c/Program Files/"oder "$dir"an der Shell-Eingabeaufforderung eingeben, um in dieses Verzeichnis zu wechseln. Sie brauchen immer noch die doppelten Anführungszeichen $dir; Wenn dir das nicht gefällt, benutze zsh anstelle von bash.

Gilles 'SO - hör auf böse zu sein'
quelle
6

Das Speichern von Befehlen in Variablen ist im Allgemeinen keine gute Idee. Dafür sind Funktionen und Aliase da.

Eher, als

dir="cd \"/cygdrive/c/Program Files/\""

Probieren Sie eines davon aus:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

oder

dir() { cd "/cygdrive/c/Program Files"; }
dir

oder

alias dir='cd "/tmp/Program Files"'
...
dir

Das erste Formular, in dem der Name des Verzeichnisses in einer Variablen gespeichert wird, bedeutet etwas mehr Eingabe, aber es ist klarer, wenn Sie den Befehl ausführen, den Sie ausführen cd. Die zweite ist der nächsten, die Sie erreichen möchten. (Ich benutze Aliase nicht viel in Bash; ich bin mir nicht sicher, welchen Vorteil sie gegenüber Funktionen haben.)

EDIT:

Und dirist wahrscheinlich ein schlechter Name für einen Alias ​​oder eine Funktion, da es einen vorhandenen Befehl mit diesem Namen gibt (es ist im Grunde eine Version von ls, zumindest wenn Sie GNU-Coreutils haben).

Keith Thompson
quelle
Upvote fürStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob