Как 'find -exec' передает имена файлов с пробелами?

14

Если у меня есть каталог, содержащий некоторые файлы, имена которых имеют пробелы, например

$ ls -1 dir1
file 1
file 2
file 3

Я могу успешно скопировать их все в другой каталог, например так:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Однако выходные данные find dir1 -mindepth 1содержат неэкранированные пробелы:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Если я использую print0вместо print, вывод все еще содержит неэкранированные пробелы:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Чтобы скопировать эти файлы вручную cp, мне нужно было бы избежать пробелов; но кажется, что в этом нет необходимости, когда cpприходят аргументы find, независимо от того, использую я +или \;в конце команды.

В чем причина этого?

EmmaV
источник

Ответы:

8

Команда findвыполняет команду напрямую. Команда, включая аргумент имени файла, не будет обрабатываться оболочкой или чем-либо еще, что может изменить имя файла. Это очень безопасно.

Вы правы, что нет необходимости экранировать имена файлов, которые представлены {}в findкомандной строке.

findпередает необработанное имя файла с диска непосредственно во внутренний список аргументов -execкоманды, в вашем случае - cpкоманды.

RobertL
источник
1
В двух словах, find..execможет обрабатывать странные имена файлов самостоятельно ..
Heemayl
2
Первое правило linux club - не разбирай ls
Сергей Колодяжный
5

Вопрос состоит из двух частей:

  • как же find управлять с программами обработки вызовов с использованием -execне сталкиваясь с проблемами пространств , вложенных в именах файлов, и
  • чем хорош -print0вариант?

Во-первых, findэто системный вызов, фактически один из группы связанных вызовов, называемых «exec» . Он передает имя файла в качестве аргумента непосредственно этому вызову, который затем передается напрямую (после создания нового процесса) без потери информации о имени файла.

POSIX findособенность +объясняется следующим образом , в обосновании :

Особенностью findутилиты SVR4 был -execпервичный + терминатор. Это позволило группировать имена файлов, содержащие специальные символы (особенно символы новой строки ), без проблем, возникающих, если такие имена файлов передаются по конвейеру xargs. Другие реализации добавили другие способы обойти эту проблему, в частности -print0основной, который написал имена файлов с нулевым байтовым терминатором. Это было рассмотрено здесь, но не принято. Использование нулевого терминатора означало, что любая утилита, которая собиралась обрабатывать -print0вывод find, должна была добавить новую опцию для анализа нулевых терминаторов, которые она теперь будет читать.

Это « в частности-print0 , первичный» относится к GNU findи xargsкоторые решают эту проблему по-другому. Это также поддерживается FreeBSD findи xargs. Если вы добавили -0опцию (см. Страницу руководства ) к xargsвызову, то эта программа принимает строки, оканчивающиеся символами «нулевой байт». В свою очередь xargsвызывает exec -functions для выполнения своей работы. Основное различие между -print0и -0особенности по сравнению с +особенностью является то , что бывший передает имена файлов через трубу, а второй нет. Разработчики находят применение практически для любой функции; трубы не являются исключением.

Вернемся к примеру OP, в котором используется -tопция cp: которая не найдена в POSIX cp . Скорее, это расширение (также называемое «нестандартной функцией»), предоставляемое GNU cp . -0Расширение xargsне улучшит этот пример, но есть и другие случаи , когда могут быть эффективно использованы по поддержанию в виду , что есть портативная альтернатива +, которая GNU findпринимает.

Томас Дики
источник
-1

( Это должен быть комментарий, но он слишком большой. )

Для тех, кто любит пробовать вещи:

Создайте скрипт, в котором перечислены переданные позиционные параметры, вызовите его list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Запустите findего в некотором каталоге $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Как и ожидалось, во всех вызовах есть только один параметр - имя файла, есть ли в его имени пробелы или нет.

Дэвид Тонхофер
источник
1
Вы также можете использовать printfлайк printf '"%s"\n' "$@"для распечатки всех указанных позиционных аргументов для визуальной проверки.
Кусалананда