Какую идиому использовать в скриптах Bash (пожалуйста, без Perl, Python и т. П.) для создания командной строки для другой программы из аргументов скрипта при правильной обработке имен файлов правильно * * 1005
Под правильно я имею в виду обработку имен файлов с пробелами или нечетными символами без непреднамеренного побуждения другой программы обрабатывать их как отдельные аргументы (или, в случае <
или >
& mdash; что в конце концов, действительны, если неудачные символы имени файла, если их правильно экранировать - что-то еще хуже).
Вот выдуманный пример того, что я имею в виду, в форме, которую не правильно обрабатывает имена файлов: Давайте предположим, что этот скрипт (foo
) создает командную строку для команды (bar
(предполагается, что он находится в пути), взяв все входные аргументы foo
и переместив все, что выглядит как флаг, вперед, а затем вызвав bar
:
#!/bin/bash
# This is clearly wrong
FILES=
FLAGS=
for ARG in "$@"; do
echo "foo: Handling $ARG"
if [ x${ARG:0:1} = "x-" ]; then
# Looks like a flag, add it to the flags string
FLAGS="$FLAGS $ARG"
else
# Looks like a file, add it to the files string
FILES="$FILES $ARG"
fi
done
# Call bar with the flags and files (we don't care that they'll
# have an extra space or two)
CMD="bar $FLAGS $FILES"
echo "Issuing: $CMD"
$CMD
(Обратите внимание, что это просто пример ; есть много других случаев, когда нужно сделать то или иное с кучей аргументов и затем передать их другим программам.)
В наивном сценарии с простыми именами файлов это прекрасно работает. Но если мы предположим каталог, содержащий файлы
one
two
three and a half
four < five
тогда, конечно, команда foo *
терпит неудачу в своей задаче:
foo: Handling four < five
foo: Handling one
foo: Handling three and a half
foo: Handling two
Issuing: bar four < five one three and a half two
Если мы действительно позволим foo
выполнить эту команду, то результат будет не таким, как мы ожидаем.
Раньше я пытался справиться с этим с помощью простого способа обеспечить наличие кавычек вокруг каждого имени файла, но я (очень) быстро понял, что это не правильный подход. : -)
Так что же? Ограничения:
- Я хочу, чтобы идиома была максимально простой (не в последнюю очередь, чтобы я мог ее запомнить).
- Я ищу идиому общего назначения, поэтому я составляю программу
bar
и надуманный пример выше, а не использую реальный сценарий, когда люди могут легко (и разумно) пойти по пути попыток использовать функции в целевой программе.
- Я хочу придерживаться скрипта Bash, я не хочу вызывать Perl, Python и т. Д.
- Я в порядке, полагаясь на (другие) стандартные * nix-утилиты, такие как
xargs
, sed
или tr
, при условии, что мы не слишком тупые (см. # 1 выше). (Извинения программистам на Perl, Python и т. Д., Которые думают, что # 3 и # 4 объединяются, чтобы провести произвольное различие.)
- Если это имеет значение, целевой программой также может быть Bash-скрипт или нет. Я не ожидал, что это будет иметь значение ...
- Я не просто хочу обрабатывать пробелы, я хочу правильно обрабатывать и странные символы.
- Меня не беспокоит, если он не обрабатывает имена файлов со встроенными нулевыми символами (буквально код символа 0). Если кому-то удалось создать его в своей файловой системе, я не беспокоюсь об этом, он попытался очень тяжело все испортить.
Заранее спасибо, ребята.
Редактировать : Игнасио Васкес-Абрамс указал мне на Bash FAQ entry # 50 , что после некоторого чтения и экспериментов, кажется, указывает на то, что одним из способов является использовать Bash массивы :
#!/bin/bash
# This appears to work, using Bash arrays
# Start with blank arrays
FILES=()
FLAGS=()
for ARG in "$@"; do
echo "foo: Handling $ARG"
if [ x${ARG:0:1} = "x-" ]; then
# Looks like a flag, add it to the flags array
FLAGS+=("$ARG")
else
# Looks like a file, add it to the files array
FILES+=("$ARG")
fi
done
# Call bar with the flags and files
echo "Issuing (but properly delimited, not exactly as this appears): bar ${FLAGS[@]} ${FILES[@]}"
bar "${FLAGS[@]}" "${FILES[@]}"
Это правильно и разумно? Или я полагаюсь на что-то более важное, что укусит меня позже. Кажется, что работает, и это помечает все остальные поля для меня (просто, легко запомнить и т. Д.) Похоже, что он полагается на недавнюю функцию Bash относительно (запись FAQ № 50 упоминает v3.1, но я не был уверен, были ли это вообще массивы некоторых из синтаксиса, который они использовали с ним) , но я думаю, что, скорее всего, я буду иметь дело только с версиями, которые имеют его.
(Если вышеприведенное верно и вы хотите удалить свой ответ, Игнасио, я приму его, если я еще не принял никаких других, хотя я придерживаюсь своего утверждения об ответах только для ссылок .)