Обновление 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. в .
IFS=
избегать удаления пробелов в начале или в конце строк ввода. Вы можете легко проверить это, сравнив выводread var <<<' abc '; echo ">$var<"
с выводомIFS= read var <<<' abc '; echo ">$var<"
. В первом случаеabc
убираются пробелы до и после . В последнем - нет. Имена файлов, которые начинаются или заканчиваются пробелом, могут быть необычными, но если они существуют, мы хотим, чтобы они обрабатывались правильно.<'
done <<(find aaa / -not -newermt "$ last_build_timestamp_v" -type f -print0) '''
можно использовать более простое вместо$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
BLAH=$(find . -name '*.php')
. Как обсуждалось в ответе, этот подход будет работать в ограниченных случаях, но в целом он не будет работать со всеми именами файлов и не создает, как ожидалось OP, массив .Bash 4.4 представил
-d
параметр дляreadarray
/mapfile
, поэтому теперь это можно решить с помощьюreadarray -d '' array < <(find . -name "$input" -print0)
для метода, который работает с произвольными именами файлов, включая пробелы, новые строки и символы подстановки. Это требует вашей
find
поддержки-print0
, как, например, GNU find.Из руководства (без других опций):
И
readarray
это просто синонимmapfile
.источник
Если вы используете
bash
4 или более позднюю версию, вы можете заменить использованиеfind
сshopt -s globstar nullglob array=( **/*"$input"* )
Включенный
**
шаблонglobstar
соответствует 0 или более каталогам, что позволяет шаблону соответствовать произвольной глубине в текущем каталоге. Безnullglob
опции шаблон (после расширения параметра) обрабатывается буквально, поэтому без совпадений у вас будет массив с одной строкой, а не пустой массив.Добавьте
dotglob
параметр в первую строку, если вы хотите перемещаться по скрытым каталогам (например,.ssh
) и также сопоставлять скрытые файлы (например.bashrc
).источник
nullglob
тоже ...dotglob
не установлены (это может быть необходимо, а может и нет, но об этом тоже стоит упомянуть).вы можете попробовать что-то вроде
, а чтобы распечатать значения массива, вы можете попробовать что-то вроде echoarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
источник
Следующее работает как для Bash, так и для Z Shell в macOS.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
источник
printf "%b" "file name with spaces, a star * ...\012and a second line\0" | xargs -0 touch
В bash
$(<any_shell_cmd>)
помогает запустить команду и записать вывод. Передача этого параметра сIFS
помощью\n
as delimiter помогает преобразовать это в массив.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
источник
find
в массив.Вы могли бы сделать так:
#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done
источник
Для меня это отлично сработало на cygwin:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
Это работает с пробелами, но не с двойными кавычками (") в именах каталогов (что в любом случае недопустимо в среде Windows).
Остерегайтесь пробелов в параметре -printf.
источник