Запуск функции Bash для каждого файла, найденного с помощью команды find

Запуск функции Bash для каждого файла, найденного с помощью команды find

В настоящее время у меня есть эта команда, которая успешно выводит каждое имя файла на отдельной строке:

find . -exec echo {} \;

Я пытаюсь разделить логику так, чтобы findкоманда выполняла функцию. На основеэтот вопросЯ попробовал это:

my_function() {
    echo $1
}
export -f my_function
find . -exec bash -c 'my_function "$@"' bash {} +

Затем я получаю такой вывод:

.

Я также пробовал заменить $@на $*, но это приводит к тому $1, что каждый файл будет без переносов строк. Я хотел бы запустить логику, которая проверяет каждый файл, поэтому я хотел бы $1просто быть одним файлом за раз. Я пробовал разделять вывод пробелами через , for file in $1но это не работает для файлов, в имени которых есть пробелы. Как мне запустить функцию Bash для каждого файла, найденного командой find?

EDIT: Вот весь скрипт, который я использую. Кажется, он работает нормально.

# Ensures that non-csproj text files are formatted with LF line endings.
format() {
    for pathname do
        if [[ $pathname == *"git"* ]]; then
            continue
        elif [[ $pathname == *"csproj"* ]]; then
            continue
        fi
        dos2unix $pathname
    done
}
export -f format
find . -exec bash -c 'format "$@"' bash {} \;

решение1

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

find . -type f ! -name '*git*' -exec dos2unix --newline {} +

То есть, найти все обычные файлы, имена которых не соответствуют шаблону *git*, и запустить dos2unix --newlineих все в максимально больших пакетах одновременно. Измените ! -name '*git*'на ! -path '*git*', чтобы избежать любого файла, чье имя пути содержит строку git(например, файлы в .gitкаталогах).

Чтобы явно избегать любых .gitкаталогов, но включать все, что может содержать gitв своем названии:

find . -name .git -prune -o -type f -exec dos2unix --newline {} +

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


Ответьте на вопрос перед редактированием:

Ваша функция выводит только свой первый аргумент. Точка — это путь поиска верхнего уровня, который вы используете с find. Он передается, поскольку вы не выполняете никакой особой фильтрации записей каталога (как, например, -type fдля обычных файлов только, или -name, или любого другого типа findтеста).

Если вы хотите, чтобы ваша функция вывела каждый из своих аргументов, используйте либо

my_function() {
    printf '%s\n' "$@"
}

что позволяет printfвывести каждый из аргументов с новой строкой между ними, или

my_function() {
    for pathname do
        printf '%s\n' "$pathname"
    done
}

который перебирает аргументы и вызывается printfодин раз для каждого.

Ожидается, что это будет работать правильно, если вы вызовете функцию следующим образом:

my_function "$@"

из вашего встроенного bash -cскрипта. "$@"Расширяется до всех аргументов, данных скрипту, индивидуально заключенных в кавычки.

Другой способ — перенести цикл в bash -cскрипт:

for pathname do
    my_function "$pathname"
done

и затем иметь

my_function () {
    printf '%s\n' "$1"
}

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

Тогда команда findбудет выглядеть так:

find . -exec bash -c 'for pathname do my_function "$pathname"; done' bash {} +

или, возможно, немного более читабельно,

find . -exec bash -c '
    for pathname do
        my_function "$pathname"
    done' bash {} +

Это, кстати, было бы то же самое, что

shopt -s globstar nullglob dotglob

for pathname in ./**/*; do
    my_function "$pathname"
done

за исключением того, что это .не будет обработано. Используя это, вам не придется экспортировать вашу my_functionфункцию.

С циклом внутри функции (как в первых двух фрагментах кода в этом ответе) это можно сократить до

shopt -s globstar nullglob dotglob

my_function ./**/*

Связанный контент