Я видел руководства по написанию сценариев Bash, предлагающие использовать массив для работы с именами файлов, содержащими пробелы. DashAsBinSh, однако, предполагает, что массивы не являются переносимыми, поэтому я ищу POSIX-совместимый способ работы со списками имен файлов, которые могут содержать пробелы.
Я хочу изменить приведенный ниже пример сценария, чтобы он echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Вот сценарий
#!/usr/bin/env sh
INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps
dostuffwith() { echo $1; };
F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)
for f in $ALL_FILES
do
fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
dostuffwith $fpath
done
shell-script
filenames
quoting
posix
whitespace
Ээро Аалтонен
источник
источник
Ответы:
POSIX оболочка имеет один массив: позиционные параметры (
$1
,$2
и т.д., все вместе обозначаться как"$@"
).Это неудобно, потому что есть только один, и это разрушает любое другое использование позиционных параметров. Позиционные параметры являются локальными для функции, что иногда является благословением, а иногда проклятием.
Если ваши имена файлов гарантированно не содержат новых строк, вы можете использовать новые строки в качестве разделителя. Когда вы раскрываете переменную, сначала отключите глобализацию
set -f
и установите в списке символов разбиения поля только символIFS
новой строки.С элементами в вашем списке, разделенными символами новой строки, вы можете использовать много команд обработки текста, в частности
sort
.Не забывайте всегда ставить двойные кавычки вокруг подстановок переменных, кроме случаев, когда вы явно хотите, чтобы происходило разбиение поля (а также глобализация, если вы не отключили это).
источник
sort | uniq
шаг работать как предназначено.Поскольку в вашей
$INPUT
переменной в качестве разделителей используются символы новой строки, я предполагаю, что в именах ваших файлов не будет символов новой строки. Таким образом, да, существует простой способ перебора файлов и сохранения пробелов.Идея состоит в том, чтобы использовать
read
встроенную оболочку. Обычноread
разделяется на любой пробел, и пробелы разбивают его. Но вы можете установить,IFS=$'\n'
и вместо этого он будет разделяться только на новые строки. Таким образом, вы можете перебирать каждую строку в вашем списке.Вот самое маленькое решение, которое я мог придумать:
По сути, он отправляет «$ INPUT», на
awk
который дедуплицируется на основе имени файла (он разбивается на части,/
а затем печатает строку, если последний элемент ранее не был виден). Затем, как только awk сгенерирует список путей к файлам, мы используем егоwhile read
для перебора списка.источник
while
цикл и, следовательноdostuffwith
, выполняется в подоболочке. Поэтому любые переменные или изменения, внесенные в работающую оболочку, будут потеряны после завершения цикла. Единственная альтернатива - использовать полный heredoc, что не так уж и неприятно, но я подумал, что это будет предпочтительнее.IFS="\n"
разбивает на обратную косую черту и n символов. Но вread file
этом нет разделения.IFS="\n"
все еще полезно в этом, это удаляет пустые символы из $ IFS, которые иначе были бы удалены в начале и конце ввода. Для того, чтобы прочитать строку, канонический синтаксисIFS= read -r line
, хотяIFS=anything read -r line
(если ничего не содержит пробелов) будет работать.