I currently have this command, which successfully prints each file name on its own line:

find . -exec echo {} \;

I'm trying to split the logic so that the find command executes a function. Based on this question I've tried this:

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

Then I get this output:


I also tried replacing $@ with $* but this causes $1 to be every single file without line breaks. I would like to run logic that checks each file, so I would like $1 to just be one file at a time. I tried splitting the output with spaces via for file in $1 but this doesn't work for files that have spaces in the file name. How do I run a Bash function for every file found by the find command?

EDIT: Here's the whole script I am using. It seems to work fine.

# Ensures that non-csproj text files are formatted with LF line endings.
format() {
    for pathname do
        if [[ $pathname == *"git"* ]]; then
        elif [[ $pathname == *"csproj"* ]]; then
        dos2unix $pathname
export -f format
find . -exec bash -c 'format "$@"' bash {} \;
  • 1
    Change $1 to "$1" aka Quote your variable.
    – Jetchisel
    Mar 19, 2020 at 22:06
  • What are you expecting that test against *git* to do? Do you intend to test against the filename, or, like you're currently doing, the whole pathname?
    – Kusalananda
    Mar 19, 2020 at 22:36
  • I want the script to ignore the .git folder. It doesn't need to be perfect, but if you have a better way I'd appreciate it. Mar 19, 2020 at 22:46

1 Answer 1


To run dos2unix --newline on every regular file in and below the current directory, avoiding any file whose name contains the string git:

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

That is, find all regular files whose names do not match the pattern *git*, and run dos2unix --newline on all of these in as large batches as possible at once. Change ! -name '*git*' to ! -path '*git*' to avoid any file whose pathname contains the string git (e.g. files in .git directories).

To explicitly avoid any .git directories, but to include anything else that may have git in its name:

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

This stops find form even entering any directory called .git by using -prune to delete such paths from the search tree.

Answer before edit to the question:

Your function prints out its first argument only. The dot is the top-level search path that you use with find. It gets passed as you don't do any particular filtering of the directory entries (as with, for example, -type f for regular files only, or -name, or any other type of find test).

If you want your function to print each of its arguments, use either

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

which lets printf print each of the arguments with a newline in-between, or

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

which loops over the arguments and calls printf once for each.

This would be expected to work correctly if you called the function like

my_function "$@"

from within your in-line bash -c script. The "$@" expands to all the arguments given to the script, individually quoted.

Another way would be to move the looping into the bash -c script:

for pathname do
    my_function "$pathname"

and then have

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

This would be explicitly doing what you say you'd want to do, i.e. call the function once for each pathname.

The find command would then look either like

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

or, arguably a bit more readable,

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

This would, incidentally, be the same as

shopt -s globstar nullglob dotglob

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

except that . would not be processed. Using this, you would not have to export your my_function function.

With the loop inside the function (as in the first two pieces of code in this answer), this would be shortened to

shopt -s globstar nullglob dotglob

my_function ./**/*
  • One word about why you pass bash before {} would really be useful. It took me a while to understand it.
    – Enlico
    Jul 8, 2022 at 10:37
  • @Enlico It's a string that is put into $0 and should be the name of the shell (it will be used in error messages). Without that string, the first found file would be passed to the bash -c script in $0, which means it would not be part of the loop.
    – Kusalananda
    Jul 8, 2022 at 10:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .