Обновление 2020 для пользователей Linux:
Если у вас есть версия уточненного Баша (4,4-альфа или лучше), как вы , вероятно , делать , если вы на Linux, то вы должны использовать ответ Benjamin W. в .
Если вы работаете в Mac OS, которая, как я проверял в последний раз, по-прежнему использует bash 3.2 или иным образом использует более старую версию bash, переходите к следующему разделу.
Ответ для bash 4.3 или более ранней версии
Вот одно из решений для получения вывода find
в bash
массив:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Это сложно, потому что, как правило, имена файлов могут содержать пробелы, новые строки и другие символы, враждебные скрипту. Единственный способ использовать find
и безопасно отделить имена файлов друг от друга - использовать, -print0
который печатает имена файлов, разделенные нулевым символом. Это не было бы большим неудобством, если бы функции readarray
/ mapfile
функции bash поддерживали строки, разделенные нулем, но это не так. Bash read
делает это, и это приводит нас к описанному выше циклу.
[Этот ответ был первоначально написан в 2014 году. Если у вас установлена последняя версия bash, см. Обновление ниже.]
Как это работает
Первая строка создает пустой массив: array=()
Каждый раз, когда выполняется read
инструкция, из стандартного ввода считывается имя файла, разделенное нулем. -r
Опция указывает , read
чтобы оставить символы обратной косой черты в одиночку. -d $'\0'
Говорит о read
том , что вход будет нулевым разделены. Поскольку мы не указываем имя read
, оболочка помещает ввод в имя по умолчанию:REPLY
.
array+=("$REPLY")
Оператор присоединяет новое имя файла в массивarray
.
Последняя строка объединяет перенаправление и подстановку команд, чтобы обеспечить вывод find
на стандартный ввод while
цикла.
Зачем использовать замещение процесса?
Если бы мы не использовали подстановку процесса, цикл можно было бы записать так:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
В приведенном выше примере вывод find
сохраняется во временном файле, и этот файл используется как стандартный ввод для цикла while. Идея замены процесса состоит в том, чтобы сделать такие временные файлы ненужными. Итак, вместо того, чтобы while
получать стандартный ввод цикла tmpfile
, мы можем заставить его получать свой стандартный ввод из<(find . -name ${input} -print0)
.
Замена процесса широко используется. Во многих местах, где команда хочет читать из файла, вы можете указать замену процесса <(...)
вместо имени файла. Существует аналогичная форма, >(...)
которая может использоваться вместо имени файла, в котором команда хочет записать в файл.
Как и массивы, подстановка процессов - это функция bash и других расширенных оболочек. Это не часть стандарта POSIX.
Альтернатива: lastpipe
При желании lastpipe
можно использовать вместо процесса подстановку (подсказка: Цезарь ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
сообщает bash выполнить последнюю команду конвейера в текущей оболочке (не в фоновом режиме). Таким образом, array
остатки остаются в наличии после завершения конвейера. Поскольку lastpipe
действует только в том случае, если отключено управление заданиями, мы запускаем set +m
. (В сценарии, в отличие от командной строки, управление заданиями по умолчанию отключено.)
Дополнительные примечания
Следующая команда создает переменную оболочки, а не массив оболочки:
array=`find . -name "${input}"`
Если вы хотите создать массив, вам нужно будет заключить в скобки результат поиска. Итак, наивно, можно было:
array=(`find . -name "${input}"`)
Проблема в том, что оболочка выполняет разбиение слов на результаты, find
поэтому не гарантируется, что элементы массива будут такими, как вы хотите.
Обновление 2019
Начиная с версии 4.4-alpha, bash теперь поддерживает -d
опцию, так что вышеупомянутый цикл больше не нужен. Вместо этого можно использовать:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Для получения дополнительной информации об этом, смотрите (и upvote) ответ Benjamin W. в .