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

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

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

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


当前回答

function loop_through(){
        length_="$(find . -name '*.txt' | wc -l)"
        length_="${length_#"${length_%%[![:space:]]*}"}"
        length_="${length_%"${length_##*[![:space:]]}"}"   
        for i in {1..$length_}
        do
            x=$(find . -name '*.txt' | sort | head -$i | tail -1)
            echo $x
        done

}

为了获取循环文件列表的长度,我使用了第一个命令“wc -l”。 该命令被设置为一个变量。 然后,我需要从变量中删除尾随的空格,以便for循环可以读取它。

其他回答

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

这将列出文件并给出有关属性的详细信息。

如果使用grep而不是find呢?

ls | grep .txt$ > out.txt

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

你可以把find返回的文件名放入这样一个数组:

array=()
while IFS=  read -r -d ''; do
    array+=("$REPLY")
done < <(find . -name '*.txt' -print0)

现在,您可以循环遍历数组以访问单个项,并对它们做任何您想做的事情。

注意:它是空白安全的。

TL;DR:如果你只是想知道最正确的答案,你可能想知道我的个人偏好(见本文底部):

# execute `process` once for each file
find . -name '*.txt' -exec process {} \;

如果有时间,请通读其余部分,了解几种不同的方法以及其中大多数方法的问题。


完整的答案是:

最好的方法取决于你想做什么,但这里有一些选择。只要子树中没有文件名中有空格的文件或文件夹,你就可以遍历这些文件:

for i in $x; do # Not recommended, will break on whitespace
    process "$i"
done

稍微好一点,去掉临时变量x:

for i in $(find -name \*.txt); do # Not recommended, will break on whitespace
    process "$i"
done

当你可以的时候,最好是glob。空白安全,对于当前目录中的文件:

for i in *.txt; do # Whitespace-safe but not recursive.
    process "$i"
done

通过启用globstar选项,你可以glob所有匹配的文件在这个目录和所有子目录:

# Make sure globstar is enabled
shopt -s globstar
for i in **/*.txt; do # Whitespace-safe and recursive
    process "$i"
done

在某些情况下,例如,如果文件名已经在文件中,你可能需要使用read:

# IFS= makes sure it doesn't trim leading and trailing whitespace
# -r prevents interpretation of \ escapes.
while IFS= read -r line; do # Whitespace-safe EXCEPT newlines
    process "$line"
done < filename

通过适当设置分隔符,Read可以安全地与find结合使用:

find . -name '*.txt' -print0 | 
    while IFS= read -r -d '' line; do 
        process "$line"
    done

对于更复杂的搜索,你可能会使用find,或者带-exec选项,或者带-print0 | xargs -0:

# execute `process` once for each file
find . -name \*.txt -exec process {} \;

# execute `process` once with all the files as arguments*:
find . -name \*.txt -exec process {} +

# using xargs*
find . -name \*.txt -print0 | xargs -0 process

# using xargs with arguments after each filename (implies one run per filename)
find . -name \*.txt -print0 | xargs -0 -I{} process {} argument

Find还可以在运行命令之前使用-execdir而不是-exec来CD到每个文件的目录,并且可以使用-ok而不是-exec(或-okdir而不是-execdir)来进行交互(在为每个文件运行命令之前提示)。

*:从技术上讲,find和xargs(默认情况下)都会在命令行中使用尽可能多的参数运行命令,次数与遍历所有文件所需的次数相同。在实践中,除非您有非常多的文件,否则这并不重要,如果您超过了长度,但需要在同一个命令行上全部使用它们,那么SOL就会找到不同的方法。

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

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

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