x=$(find . -name "*.txt")
echo $x

如果我在Bash shell中运行上面的代码段,我得到的是一个包含几个由空白分隔的文件名的字符串,而不是一个列表。

当然,我可以进一步用空白分隔它们以得到一个列表,但我相信有更好的方法来做到这一点。

那么,循环查找命令结果的最佳方法是什么呢?


当前回答

如果使用grep而不是find呢?

ls | grep .txt$ > out.txt

现在您可以读取这个文件,文件名以列表的形式出现。

其他回答

正如Kevin已经在上面的答案中发布的那样,最好的解决方案是使用bash glob的for循环,但由于bash glob默认情况下不是递归的,这可以通过bash递归函数来修复:

#!/bin/bash
set -x
set -eu -o pipefail

all_files=();

function get_all_the_files()
{
    directory="$1";
    for item in "$directory"/* "$directory"/.[^.]*;
    do
        if [[ -d "$item" ]];
        then
            get_all_the_files "$item";
        else
            all_files+=("$item");
        fi;
    done;
}

get_all_the_files "/tmp";

for file_path in "${all_files[@]}"
do
    printf 'My file is "%s"\n' "$file_path";
done;

相关问题:

Bash loop through directory including hidden file Recursively list files from a given directory in Bash ls command: how can I get a recursive full-path listing, one line per file? List files recursively in Linux CLI with path relative to the current directory Recursively List all directories and files bash script, create array of all files in a directory How can I creates array that contains the names of all the files in a folder? How can I creates array that contains the names of all the files in a folder? How to get the list of files in a directory in a shell script?

根据@phk的其他回答和评论,使用fd #3: (仍然允许在循环中使用stdin)

while IFS= read -r f <&3; do
    echo "$f"

done 3< <(find . -iname "*filename*")

无论你做什么,都不要使用for循环:

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

三个原因:

为了使For循环开始,find必须运行到完成。 如果一个文件名中有任何空格(包括空格、制表符或换行符),它将被视为两个独立的名称。 虽然现在不太可能,但您可以溢出命令行缓冲区。想象一下,如果您的命令行缓冲区有32KB,而for循环返回40KB的文本。最后的8KB将从for循环中删除,而您永远不会知道。


总是使用while read结构:

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

循环将在执行find命令时执行。另外,即使返回的文件名中有空格,该命令也可以工作。而且,不会溢出命令行缓冲区。

print0将使用NULL作为文件分隔符而不是换行符,而-d $'\0'将在读取时使用NULL作为分隔符。

(更新到包括@Socowi的卓越速度提升)

使用任何支持它的$SHELL (dash/zsh/bash…):

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

完成了。


原来的答案(更短,但更慢):

find . -name "*.txt" -exec $SHELL -c '
    echo "$0"
' {} \;
find . -name "*.txt"|while read fname; do
  echo "$fname"
done

注意:此方法和bmargulies所示的(第二个)方法在文件/文件夹名称中使用空白时是安全的。

为了在文件/文件夹名称中包含换行符,你必须使用find的-exec谓词,就像这样:

find . -name '*.txt' -exec echo "{}" \;

{}是找到的项的占位符,\;用于终止-exec谓词。

为了完整起见,让我添加另一个变体-你必须喜欢*nix的多功能性:

find . -name '*.txt' -print0|xargs -0 -n 1 echo

这将使用\0字符分隔打印项,据我所知,在任何文件系统的文件或文件夹名称中都不允许使用\0字符,因此应该涵盖所有基础。Xargs一个接一个地把它们捡起来,然后……