Przechodzenie do katalogu przy użyciu zmiennych bash nie działa, gdy w nazwach katalogów znajdują się spacje

11

Powiedzmy, że chcę zapisać następujące polecenie w zmiennej

cd "/cygdrive/c/Program Files/"

Więc to robię

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

To powinno przechowywać polecenie, aby przejść do katalogu Program Files, więc kiedy wpisuję $ dir, zabierze mnie do tego katalogu. Aby sprawdzić, czy cytaty zostały poprawnie zmienione, piszę

echo $dir

co daje mi

cd "/cygdrive/c/Program Files/"

Więc wszystko powinno działać dobrze. Jednak kiedy piszę,

$dir

dostaję

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

Co ja robię źle? Używam Cygwin, ale zakładam, że ten problem dotyczy ogólnie bash.

gsingh2011
źródło
Spróbuj usunąć cudzysłowy wokół nazwy katalogu i zamiast tego użyj znaku ukośnika odwrotnego.
Mike Fitzpatrick

Odpowiedzi:

8

Krótka odpowiedź: patrz BashFAQ # 050 („Próbuję wstawić polecenie do zmiennej, ale złożone przypadki zawsze zawodzą!”).

Długa odpowiedź: gdy bash analizuje polecenie, analizuje cudzysłowy, zanim zastąpi zmienne; nigdy nie wraca i ponownie analizuje cudzysłowy wartości zmiennych, więc nie robią nic pożytecznego. Użycie echa do sprawdzenia polecenia jest całkowicie mylące, ponieważ pokazuje polecenie po jego przeanalizowaniu; jeśli chcesz zobaczyć, co tak naprawdę jest wykonywane, użyj albo set -xpoleceń drukowania powłoki w trakcie ich wykonywania, albo użyj printf "%q " $dir; echozamiast zwykłego echa.

Jeśli chcesz przechowywać złożone polecenie (np. Ze spacjami lub innymi znakami specjalnymi w „słowach”), musisz umieścić je w tablicy zamiast prostej zmiennej tekstowej, a następnie rozwinąć je za pomocą idiomu „” $ { array [@]} ”, jak to:

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

Teraz, jeśli celem jest stworzenie łatwego do pisania skrótu, najwyraźniej nie jest to właściwy sposób. Zamiast tego użyj aliasu powłoki lub funkcji:

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

lub

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
źródło
7

Kiedy bash się rozwija $dir, wykonuje tylko dzielenie i globowanie słów. Rozszczepienie słowo produkuje trzy słowa: cd, "/cygdrive/c/Programi Files/". Następnie polecenie wykonania ma cddwa argumenty; cdpatrzy tylko na pierwszy argument "/cygdrive/c/Program, który nie jest istniejącym katalogiem.

Jeśli chcesz przeprowadzić pełną ocenę powłoki dla zawartości zmiennej, użyj eval:

eval "$dir"

Zauważ, że potrzebujesz podwójnych cudzysłowów $dir, w przeciwnym razie najpierw zostanie wykonane dzielenie słów, a następnie evalpołączy swoje argumenty spacjami. To by się działało tutaj, ale ogólnie byłoby źle (np. Gdyby w nazwie pliku były dwie kolejne spacje).

Jednak ciąg znaków nie jest właściwym sposobem przechowywania polecenia powłoki, które chcesz wykonać. Jeśli nie masz nietypowych wymagań, zamiast tego powinieneś użyć funkcji:

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

Jeśli jesteś naprawdę zainteresowany w typowania $dirdo przełącznika do określonego katalogu i nie jest to tylko prosty przykład, ponieważ bash 4, umieścić shopt -s autocdw swoim .bashrci ustawić

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

po czym możesz wpisać just "/cygdrive/c/Program Files/"lub "$dir"w wierszu poleceń powłoki, aby przejść do tego katalogu. Nadal potrzebujesz podwójnych cudzysłowów $dir; jeśli ci się to nie podoba, użyj zsh zamiast bash.

Gilles „SO- przestań być zły”
źródło
6

Przechowywanie poleceń w zmiennych ogólnie nie jest dobrym pomysłem. Do tego służą funkcje i aliasy.

Zamiast

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

spróbuj jednego z tych:

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

lub

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

lub

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

Pierwsza forma, przechowująca nazwę katalogu w zmiennej, oznacza trochę więcej pisania, ale jest wyraźniejsza, gdy wykonujesz polecenie, które wykonujesz cd. Drugi jest najbliższy temu, co próbujesz osiągnąć. (Nie używam aliasów w bash; nie jestem pewien, jaką przewagę mają one nad funkcjami).

EDYTOWAĆ :

I dirprawdopodobnie jest to zła nazwa dla aliasu lub funkcji, ponieważ istnieje taka komenda o tej nazwie (jest to w zasadzie wersja ls, przynajmniej jeśli masz GNU coreutils).

Keith Thompson
źródło
głosowanie zaStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob