Обратите внимание, что в файлах также могут быть новые строки в имени файла. Вот почему есть find -print0и xargs -0.
Даниэль Бек
Ответы:
12
В идеале вы вообще так не делаете, потому что синтаксический разбор имен файлов в скрипте оболочки всегда затруднителен (исправьте это для пробелов, у вас все равно будут проблемы с другими встроенными символами, в частности с новой строкой). Это даже указано как первая запись на странице BashPitfalls.
Тем не менее, есть способ почти сделать то, что вы хотите:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
Не забывайте также цитировать $iпри его использовании, чтобы потом не интерпретировать пробелы. Также не забывайте $IFSвозвращаться после его использования, потому что если вы этого не сделаете, это приведет к ошибкам позже.
К этому действительно прикреплено еще одно предупреждение: то, что происходит внутри whileцикла, может происходить в подоболочке, в зависимости от используемой вами оболочки, поэтому настройки переменных могут не сохраняться. В forверсии петли позволяет избегать этого , но по цене , которая, даже если применить $IFSрешение вопросов , остерегайтесь с пробелами, вы затем попасть в беду , если findвозвращается слишком много файлов.
В какой-то момент правильным решением для всего этого становится выполнение этого на языке, таком как Perl или Python вместо оболочки.
Мне нравится идея просто использовать Python, чтобы избежать всего этого.
Скотт С. Уилсон
12
Используйте find -print0и передайте ее xargs -0или напишите свою собственную маленькую C-программу и передайте ее вашей маленькой C-программе. Это то, для чего -print0и -0были придуманы.
Сценарии оболочки - не лучший способ обработки имен файлов с пробелами в них: вы можете сделать это, но это становится неуклюжим.
Вы можете установить «внутренний разделитель полей» ( IFS) на что-то другое, чем пространство для разделения аргументов цикла, например
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Я сбрасываю IFSпосле его использования в find, в основном потому, что он выглядит красиво, я думаю. Я не видел никаких проблем с установкой новой строки, но я думаю, что это "чище".
Другой метод, в зависимости от того, что вы хотите сделать с выходным сигналом find, является либо непосредственно использовать -execс findкомандой, или использования -print0и трубы его в xargs -0. В первом случае findзаботится о экранировании имени файла. В этом -print0случае findвыводит вывод с нулевым разделителем, а затем xargsразбивает на него. Поскольку ни одно имя файла не может содержать этот символ (что я знаю), это также всегда безопасно. Это в основном полезно в простых случаях; и обычно не является хорошей заменой для полного forцикла.
Использование в find -print0сочетании с xargs -0полностью устойчиво к допустимым именам файлов и является одним из наиболее расширяемых доступных методов. Например, допустим, что вы хотите получить список всех файлов PDF в текущем каталоге. Вы могли бы написать
Он найдет каждый PDF (через -iname '*.pdf') в текущем каталоге ( .) и любом подкаталоге и передаст каждый из них в качестве аргумента echoкоманде. Поскольку мы указали -n 1опцию, xargsбудет передавать только один аргумент за раз echo. Если бы мы пропустили эту опцию, xargsпередали бы как можно больше echo. (Вы можете echo short input | xargs --show-limitsувидеть, сколько байтов разрешено в командной строке.)
Что именно делает xargs?
Мы можем ясно увидеть эффект, который он xargsоказывает на его ввод - и, -nв частности, эффект - используя скрипт, который более точно повторяет его аргументы echo.
Я не согласен с bashрасшифровщиками, потому что bash, наряду с набором инструментов * nix, весьма искусен в обработке файлов (включая те, чьи имена имеют встроенный пробел).
На самом деле, findдает вам точный контроль над выбором файлов для обработки ... Что касается bash, вам действительно нужно только осознать, что вы должны создавать свои строки bash words; как правило, с помощью «двойных кавычек», или другого механизма, такого как IFS, или find{}
Обратите внимание, что в большинстве / многих ситуациях вам не нужно устанавливать и сбрасывать IFS; просто используйте IFS локально, как показано в примерах ниже. Все три отлично справляются с пробелами. Также вам не нужна «стандартная» структура цикла, потому что find - \;это фактически цикл; просто поместите свою логику цикла в функцию bash (если вы не вызываете стандартный инструмент).
Существует некоторая обоснованность для обеих точек зрения. Когда я работал только с моими собственными файлами, я просто использовал find и не беспокоился об этом, потому что в моих файлах нет пробелов (или возврат каретки!) В их именах. Но когда вы начинаете работать с файлами других людей, вы должны использовать более надежные методы.
find -print0
иxargs -0
.Ответы:
В идеале вы вообще так не делаете, потому что синтаксический разбор имен файлов в скрипте оболочки всегда затруднителен (исправьте это для пробелов, у вас все равно будут проблемы с другими встроенными символами, в частности с новой строкой). Это даже указано как первая запись на странице BashPitfalls.
Тем не менее, есть способ почти сделать то, что вы хотите:
Не забывайте также цитировать
$i
при его использовании, чтобы потом не интерпретировать пробелы. Также не забывайте$IFS
возвращаться после его использования, потому что если вы этого не сделаете, это приведет к ошибкам позже.К этому действительно прикреплено еще одно предупреждение: то, что происходит внутри
while
цикла, может происходить в подоболочке, в зависимости от используемой вами оболочки, поэтому настройки переменных могут не сохраняться. Вfor
версии петли позволяет избегать этого , но по цене , которая, даже если применить$IFS
решение вопросов , остерегайтесь с пробелами, вы затем попасть в беду , еслиfind
возвращается слишком много файлов.В какой-то момент правильным решением для всего этого становится выполнение этого на языке, таком как Perl или Python вместо оболочки.
источник
Используйте
find -print0
и передайте ееxargs -0
или напишите свою собственную маленькую C-программу и передайте ее вашей маленькой C-программе. Это то, для чего-print0
и-0
были придуманы.Сценарии оболочки - не лучший способ обработки имен файлов с пробелами в них: вы можете сделать это, но это становится неуклюжим.
источник
Вы можете установить «внутренний разделитель полей» (
IFS
) на что-то другое, чем пространство для разделения аргументов цикла, напримерЯ сбрасываю
IFS
после его использования в find, в основном потому, что он выглядит красиво, я думаю. Я не видел никаких проблем с установкой новой строки, но я думаю, что это "чище".Другой метод, в зависимости от того, что вы хотите сделать с выходным сигналом
find
, является либо непосредственно использовать-exec
сfind
командой, или использования-print0
и трубы его вxargs -0
. В первом случаеfind
заботится о экранировании имени файла. В этом-print0
случаеfind
выводит вывод с нулевым разделителем, а затемxargs
разбивает на него. Поскольку ни одно имя файла не может содержать этот символ (что я знаю), это также всегда безопасно. Это в основном полезно в простых случаях; и обычно не является хорошей заменой для полногоfor
цикла.источник
Использование
find -print0
сxargs -0
Использование в
find -print0
сочетании сxargs -0
полностью устойчиво к допустимым именам файлов и является одним из наиболее расширяемых доступных методов. Например, допустим, что вы хотите получить список всех файлов PDF в текущем каталоге. Вы могли бы написатьОн найдет каждый PDF (через
-iname '*.pdf'
) в текущем каталоге (.
) и любом подкаталоге и передаст каждый из них в качестве аргументаecho
команде. Поскольку мы указали-n 1
опцию,xargs
будет передавать только один аргумент за разecho
. Если бы мы пропустили эту опцию,xargs
передали бы как можно большеecho
. (Вы можетеecho short input | xargs --show-limits
увидеть, сколько байтов разрешено в командной строке.)Что именно делает
xargs
?Мы можем ясно увидеть эффект, который он
xargs
оказывает на его ввод - и,-n
в частности, эффект - используя скрипт, который более точно повторяет его аргументыecho
.Обратите внимание, что он отлично обрабатывает пробелы и переводы строк,
что было бы особенно проблематичным со следующим общим решением:
Примечанияисточник
Я не согласен с
bash
расшифровщиками, потому чтоbash
, наряду с набором инструментов * nix, весьма искусен в обработке файлов (включая те, чьи имена имеют встроенный пробел).На самом деле,
find
дает вам точный контроль над выбором файлов для обработки ... Что касается bash, вам действительно нужно только осознать, что вы должны создавать свои строкиbash words
; как правило, с помощью «двойных кавычек», или другого механизма, такого как IFS, или find{}
Обратите внимание, что в большинстве / многих ситуациях вам не нужно устанавливать и сбрасывать IFS; просто используйте IFS локально, как показано в примерах ниже. Все три отлично справляются с пробелами. Также вам не нужна «стандартная» структура цикла, потому что find -
\;
это фактически цикл; просто поместите свою логику цикла в функцию bash (если вы не вызываете стандартный инструмент).И еще два примера
'find
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)источник