Элементы списка с пробелами в zsh

10

В этот момент я изучал zsh-скриптинг в течение всех 2 часов, и я столкнулся со стеной. Я хочу просмотреть список файлов, в которых могут быть пробелы. Я открыт для совершенно других подходов, чем в следующем примере, поскольку они zsh, поскольку zsh - это то, что я изучаю, а не задача, которую я пытаюсь написать.

files=(`ls`)
for f in $files; do
    print $f
done

Я явно не просто воссоздаю ls, я просто хочу $fзаписывать полное имя файла на каждой итерации цикла.

Феликс
источник

Ответы:

12

Во-первых, я предполагаю, что использование lsявляется лишь примером. Вы не можете анализировать вывод lsв любой оболочке, потому что это неоднозначно. Прочтите Почему вы не должны анализировать вывод ls (1), если это новость для вас. В любой оболочке для получения списка файлов используйте подстановочные знаки, например files=(*).

В zsh, как и в других оболочках, результат подстановки команды разбивается на слова с пробельными символами (точнее, в соответствии со значением IFS). (В отличие от других оболочек, результат подстановки команды не подлежит смещению в zsh.) Поэтому, если вывод lsкоманды

hello world
wibble

затем files=($(ls))устанавливает filesмассив содержит 3 элемента: hello, worldи wibble.

Если подстановка команды в двойных кавычках, то расщепление не выполняется. Вы можете выполнить произвольное разбиение с помощью флагов расширения параметров . Используйте @флаг, чтобы указать, что результатом разбиения должен быть массив (как ни странно, вам нужно хранить раскрытие в двойных кавычках, т. Е. "${(@)…}"Даже если строка в двойных кавычках будет расширяться до нескольких слов). Для разделения используйте sфлаг, например, "${(@s:,:)…}"чтобы разделить запятыми; fфлаг расщепляется на только переводы строк.

files=("${(@f)$(ls)}")

Обратите внимание, что правильный способ перебора массива в целом заключается for f in $files[@]в $filesудалении пустых элементов (здесь это не имеет значения, поскольку элементы не будут пустыми).

print $fинтерпретируется $fкак переключатель, если он начинается с -и расширяется обратным слешем в $f. Используйте print -r -- $f, или print -rn -- $fесли вы не хотите добавлять новую строку после строки.

Жиль "ТАК - перестань быть злым"
источник
Спасибо. Да, это был просто пример. В данный момент у меня не было конкретной цели, я просто хочу узнать, как заключать в кавычки или экранировать элементы списка из любой команды, которая может иметь отдельные результаты с пробелами в них.
Феликс
Одна вещь, которая меня смущает, - это почему external () в ваших файлах = ("$ {(@ f) $ (ls)}") необходимо использовать с @f? И просто игра вокруг (f), кажется, работает нормально, пока () появляется снаружи.
Koobz
@Koobz 1. Внешнее (…)необходимо, чтобы он filesпредставлял собой массив, а не строку (которая содержала бы конкатенацию строк из команды, отменяя деление, выполненное с помощью (f)). 2. С помощью foo=$'one\n\nthree', contrast print -rl $ {(f) foo} `и print "${(@f)foo}". Двойные кавычки необходимы для сохранения пустых строк, от которых вы не получите, lsно это может произойти с другими командами.
Жиль "ТАК - перестать быть злым"
Очень признателен. Есть ли какая-то рифма или причина этого безумия? Даже там, что сбивает с толку, так это то, что external () не просто заново разделяет строку на отдельные слова, если промежуточное значение является моей сцепленной строкой. Это похоже на интерполяцию строк в ruby ​​или php, например.
Koobz
@Koobz Причиной особой обработки пустых слов является историческая совместимость с давно забытыми старыми версиями zsh. Причина не автоматического разделения заключается в том, что это удивительное и часто нежелательное поведение. Автоматическое разбиение - это поведение оболочки Bourne, которое zsh не эмулирует, если вы не скажете это.
Жиль "ТАК ... перестать быть злым"
2

В zsh вы можете использовать расширение оболочки, которое по умолчанию не разделяет слова. Пытаться

for f in /path/to/files/*; do
    print ${f}
done
U-Punkt
источник