x=$(find . -name "*.txt")
echo $x
如果我在Bash shell中运行上面的代码段,我得到的是一个包含几个由空白分隔的文件名的字符串,而不是一个列表。
当然,我可以进一步用空白分隔它们以得到一个列表,但我相信有更好的方法来做到这一点。
那么,循环查找命令结果的最佳方法是什么呢?
x=$(find . -name "*.txt")
echo $x
如果我在Bash shell中运行上面的代码段,我得到的是一个包含几个由空白分隔的文件名的字符串,而不是一个列表。
当然,我可以进一步用空白分隔它们以得到一个列表,但我相信有更好的方法来做到这一点。
那么,循环查找命令结果的最佳方法是什么呢?
当前回答
我喜欢使用find,这是第一次分配给变量和IFS切换到新行如下:
FilesFound=$(find . -name "*.txt")
IFSbkp="$IFS"
IFS=$'\n'
counter=1;
for file in $FilesFound; do
echo "${counter}: ${file}"
let counter++;
done
IFS="$IFSbkp"
正如@Konrad Rudolph所评论的那样,这将不适用于文件名中的“新行”。我仍然认为它很方便,因为它涵盖了需要遍历命令输出的大多数情况。
其他回答
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一个接一个地把它们捡起来,然后……
你可以存储你的查找输出在数组中,如果你希望以后使用输出:
array=($(find . -name "*.txt"))
现在要打印new line中的每个元素,可以使用for循环迭代数组的所有元素,也可以使用printf语句。
for i in ${array[@]};do echo $i; done
or
printf '%s\n' "${array[@]}"
你还可以使用:
for file in "`find . -name "*.txt"`"; do echo "$file"; done
这将以换行符打印每个文件名
若要仅以列表形式打印查找输出,可以使用以下方法之一:
find . -name "*.txt" -print 2>/dev/null
or
find . -name "*.txt" -print | grep -v 'Permission denied'
这将删除错误消息,并仅在新行中输出文件名。
如果您希望对文件名做一些事情,将其存储在数组中是很好的,否则不需要占用该空间,您可以直接从find输出。
你可以把find返回的文件名放入这样一个数组:
array=()
while IFS= read -r -d ''; do
array+=("$REPLY")
done < <(find . -name '*.txt' -print0)
现在,您可以循环遍历数组以访问单个项,并对它们做任何您想做的事情。
注意:它是空白安全的。
另一种选择是不使用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中将字符串分割成数组?
我喜欢使用find,这是第一次分配给变量和IFS切换到新行如下:
FilesFound=$(find . -name "*.txt")
IFSbkp="$IFS"
IFS=$'\n'
counter=1;
for file in $FilesFound; do
echo "${counter}: ${file}"
let counter++;
done
IFS="$IFSbkp"
正如@Konrad Rudolph所评论的那样,这将不适用于文件名中的“新行”。我仍然认为它很方便,因为它涵盖了需要遍历命令输出的大多数情况。