Переход в каталог с использованием переменных bash не работает, если в именах каталогов есть пробелы

11

Допустим, я хочу сохранить следующую команду в переменной

cd "/cygdrive/c/Program Files/"

Так что я делаю это

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

В ней должна храниться команда для перехода в каталог Program Files, поэтому, когда я набираю $ dir, он переносит меня в этот каталог. Чтобы проверить, что цитаты были правильно экранированы, я набираю

echo $dir

что дает мне

cd "/cygdrive/c/Program Files/"

Так что все должно работать нормально. Тем не менее, когда я печатаю,

$dir

я получил

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

Что я делаю неправильно? Я использую Cygwin, но я предполагаю, что эта проблема относится к Bash в целом.

gsingh2011
источник
Попробуйте удалить кавычки вокруг имени каталога и вместо этого использовать пробел с обратной косой чертой.
Майк Фицпатрик

Ответы:

8

Краткий ответ: см. BashFAQ # 050 («Я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!»).

Длинный ответ: когда bash анализирует команду, он анализирует кавычки перед тем, как заменить переменные; он никогда не возвращается и повторно анализирует кавычки в значениях переменных, поэтому они в итоге не делают ничего полезного. Использование echo для проверки команды полностью вводит в заблуждение, поскольку она показывает команду после ее анализа; если вы хотите увидеть, что в действительности выполняется, используйте либо set -xкоманды печати оболочки по мере их выполнения, либо используйте printf "%q " $dir; echoвместо простого эха.

Если вы хотите сохранить сложную команду (то есть команду с пробелами или другими специальными символами в «словах»), вам нужно поместить ее в массив вместо простой текстовой переменной, а затем развернуть ее с помощью идиомы `" $ { массив [@]} ", вот так:

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

Теперь, если цель состоит в том, чтобы сделать простую для ввода стенографию, это явно не тот путь. Вместо этого используйте псевдоним или функцию оболочки:

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

или

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Гордон Дэвиссон
источник
7

Когда bash расширяется $dir, он выполняет только разбиение по словам и сглаживание. Слово расщепление производит три слова: cd, "/cygdrive/c/Programи Files/". Тогда команда для выполнения имеет cdдва аргумента; cdтолько смотрит на его первый аргумент "/cygdrive/c/Program, который не является существующим каталогом.

Если вы хотите выполнить полную оценку оболочки для содержимого переменной, используйте eval:

eval "$dir"

Обратите внимание, что вам нужны двойные кавычки $dir, иначе сначала произойдет разбиение слова, а затем evalконкатенация его аргументов с пробелами. Это может сработать, но в целом будет плохо (например, если в имени файла будет два последовательных пробела).

Однако строка не является правильным способом хранения команды оболочки, которую вы хотите выполнить. Если у вас нет необычного требования, вы должны использовать вместо этого функцию:

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

Если вы действительно заинтересованы в наборе текста $dirдля переключения на конкретный каталог, и это не просто пример, начиная с bash 4, вставьте shopt -s autocdваш .bashrcи установите

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

после чего вы можете просто "/cygdrive/c/Program Files/"или "$dir"по приглашению оболочки переключиться на этот каталог. Вам все еще нужны двойные кавычки $dir; если вам это не нравится, используйте zsh вместо bash.

Жиль "ТАК - перестань быть злым"
источник
6

Хранение команд в переменных, как правило, не очень хорошая идея. Вот для чего нужны функции и псевдонимы.

Скорее, чем

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

попробуйте один из них:

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

или

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

или

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

Первая форма, хранящая имя каталога в переменной, означает немного больше ввода, но она понятнее, когда вы выполняете команду, которую вы делаете cd. Второе ближе всего к тому, что вы пытаетесь достичь. (Я не часто использую псевдонимы в bash; на самом деле я не уверен, какое преимущество они имеют над функциями.)

РЕДАКТИРОВАТЬ :

И dirэто , вероятно , плохое имя для псевдонима или функции, так как есть существующая команда с таким именем (это в основном вариант ls, по крайней мере , если у вас есть GNU Coreutils).

Кит Томпсон
источник
upvote дляStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Роб