如何递归地获取文件列表?

如何递归地获取文件列表?

我想要以下格式的输出:

/media/username/New/Audio/time machine by Ariya Hedie.mp3
/media/username/New/Audio/You areever alone by Dj Quads.flac
/media/username/New/Audio/You'rehe Boss by The Barr Brothers.opus
/media/username/New/Audio/Your Wedding byosimo Fogg.mp3
/media/username/New/Audio/You,y Love by Floppyircus.opus
/media/username/New/Library/Yup byteven O'Brien.flac
/media/username/New/Library/Zen by Warptech.opus
/media/username/New/Library/無機質な怒りに対する優しさの反論.opus
/media/username/New/Library/鎖 -hain Border byaoyaakamata.opus

这些文件是不同格式的音乐文件,opus、mp3 等,文件夹不包含非音乐文件。文件名通常包含 Unicode 字符。

答案1

.lua如果您的意思是您想要名称以目录中或目录下结尾的常规文件的完整路径/media/username,那么您可以使用标准find.

LC_ALL=C find /media/username -name '*.lua' -type f

如果您希望该列表按词法排序,您可以将其通过管道传输到sort,但这仅适用于特殊情况,即没有文件路径包含换行符,并且最终可能会分散多行文件路径的行。例如:

/media/username/my
dir/my
strange file.lua

一旦通过管道传输到sort,就会变成(在大多数语言环境中):

dir/my
/media/username/my
strange file.lua

要对这些路径进行排序而不破坏多行路径,您可以执行以下操作:

LC_ALL=C find /media/username -name '*.lua' -type f -print0 |
  sort -z |
  tr '\0' '\n'

尽管这需要 GNU 扩展findsort因此不再是标准。

或者,您可以使用 zsh shell 代替 bash。它的 glob 默认情况下按词法排序,就像在所有 shell 中一样,但也允许按文件类型进行过滤(使用限定符.相当于-type f此处的示例)并支持递归 globbing:

print -rC1 /media/username/**/*.lua(ND.)

其他一些替代方案findrawhide使用类似 C 的查询语言:

LC_ALL=C rh -e '"*.lua" && file' /media/username

或者fd-findfdfind,有时在某些系统上可用:

fdfind -usgtf '*.lua' /media/username

短缺:

fdfind --unrestricted --case-sensitive --glob --type file '*.lua' /media/username

fdfind还有一个-e/--extension但它似乎不尊重-s/--case-sensitive标志,我发现它通常有一种令人讨厌的倾向,认为它比你更了解你想要什么)。

也没有fdfind内置rh支持对列表进行排序,但您可以使用相同的技巧让它们打印 NUL 分隔的列表(均带有选项-0),如果您想要的话,可以通过管道sort -z将 NUL 转换为换行符tr排序列表。

*.lua模式与 shell glob 中使用的模式相同。*匹配任意数量的字符。为了匹配任何字节序列,无论它们是否构成字符,在某些工具和某些系统或版本上,我们 C通过传递LC_ALL=C到查找命令的环境来设置区域设置。

如果您想查找任何常规文件,无论其名称如何,您可以

  • 删除-name '*.lua'with find(并且您不再需要LC_ALL=C
  • 在 zsh 中更改**/*.lua为,**/*
  • 更改"*.lua" && file为仅file(或什至f) in rh(并且再次不需要LC_ALL=C.
  • 更改'*.lua''*'使用fdfind或删除-g/--glob选项并使用.正则表达式作为模式。

.mp3要匹配名称仅以, .flac,之一结尾的文件.opus

  • '(' -name '*.flac' -o -name '*.mp3' -o -name '*.opus' ')' -type f标准find(某些find实现也支持,-regex但正则表达式风格和语法因实现而异)。
  • **/*.(flac|opus|mp3)在 zsh 中
  • "*.@(flac|opus|mp3)" && file在 rh 中(在fnmatch()支持 ksh88 扩展 glob 模式运算符的系统上),或者"\.(flac|opus|mp3)\z".re && file如果不支持则使用。
  • -ustf '\.(flac|open|mp3)$'在 fdfind 中(使用正则表达式而不是 glob 模式)。

为了不区分大小写地匹配,

  • '(' -name '*.[fF][lL][aA][cC]' -o -name '*.[mM][pP]3' -o -name '*.[oO][pP][uU][sS]' ')' -type f在标准中find(一些find实现也支持-iname-iregex)。
  • **/*.(#i)(flac|opus|mp3)在 zsh 中(需要set -o extendedglob事先)
  • "*.@(flac|opus|mp3)".i && file在 rh 中(在fnmatch()支持 ksh88 扩展 glob 模式运算符的系统上),或者"\.(flac|opus|mp3)\z".rei && file如果不支持则使用。
  • 删除fdfind 中的-s/ 。如果模式包含大写字母,--case-sensitive您还需要-i/。--ignore-case

如果您想获取数组中的文件列表,以便可以使用 zsh 对它们执行任何操作,只需执行以下操作:

set -o extendedglob
files=( /media/username/**/*.(#i)(flac|opus|mp3) )
do-anything-with $files

bash

readarray -td '' < <(find ... -print0 | sort -z)
do-anything-with "${files[@]}"

(需要 bash 4.4 或更高版本)。


1 它-e似乎也无法匹配扩展名左侧无法以 UTF-8 解码的文件名(即使在不使用 UTF-8 的语言环境中)。然而,它确实认为对于名为 just 的文件.lualua不是扩展名,这实际上可能是理想的。要使用基于 glob 模式的方法实现这一效果,您可以将模式更改为*?.lua?*.lua以确保LC_ALL=C扩展名之前至少有一个字符(带有 的一个字节)。

答案2

启用globstarextglob后,您可以递归地查找与特定模式匹配的文件名。

shopt -s globstar extglob

例如,要media/使用mp3flac或打印该端下的所有文件opus,您可以运行:

printf '%s\n' media/**/*.@(mp3|flac|opus)

如果您需要处理每个匹配的文件,您可以在不调用外部程序的情况下完成,例如find

#!/bin/bash

shopt -s globstar extglob

for file in media/**/*.@(mp3|flac|opus); do
    echo "do something with $file"
done

此外,您可以添加选项nullglob以确保在没有匹配项时不生成任何输出。


您可以使用该命令执行相同的操作find,但编写起来要困难得多,因此更容易出错:

find media/ \( \( -name '*.mp3' \) -o \( -name '*.flac' \) -o \( -name '*.opus' \) \) -exec echo do something with '{}' \;

相关内容