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 в целом.

4 ответа4

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 and 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).

-1

Пытаться

bash$ echo  'This   is   "a     long"  line' > mylist
bash$ echo @mylist
@mylist
bash$ cmd
c:\> c:\cygwin\bin\echo @mylist
This is a     long line

Но даже более конкретно:

https://stackoverflow.com/questions/4334467/cygwin-using-a-path-variable-containing-a-windows-path-with-a-space-in-it

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .