Bash Расширение цитируемого массива
Когда я пишу программу bash, я обычно создаю вызовы вроде:
declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc )
"${mycmd[@]}" || echo "Failed: foo"
Где die foo
- это функция bash, которая печатает Error foo
и выходит.
Но если я хочу четко объяснить причину ошибки, я хочу напечатать неудачную команду:
"${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}"
Таким образом, пользователь может запустить мертвую команду и выяснить, почему. Тем не менее, цитирование теряется на этом проходе - аргументы с сообщением Failed, которые имеют пробелы или экранированные символы, не печатаются способом, который может быть разрезан и запущен.
Есть ли у кого-нибудь предложение для компактного способа устранения этой проблемы?
Я думаю, что проблема заключается в том, как bash имеет дело с анализом аргументов для команд, а способ (встроенный) эхо обрабатывает аргументы. Другой способ заявить о проблеме:
Как распечатать кавычки вокруг аргументов с пробелами в следующем примере bash (который должен запускаться как script, а не в непосредственном режиме):
#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
фактический результат:
1 2 3 4
1 2 3 4
желаемый результат
1 2 3 4
1 2 "3 4"
ИЛИ
1 2 3 4
"1" "2" "3 4"
В нескольких дополнительных символах кода bash.
Вопрос закрыт: @camh великолепно ответил:
обновлен script:
#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
echo $(printf "'%s' " "${myargs[@]}")
выход:
1 2 3 4
1 2 3 4
'1' '2' '3 4'
Ответы
Ответ 1
Ваша проблема связана с echo
. Он получает правильное количество параметров, с некоторыми параметрами, содержащими пробелы, но этот вывод теряет различие пробелов между параметрами и пробелами внутри параметров.
Вместо этого вы можете использовать printf(1)
для вывода параметров и всегда включать кавычки, используя функцию printf, которая последовательно применяет строку формата к параметрам, когда в строке формата есть больше параметров, чем спецификаторы формата:
echo "Failed: foo:" $(printf "'%s' " "${mycmd[@]}")
Это добавит одинарные кавычки вокруг каждого аргумента, даже если это не требуется:
Failed: foo: 'command.ext' 'arg1 with space' 'arg2' 'thing' 'etc'
Я использовал одинарные кавычки, чтобы другие метасимволы оболочки не были неправильно обработаны. Это будет работать для всех символов, кроме самой отдельной кавычки, т.е. Если у вас есть параметр, содержащий одиночную кавычку, вывод из указанной выше команды не будет вырезать и вставить правильно. Скорее всего, это будет самое близкое, что вы получите, не запутавшись.
Изменить: почти 5 лет спустя, и после того, как я ответил на этот вопрос, был выпущен bash 4.4. Это имеет расширение "${[email protected]}"
, которое цитирует переменную так, что ее можно проанализировать с помощью bash.
Это упрощает этот ответ:
echo "Failed: foo: " "${mycmd[@]@Q}"
Это правильно обрабатывает одинарные кавычки в аргументе, который не был у моей более ранней версии.
Ответ 2
bash Команда printf имеет формат% q, который добавляет соответствующие кавычки в строки при печати:
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Помните, что его идея "лучшего" способа процитировать что-то не всегда такая же, как моя, например. он предпочитает избегать забавных символов вместо того, чтобы обернуть строку в кавычки. Например:
crlf=$'\r\n'
declare -a mycmd=( command.ext "arg1 with space 'n apostrophe" "arg2 with control${crlf} characters" )
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Печать
Failed: foo: command.ext arg1\ with\ space\ \'n\ apostrophe $'arg2 with control\r\n characters'
Ответ 3
Как насчет declare -p quotedarray
?
- изменить -
Фактически, declare -p quotedarray
удовлетворит вашу цель. Если вы настаиваете на выходном формате результата, то у меня есть небольшой трюк, который выполнит эту работу, но только для индексированного массива, который не является ассоциативным.
declare -a quotedarray=(1 2 "3 4")
temparray=( "${quotedarray[@]/#/\"}" ) #the outside double quotes are critical
echo ${temparray[@]/%/\"}
Ответ 4
Громоздкий метод (который цитирует только аргументы, содержащие пробелы):
declare -a myargs=(1 2 "3 4")
for arg in "${myargs[@]}"; do
# testing if the argument contains space(s)
if [[ $arg =~ \ ]]; then
# enclose in double quotes if it does
arg=\"$arg\"
fi
echo -n "$arg "
done
Вывод:
1 2 "3 4"
Кстати, что касается quoting is lost on this pass
, обратите внимание, что кавычки никогда не сохраняются. " "
- это специальный символ, который сообщает оболочке обрабатывать все, что внутри, как одно поле/аргумент (т.е. не разделять его). С другой стороны, буквенные кавычки (такие как \"
) сохраняются.
Ответ 5
Мне нравится вводить код в функцию, поэтому его легче использовать и документировать:
function myjoin
{
local list=("${@}")
echo $(printf "'%s', " "${list[@]}")
}
declare -a colorlist=()
colorlist+=('blue')
colorlist+=('red')
colorlist+=('orange')
echo "[$(myjoin ${colorlist[@]})]"
Обратите внимание, как я добавил запятую в решение, потому что я генерирую код с помощью bash script.
[EDIT: исправить проблему, которую EM0 указала, что она возвращает ['blue',]]