我想要以下格式的输出:
/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 扩展find
和sort
因此不再是标准。
或者,您可以使用 zsh shell 代替 bash。它的 glob 默认情况下按词法排序,就像在所有 shell 中一样,但也允许按文件类型进行过滤(使用限定符.
相当于-type f
此处的示例)并支持递归 globbing:
print -rC1 /media/username/**/*.lua(ND.)
其他一些替代方案find
是rawhide
使用类似 C 的查询语言:
LC_ALL=C rh -e '"*.lua" && file' /media/username
或者fd-find
fdfind
,有时在某些系统上可用:
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'
withfind
(并且您不再需要LC_ALL=C
) - 在 zsh 中更改
**/*.lua
为,**/*
- 更改
"*.lua" && file
为仅file
(或什至f
) inrh
(并且再次不需要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 的文件.lua
,lua
不是扩展名,这实际上可能是理想的。要使用基于 glob 模式的方法实现这一效果,您可以将模式更改为*?.lua
或?*.lua
以确保LC_ALL=C
扩展名之前至少有一个字符(带有 的一个字节)。
答案2
启用globstar
和extglob
后,您可以递归地查找与特定模式匹配的文件名。
shopt -s globstar extglob
例如,要media/
使用mp3
、flac
或打印该端下的所有文件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 '{}' \;