我有一个音乐文件夹,其内容如下:
音乐 —艺术家A -歌曲1.mp3 -专辑mA -歌曲2.mp3 -歌曲2.m4a —专辑B -歌曲1.mp3 -歌曲2.mp3 -歌曲3.mp3 —专辑C -歌曲1.mp3 -歌曲1.jpg -某些文件夹1 -歌曲1.mp3 —艺术家B -歌曲1.mp3 —艺术家C -歌曲1.mp3 -歌曲2.mp3 -歌曲3.mp3 -某些文件夹2 -歌曲1.jpg -歌曲2.jpg -某些文件夹3
我正在寻找这样的输出:
艺术家 A/歌曲 1.mp3 艺术家 A/专辑 A/歌曲 2.mp3 艺术家 A/专辑 C/歌曲 1.mp3 艺术家 A/专辑 C/SomeFolder1/歌曲 1.mp3 艺术家B/歌曲1.mp3
因此,我不想要只有一个文件的目录,我想要只有一个 mp3 的目录 - 不管子目录或其他文件。我也不想要空目录,或者包含多个 mp3 的目录。
我并不反对 Linux 的 shell 脚本。但是,我不想在任何一个操作系统上编译程序。
我已经尝试过:
find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print
但是这给了我这个输出:
。 ./艺术家B ./艺术家A ./艺术家 A/专辑 A ./艺术家 A/专辑 C ./艺术家A/专辑C/SomeFolder1 ./ArtistC/SomeFolder2 ./ArtistC/SomeFolder3
虽然很接近,但不是我想要的。有两个空文件夹,没有文件名。
答案1
乍一看,第一个命令报告空文件夹(没有*.mp3
文件的文件夹)的原因在于您说的-le 1
是 而不是-eq 1
。这看起来会报告包含一个或更少 MP3 文件的目录。但是更改-le 1
为-eq 1
不起作用。事实证明,问题是,在包含 ≥ 一个 MP3 文件的目录中,
directory_name/*.mp3
会为您提供 MP3 文件名列表,但是,在没有文件的目录中,它仍然保持为directory_name/*.mp3
。可以通过告诉 shell 不匹配任何文件的通配符应该从命令行中消失来解决这个问题:
查找 .-type d-exec 響 -c 'shopt -s nullglob;设置——“$0”/*.mp3;[$#-eq 1]'{}\;-print
(全部输入为一行)结果
./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistB
但是您让 shell 统计文件数量,并要求find
打印 – 并且find
只查看目录。为什么不让 shell 打印它看到的文件名呢?
查找 .-type d-exec sh -c'shopt-s nullglob;设置——“$0”/*.mp3; [$#-eq 1]&& 回显“$1”‘{} \;
(即,如果有一个文件,echo
第一个文件的名称($1
))将会提供您想要的内容。
答案2
对于使用 PowerShell 的 Windows,假设您已位于文件夹 C:\YOUR_PATH 中:
PS C:\YOUR_PATH> (Get-ChildItem -recurse | Where-Object {$_.name -match ".mp3"}) | Group-Object -Property Directory | Where-Object {$_.Count -eq 1} | ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}
输出:
C:\YOUR_PATH\ArtistA\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumA\Song2.mp3
C:\YOUR_PATH\ArtistA\AlbumC\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumC\SomeFolder1/Song1.mp3
C:\YOUR_PATH\ArtistB\Song1.mp3
分解管道命令:
获取当前目录下的所有文件和文件夹
Get-ChildItem -recurse
仅根据您的掩码匹配文件
Where-Object {$_.name -match ".mp3"})
根据目录属性执行分组
Group-Object -Property Directory
仅选择包含 1 个项目的目录
Where-Object {$_.Count -eq 1}
提取每个目录中与文件掩码匹配的一个文件的全名
ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}
答案3
我确信有人有更好的方法来实现这一点,但是这个 bash 命令可以在 Linux 上运行。
for Folder in $(find . -name "*.mp3" -printf "%h\n")
do
[[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
done
此脚本对每个 .mp3 文件的文件夹执行测试,对同一文件的每个匹配项重复执行测试。例如,它会检查 Music/ArtistA/AlbumB 3 次。根据您拥有的 mp3 文件数量,这可能需要很长时间(您需要一个非常大的库)。
如果是这样,你可以让它稍微复杂一点:
for Folder in $(find . -name "*.mp3" -printf "%h\n" | awk '!x[$0]++')
do
[[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
done
结果是一样的,但它只会检查每个文件夹一次,这可以节省大量时间。awk 命令会删除任何重复的文件夹名称。
另一个注意事项是,Linux 上的文件区分大小写,因此这将完全忽略任何.MP3
或.Mp3
文件。如果您希望它匹配这些文件,请将所有 3 个实例更改mp3
为[mM][pP]3
答案4
以下是答案:
find Music -name "*.mp3" -exec dirname {} \; |
sort |
uniq -c |
while read count parent
do
if [[ $count -eq 1 ]]
then
echo "$parent"/*.mp3
fi
done