Захватив выход найти. - print0 в массив bash



используя find . -print0 кажется, это единственный безопасный способ получения списка файлов в bash из-за возможности имен файлов, содержащих пробелы, новые строки, кавычки и т. д.



тем не менее, мне трудно сделать вывод find полезным в bash или с другими утилитами командной строки. Единственный способ, которым мне удалось использовать выход, - это передать его в perl и изменить IFS perl на null:



find . -print0 | perl -e '$/=""; @files=<>; print $#files;'


в этом примере выводится количество файлов найдено, избегая опасности появления новых строк в именах файлов, искажающих счет, как это происходит с:



find . | wc -l


поскольку большинство программ командной строки не поддерживают ввод с нулевыми разделителями, я считаю, что лучше всего было бы захватить вывод find . -print0 в массиве bash, как я сделал в приведенном выше фрагменте perl, а затем продолжите выполнение задачи, какой бы она ни была.



как я могу это сделать?



Это не работает:



find . -print0 | ( IFS=$'' ; array=( $( cat ) ) ; echo ${#array[@]} )


гораздо более общий вопрос может быть: как я могу сделать полезные вещи со списками файлов в bash?

1005   13  

13 ответов:

бесстыдно украли у Грег BashFAQ:

unset a i
while IFS= read -r -d $'' file; do
    a[i++]="$file"        # or however you want to process each file
done < <(find /tmp -type f -print0)

обратите внимание, что конструкция перенаправления используется здесь (cmd1 < <(cmd2)) похоже, но не совсем то же самое, что и более обычный конвейер (cmd2 | cmd1) -- если команды являются встроенными оболочками (например,while), версия конвейера выполняет их в дочерних ячейках и любых переменных, которые они устанавливают (например, массив a) теряются, когда они выходят. cmd1 < <(cmd2) только запускает cmd2 в подобласти, поэтому массив живет после его построения. Предупреждение: эта форма перенаправления доступна только в bash, даже не bash в режиме SH-эмуляции; вы должны запустить свой скрипт с #!/bin/bash.

также, потому что шаг обработки файла (в данном случае, просто a[i++]="$file", но вы можете сделать что-то более причудливое непосредственно в цикле) перенаправляет свой вход, он не может использовать какие-либо команды, которые могут считываться из stdin. Чтобы избежать этого ограничения, я обычно использую:

unset a i
while IFS= read -r -u3 -d $'' file; do
    a[i++]="$file"        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

...который передает список файлов через блок 3, а не стандартный ввод.

может быть, вы ищете размер:

find . -print0 | xargs -r0 do_something_useful

опция-L 1 может быть полезна и для вас, что делает xargs exec do_something_useful только с одним аргументом файла.

основная проблема заключается в том, что разделитель NUL (\0) здесь бесполезен, потому что невозможно присвоить IFS нулевое значение. Поэтому, как хорошие программисты, мы заботимся о том, чтобы вход для нашей программы был чем-то, что он может обрабатывать.

Сначала мы создаем небольшую программу, которая делает эту часть для нас:

#!/bin/bash
printf "%s" "$@" | base64

...и назовите его base64str (не забудьте chmod +x)

во-вторых, теперь мы можем использовать простой и понятный for-loop:

for i in `find -type f -exec base64str '{}' \;`
do 
  file="`echo -n "$i" | base64 -d`"
  # do something with file
done

Так хитрость заключается в том, что строка base64 не имеет знака, который вызывает проблемы для bash-конечно, xxd или что - то подобное также может выполнять эту работу.

еще один способ подсчета файлов:

find /DIR -type f -print0 | tr -dc '' | wc -c 

Я думаю, что более элегантные решения существуют, но я брошу это. Это также будет работать для имен файлов с пробелами и / или новыми строками:

i=0;
for f in *; do
  array[$i]="$f"
  ((i++))
done

затем вы можете, например, перечислить файлы один за другим (в этом случае в обратном порядке):

for ((i = $i - 1; i >= 0; i--)); do
  ls -al "${array[$i]}"
done

на этой странице дает хороший пример, и для получения дополнительной информации см. Глава 26 на Advanced Bash-Scripting Guide.

вы можете безопасно сделать подсчет с этого:

find . -exec echo ';' | wc -l

(он печатает новую строку для каждого найденного файла/dir, а затем подсчитывает распечатанные новые строки...)

избегайте xargs, если вы можете:

man ruby | less -p 777 
IFS=$'7' 
#array=( $(find ~ -maxdepth 1 -type f -exec printf "%s7" '{}' \; 2>/dev/null) ) 
array=( $(find ~ -maxdepth 1 -type f -exec printf "%s7" '{}' + 2>/dev/null) ) 
echo ${#array[@]} 
printf "%s\n" "${array[@]}" | nl 
echo "${array[0]}" 
IFS=$' \t\n' 

Я новичок, но я считаю, что это ответ; надеюсь, что это поможет кому-то:

STYLE="$HOME/.fluxbox/styles/"

declare -a array1

LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f`


echo $LISTING
array1=( `echo $LISTING`)
TAR_SOURCE=`echo ${array1[@]}`

#tar czvf ~/FluxieStyles.tgz $TAR_SOURCE

Это похоже на версию Stephan202, но файлы (и каталоги) помещаются в массив сразу. Элемент for цикл здесь просто "делать полезные вещи":

files=(*)                        # put files in current directory into an array
i=0
for file in "${files[@]}"
do
    echo "File ${i}: ${file}"    # do something useful 
    let i++
done

чтобы получить граф:

echo ${#files[@]}

старый вопрос, но никто не предложил этот простой метод, поэтому я подумал, что буду. Конечно, если ваши имена файлов имеют ETX, это не решает вашу проблему, но я подозреваю, что это служит для любого реального сценария. Попытка использовать null, похоже, противоречит правилам обработки IFS по умолчанию. Сезон на ваш вкус с найти варианты и обработки ошибок.

savedFS="$IFS"
IFS=$'\x3'
filenames=(`find wherever -printf %p$'\x3'`)
IFS="$savedFS"

ответ Гордона Дэвиссона отлично подходит для bash. Однако для пользователей zsh существует полезный ярлык:

во-первых, поместите строку в переменную:

A="$(find /tmp -type f -print0)"

затем разделите эту переменную и сохраните ее в массиве:

B=( ${(s/^@/)A} )

есть один трюк:^@ - это символ NUL. Для этого вам нужно ввести Ctrl+V, а затем Ctrl+@.

вы можете проверить, что каждая запись $B содержит правильное значение:

for i in "$B[@]"; echo \"$i\"

внимательные читатели могут заметить, что звоните в find команды можно избежать в большинстве случаев с помощью ** синтаксис. Например:

B=( /tmp/** )

С Баш 4.4, встроенный mapfile имеет -d переключатель (для указания разделителя, похожие на -d переключатель read оператор), а разделителем может быть нулевой байт. Следовательно, хороший ответ на вопрос в заголовке

захват производства find . -print0 в массив bash

- это:

mapfile -d '' ary < <(find . -print0)

Bash никогда не был хорош в обработке имен файлов (или любого текста на самом деле), потому что он использует пробелы в качестве разделителя списка.

Я бы рекомендовал использовать python с ш библиотека.

Comments

    Ничего не найдено.