How to loop through file names returned by find?

前端 未结 13 1127
野性不改
野性不改 2020-11-22 04:20
x=$(find . -name \"*.txt\")
echo $x

if I run the above piece of code in Bash shell, what I get is a string containing several file names separated

相关标签:
13条回答
  • 2020-11-22 04:29

    based on other answers and comment of @phk, using fd #3:
    (which still allows to use stdin inside the loop)

    while IFS= read -r f <&3; do
        echo "$f"
    
    done 3< <(find . -iname "*filename*")
    
    0 讨论(0)
  • 2020-11-22 04:35

    What ever you do, don't use a for loop:

    # Don't do this
    for file in $(find . -name "*.txt")
    do
        …code using "$file"
    done
    

    Three reasons:

    • For the for loop to even start, the find must run to completion.
    • If a file name has any whitespace (including space, tab or newline) in it, it will be treated as two separate names.
    • Although now unlikely, you can overrun your command line buffer. Imagine if your command line buffer holds 32KB, and your for loop returns 40KB of text. That last 8KB will be dropped right off your for loop and you'll never know it.

    Always use a while read construct:

    find . -name "*.txt" -print0 | while read -d $'\0' file
    do
        …code using "$file"
    done
    

    The loop will execute while the find command is executing. Plus, this command will work even if a file name is returned with whitespace in it. And, you won't overflow your command line buffer.

    The -print0 will use the NULL as a file separator instead of a newline and the -d $'\0' will use NULL as the separator while reading.

    0 讨论(0)
  • 2020-11-22 04:37

    How about if you use grep instead of find?

    ls | grep .txt$ > out.txt
    

    Now you can read this file and the filenames are in the form of a list.

    0 讨论(0)
  • 2020-11-22 04:38

    (Updated to include @Socowi's execellent speed improvement)

    With any $SHELL that supports it (dash/zsh/bash...):

    find . -name "*.txt" -exec $SHELL -c '
        for i in "$@" ; do
            echo "$i"
        done
    ' {} +
    

    Done.


    Original answer (shorter, but slower):

    find . -name "*.txt" -exec $SHELL -c '
        echo "$0"
    ' {} \;
    
    0 讨论(0)
  • 2020-11-22 04:39

    find <path> -xdev -type f -name *.txt -exec ls -l {} \;

    This will list the files and give details about attributes.

    0 讨论(0)
  • 2020-11-22 04:41

    I like to use find which is first assigned to variable and IFS switched to new line as follow:

    FilesFound=$(find . -name "*.txt")
    
    IFSbkp="$IFS"
    IFS=$'\n'
    counter=1;
    for file in $FilesFound; do
        echo "${counter}: ${file}"
        let counter++;
    done
    IFS="$IFSbkp"
    

    Just in case you would like to repeat more actions on the same set of DATA and find is very slow on your server (I/0 high utilization)

    0 讨论(0)
提交回复
热议问题