Я хочу перебрать список файлов. Этот список является результатом find
команды, поэтому я придумал:
getlist() {
for f in $(find . -iname "foo*")
do
echo "File found: $f"
# do something useful
done
}
Это нормально, за исключением случаев, когда в имени файла есть пробелы:
$ ls
foo_bar_baz.txt
foo bar baz.txt
$ getlist
File found: foo_bar_baz.txt
File found: foo
File found: bar
File found: baz.txt
Что я могу сделать, чтобы избежать разделения на пробелы?
Ответы:
Вы можете заменить итерацию на основе слов на итерацию на основе строк:
источник
touch "$(printf "foo\nbar")"
IFS= while read -r f
вместо этого.find
и цикл while.-exec
будут чист , чем явный цикл:find . -iname "foo*" -exec echo "File found: {}" \;
. Кроме того , во многих случаях вы можете заменить , что в прошлом\;
с+
положить много файлов в одной команде.Есть несколько реальных способов сделать это.
Если вы хотите придерживаться своей оригинальной версии, это можно сделать следующим образом:
Это все равно не удастся, если в именах файлов есть буквальные символы новой строки, но пробелы не нарушат его.
Однако возиться с IFS не обязательно. Вот мой предпочтительный способ сделать это:
Если вы найдете
< <(command)
синтаксис незнакомым, вам следует прочитать о замене процесса . Преимущество этогоfor file in $(find ...)
заключается в том, что файлы с пробелами, символами новой строки и другими символами обрабатываются правильно. Это работает, потому чтоfind
with-print0
будет использоватьnull
(aka\0
) в качестве терминатора для каждого имени файла и, в отличие от новой строки, null не является допустимым символом в имени файла.Преимущество этого перед почти эквивалентной версией
Это присваивание любой переменной в теле цикла while. То есть, если вы передадите трубку,
while
как указано выше, то телоwhile
находится в подоболочке, которая может не соответствовать вашему желанию.Преимущество версии подстановки процесса
find ... -print0 | xargs -0
минимально:xargs
версия хороша, если все, что вам нужно, это напечатать строку или выполнить одну операцию над файлом, но если вам нужно выполнить несколько шагов, версия цикла становится проще.РЕДАКТИРОВАТЬ : Вот хороший тестовый скрипт, чтобы вы могли понять разницу между различными попытками решения этой проблемы
источник
$IFS
и о< <(cmd)
синтаксисе. Еще одна вещь остается неясной для меня, почему$
в$'\0'
? Большое спасибо.while IFS= read
... для обработки файлов, которые начинаются или заканчиваются пробелами.bash
поэтому я чувствовал себя в безопасности, используя специфичные для bash функции. Подстановка процессов не переносима на другие оболочки (сама sh вряд ли когда-либо получит такое значительное обновление).IFS=$'\n'
сfor
предотвращает расщепление слов во внутренней строке, но при этом приводит к тому, что результирующие строки становятся объектами глобализации, поэтому этот подход не является полностью надежным (если только вы сначала не отключите глобализацию). Хотя этоread -d $'\0'
работает, оно немного вводит в заблуждение, так как предполагает, что вы можете использовать$'\0'
для создания NUL - вы не можете: a\0
в строке ANSI C в кавычках эффективно завершает строку, так что-d $'\0'
фактически совпадает с-d ''
.Существует также очень простое решение: полагаться на bash globbing
Обратите внимание, что я не уверен, что это поведение по умолчанию, но я не вижу никаких специальных настроек в моем шопе, поэтому я бы сказал, что это должно быть "безопасно" (протестировано на osx и ubuntu).
источник
источник
См
man xargs
.источник
Поскольку вы не выполняете никакой другой тип фильтрации с помощью
find
, вы можете использовать следующее начиная сbash
4.0:Значение
**/
будет соответствовать нулю или нескольким каталогам, поэтому полный шаблон будет соответствоватьfoo*
текущему каталогу или любым его подкаталогам.источник
Мне действительно нравятся циклы и итерации массивов, поэтому я решил добавить этот ответ в смесь ...
Мне также понравился глупый пример файла Марчелблинга. :)
Внутри тестовой директории:
Это добавляет каждую строку списка файлов в массив bash, названный
arr
с удаленным последним переводом строки.Допустим, мы хотим дать этим файлам лучшие имена ...
$ {! arr [@]} расширяется до 0 1 2, поэтому «$ {arr [$ i]}» - это i- й элемент массива. Кавычки вокруг переменных важны для сохранения пробелов.
Результат - три переименованных файла:
источник
find
имеет-exec
аргумент, который перебирает результаты поиска и выполняет произвольную команду. Например:Здесь
{}
представлены найденные файлы, а их обтекание""
позволяет полученной команде оболочки обрабатывать пробелы в имени файла.Во многих случаях вы можете заменить эту последнюю
\;
(которая запускает новую команду) на\+
, которая поместит несколько файлов в одну команду (хотя не обязательно все они одновременно, см.man find
Более подробную информацию).источник
В некоторых случаях здесь, если вам просто нужно скопировать или переместить список файлов, вы также можете передать этот список в awk.
Важно по
\"" "\"
всему полю$0
(короче, ваши файлы, один список строк = один файл).источник
Хорошо - мой первый пост о переполнении стека!
Хотя мои проблемы с этим всегда были в csh, а не в bash, решение, которое я представляю, будет работать в обоих случаях. Проблема заключается в интерпретации оболочкой возвратов "ls". Мы можем удалить «ls» из проблемы, просто используя расширение оболочки
*
подстановочного знака - но это дает ошибку «нет соответствия», если в текущей (или указанной папке) нет файлов - чтобы обойти это, мы просто расширяем расширение, чтобы включить точечные файлы таким образом:* .*
- это всегда будет давать результаты, так как файлы. и .. всегда будет присутствовать. Так что в CSH мы можем использовать эту конструкцию ...если вы хотите отфильтровать стандартные точечные файлы, то это достаточно просто ...
Код в первом посте в этой теме будет написан так:
Надеюсь это поможет!
источник
Еще одно решение для работы ...
Целью было:
источник