$ ls -l /tmp/test/my\ dir/
total 0
Мне было интересно, почему следующие способы запуска вышеуказанной команды не удаются или преуспеют?
$ abc='ls -l "/tmp/test/my dir"'
$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory
$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory
$ bash -c $abc
'my dir'
$ bash -c "$abc"
total 0
$ eval $abc
total 0
$ eval "$abc"
total 0
Ответы:
Это обсуждалось в ряде вопросов о unix.SE, я постараюсь собрать все вопросы, которые могут возникнуть здесь. Ссылки в конце.
Почему это не удается
Причина, по которой вы сталкиваетесь с этими проблемами, заключается в разделении слов, и тот факт, что кавычки, расширенные из переменных, не действуют как кавычки, а являются просто обычными символами.
Случаи, представленные в вопросе:
Здесь
$abc
разделяется иls
получает два аргумента"/tmp/test/my
иdir"
(с кавычками в начале первого и в конце второго):Здесь расширение цитируется, поэтому оно хранится как одно слово. Оболочка пытается найти программу с именем
ls -l "/tmp/test/my dir"
, включая пробелы и кавычки.И здесь
$abc
в качестве аргумента берется только первое слово or-c
, поэтому Bash просто запускаетсяls
в текущем каталоге. Остальные слова аргументы Баш, и используются для заполнения$0
,$1
и т.д.С
bash -c "$abc"
, иeval "$abc"
, есть дополнительный шаг обработки оболочки, который заставляет работать кавычки, но также вызывает повторную обработку всех расширений оболочки , поэтому существует риск случайного запуска расширения команды из предоставленных пользователем данных, если вы не очень осторожно процитировать.Лучшие способы сделать это
Два лучших способа сохранить команду: а) использовать функцию, б) использовать переменную массива (или позиционные параметры).
Используя функцию:
Просто объявите функцию с командой внутри и запустите функцию, как если бы это была команда. Расширения в командах внутри функции обрабатываются только тогда, когда команда выполняется, а не когда она определена, и вам не нужно указывать отдельные команды.
Используя массив:
Массивы позволяют создавать переменные из нескольких слов, где отдельные слова содержат пробелы. Здесь отдельные слова хранятся как отдельные элементы массива, а
"${array[@]}"
расширение расширяет каждый элемент как отдельные слова оболочки:Синтаксис немного ужасен, но массивы также позволяют вам собирать командную строку по частям. Например:
или оставьте части командной строки постоянными и используйте массив, заполняющий только его часть, параметры или имена файлов:
Недостатком массивов является то, что они не являются стандартной функцией, поэтому простые оболочки POSIX (например
dash
, по умолчанию/bin/sh
в Debian / Ubuntu) их не поддерживают (но см. Ниже). Bash, ksh и zsh, однако, так что, вероятно, ваша система имеет какую-то оболочку, которая поддерживает массивы.С помощью
"$@"
В оболочках без поддержки именованных массивов все еще можно использовать позиционные параметры (псевдомассив
"$@"
) для хранения аргументов команды.Следующее должно быть переносимыми битами сценария, которые делают эквивалент битов кода в предыдущем разделе. Массив заменяется
"$@"
списком позиционных параметров. Установка"$@"
выполняется с помощьюset
, и двойные кавычки"$@"
важны (это приводит к тому, что элементы списка заключаются в отдельные кавычки).Во-первых, просто сохраните команду с аргументами
"$@"
и запустите ее:Условное задание частей параметров командной строки для команды:
Используется только
"$@"
для опций и операндов:(Конечно,
"$@"
обычно он заполнен аргументами самого скрипта, поэтому вам придется их где-то сохранить перед повторным рассмотрением"$@"
.)Будьте осторожны с
eval
!Поскольку
eval
вводится дополнительный уровень обработки цитат и расширения, вам нужно быть осторожным с пользовательским вводом. Например, это работает до тех пор, пока пользователь не введет ни одной кавычки:Но если они дают ввод
'$(uname)'.txt
, ваш сценарий успешно выполняет подстановку команд.Версия с массивами невосприимчива к тому, что слова хранятся отдельно в течение всего времени, поэтому нет никакого предложения или другой обработки для содержимого
filename
.Ссылки
источник
cmd="ls -l $(printf "%q" "$filename")"
. это не очень красиво, но если пользователь не работаетeval
, то это помогает. Это также очень полезно для отправки команды , хотя подобные вещи, напримерssh foohost "ls -l $(printf "%q" "$filename")"
, или в Sprit этого вопроса:ssh foohost "$cmd"
.$ alias abc='ls -l "/tmp/test/my dir"'
Самый безопасный способ выполнить (нетривиальную) команду - это
eval
. Затем вы можете написать команду так же, как в командной строке, и она будет выполнена точно так же, как если бы вы только что ее ввели. Но вы должны процитировать все.Простой случай:
не все так просто
источник
Второй знак кавычки нарушает команду.
Когда я бегу:
Это дало мне ошибку.
Но когда я бегу
Там нет ошибки вообще
В настоящее время нет способа исправить это (для меня), но вы можете избежать ошибки, не используя пробел в имени каталога.
В этом ответе сказано, что команду eval можно исправить, но у меня она не работает :(
источник
Если это не работает
''
, то вы должны использовать``
:Обнови лучше:
источник
$( command... )
вместо кавычек.Как насчет Python3 с одним вкладышем?
источник