findコマンドで見つかったファイルごとにBash関数を実行する

findコマンドで見つかったファイルごとにBash関数を実行する

現在、次のコマンドがあり、各ファイル名をそれぞれの行に正常に印刷します。

find . -exec echo {} \;

findコマンドが関数を実行するようにロジックを分割しようとしています。この質問私はこれを試しました:

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

すると、次の出力が得られます。

.

$@に置き換えてみることも試しましたが、改行のないすべてのファイルが対象になります。各ファイルをチェックするロジックを実行したいので、$*一度に 1 つのファイルだけにしたいと思います。 で出力をスペースで分割しようとしましたが、ファイル名にスペースがあるファイルでは機能しません。 コマンドで見つかったすべてのファイルに対して Bash 関数を実行するにはどうすればよいですか?$1$1for file in $1find

編集: 私が使用しているスクリプト全体は次のとおりです。問題なく動作するようです。

# 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それぞれを 1 回呼び出します。

次のように関数を呼び出すと、正しく動作するはずです。

my_function "$@"

インラインbash -cスクリプト内から。"$@"スクリプトに渡されたすべての引数を個別に引用符で囲んで展開します。

別の方法としては、ループをbash -cスクリプト内に移動することです。

for pathname do
    my_function "$pathname"
done

そして

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

これは、実行したいことを明示的に実行することになります。つまり、パス名ごとに関数を 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

関数内のループ(この回答の最初の2つのコードのように)を使用すると、これは次のように短縮されます。

shopt -s globstar nullglob dotglob

my_function ./**/*

関連情報