x=$(find . -name "*.txt")
echo $x
如果我在Bash shell中运行上面的代码段,我得到的是一个包含几个由空白分隔的文件名的字符串,而不是一个列表。
当然,我可以进一步用空白分隔它们以得到一个列表,但我相信有更好的方法来做到这一点。
那么,循环查找命令结果的最佳方法是什么呢?
x=$(find . -name "*.txt")
echo $x
如果我在Bash shell中运行上面的代码段,我得到的是一个包含几个由空白分隔的文件名的字符串,而不是一个列表。
当然,我可以进一步用空白分隔它们以得到一个列表,但我相信有更好的方法来做到这一点。
那么,循环查找命令结果的最佳方法是什么呢?
当前回答
你可以把find返回的文件名放入这样一个数组:
array=()
while IFS= read -r -d ''; do
array+=("$REPLY")
done < <(find . -name '*.txt' -print0)
现在,您可以循环遍历数组以访问单个项,并对它们做任何您想做的事情。
注意:它是空白安全的。
其他回答
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一个接一个地把它们捡起来,然后……
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循环可以读取它。
另一种选择是不使用bash,而是调用Python来完成繁重的工作。我反复使用这个方法是因为bash解决方案作为我的另一个答案太慢了。
使用这个解决方案,我们从内联Python脚本构建一个bash文件数组:
#!/bin/bash
set -eu -o pipefail
dsep=":" # directory_separator
base_directory=/tmp
all_files=()
all_files_string="$(python3 -c '#!/usr/bin/env python3
import os
import sys
dsep="'"$dsep"'"
base_directory="'"$base_directory"'"
def log(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def check_invalid_characther(file_path):
for thing in ("\\", "\n"):
if thing in file_path:
raise RuntimeError(f"It is not allowed {thing} on \"{file_path}\"!")
def absolute_path_to_relative(base_directory, file_path):
relative_path = os.path.commonprefix( [ base_directory, file_path ] )
relative_path = os.path.normpath( file_path.replace( relative_path, "" ) )
# if you use Windows Python, it accepts / instead of \\
# if you have \ on your files names, rename them or comment this
relative_path = relative_path.replace("\\", "/")
if relative_path.startswith( "/" ):
relative_path = relative_path[1:]
return relative_path
for directory, directories, files in os.walk(base_directory):
for file in files:
local_file_path = os.path.join(directory, file)
local_file_name = absolute_path_to_relative(base_directory, local_file_path)
log(f"local_file_name {local_file_name}.")
check_invalid_characther(local_file_name)
print(f"{base_directory}{dsep}{local_file_name}")
' | dos2unix)";
if [[ -n "$all_files_string" ]];
then
readarray -t temp <<< "$all_files_string";
all_files+=("${temp[@]}");
fi;
for item in "${all_files[@]}";
do
OLD_IFS="$IFS"; IFS="$dsep";
read -r base_directory local_file_name <<< "$item"; IFS="$OLD_IFS";
printf 'item "%s", base_directory "%s", local_file_name "%s".\n' \
"$item" \
"$base_directory" \
"$local_file_name";
done;
相关:
操作系统。不用隐藏文件夹行走 如何做一个递归子文件夹搜索和返回文件在一个列表? 如何在Bash中将字符串分割成数组?
正如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?
如果你可以假设文件名不包含换行符,你可以使用以下命令将find的输出读入Bash数组:
readarray -t x < <(find . -name '*.txt')
注意:
-t导致readarray删除换行符。 如果readarray在管道中,它将不起作用,因此需要进行进程替换。 readarray从Bash 4开始就可用了。
Bash 4.4及更高版本还支持-d参数来指定分隔符。使用空字符而不是换行符来分隔文件名也适用于文件名包含换行符的罕见情况:
readarray -d '' x < <(find . -name '*.txt' -print0)
Readarray也可以作为具有相同选项的mapfile调用。
参考:https://mywiki.wooledge.org/BashFAQ/005 # Loading_lines_from_a_file_or_stream