查找不包含文件的目录

查找不包含文件的目录

是的,我正在整理我的音乐。我按照以下的原则把所有东西都整理得井井有条:/Artist/Album/Track - Artist - Title.ext如果存在,封面就放在里面/Artist/Album/cover.(jpg|png)

我想扫描所有二级目录并找到没有封面的目录。二级目录的意思是,我不在乎它是否没有/Britney Spears/cover.jpg,但我会关心它是否/Britney Spears/In The Zone/没有 cover.jpg。

不要担心封面下载(这对我明天来说是一个有趣的项目)我只关心相反的find例子的光荣的bash-fuiness。

答案1

情况 1:您知道要查找的确切文件名

使用findwithtest -e your_file检查文件是否存在。例如,查找没有文件的目录cover.jpg

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

但它区分大小写。

案例 2:你想更加灵活

您不确定这种情况,扩展名可能是jPgpng...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

解释:

  • 您需要sh为每个目录生成一个 shell,因为使用时无法进行管道传输find
  • ls -1 "{}"find仅输出当前正在遍历的目录的文件名
  • egrep(而不是grep)使用扩展正则表达式;-i使搜索不区分大小写,-q使其省略任何输出
  • "^cover\.(jpg|png)$"是搜索模式。在此示例中,它匹配例如cOver.pngCover.JPGcover.png.必须转义 ,否则意味着它匹配任何字符。^标记行的开始和$结束

egrep 的其他搜索模式示例

将该部分替换egrep -i -q "^cover\.(jpg|png)$"为:

  • egrep -i -q "cover\.(jpg|png)$":也匹配cd_cover.pngalbum_cover.JPG...
  • egrep -q "^cover\.(jpg|png)$": 匹配cover.png, cover.jpg, 但不匹配Cover.jpg(不关闭区分大小写)
  • egrep -iq "^(cover|front)\.jpg$": 匹配例如front.jpgCover.JPG但是不是 Cover.PNG

有关更多信息,请查看常用表达

答案2

事实证明,这很简单。下面获取带有封面的目录列表,并将其与所有二级目录的列表进行比较。两个“文件”中出现的行被抑制,留下需要封面的目录列表。

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

万岁。

笔记:

  • comm的论点如下:

    • -1隐藏 file1 特有的行
    • -2隐藏 file2 特有的行
    • -3抑制同时出现在两个文件中的行
  • comm只接受文件,因此采用了古怪的<(...)输入法。它通过真实的 [临时] 文件传输内容。

  • comm需要对输入进行排序,否则它将无法工作,并且find绝不保证顺序。它还必须是唯一的。第一个find操作可能会找到多个文件,cover.*因此可能会有重复的条目。sort -u快速将它们整理为一个。第二个查找始终是唯一的。

  • dirnamesed是一个方便的工具,无需借助(等)即可获取文件的目录。

  • findcomm的输出都有点混乱。最后一个sed用来清理一下,所以你只剩下Artist/Album。这对你来说可能是或可能不是理想的。

答案3

使用通配符来解决这个问题比使用查找要好得多。

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

现在假设这个良好的结构中没有杂散文件。当前目录仅包含艺术家子目录,而这些子目录仅包含专辑子目录。然后我们可以执行以下操作:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

语法<(...)是 Bash 进程替换:它允许您使用命令代替文件参数。它允许您将命令的输出视为文件。因此,我们可以运行两个程序并获取它们的差异,而无需将它们的输出保存在临时文件中。程序diff认为它正在处理两个文件,但实际上它正在从两个管道读取。

生成右侧输入的命令diffprintf "%s\n" */*列出专辑目录。左侧命令遍历路径*.cover并打印其目录名称。

测试运行:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

啊哈,a/bfoo/bar目录没有cover.jpg

有一些不合理的极端情况,比如默认情况下,*如果它不匹配任何内容,则会扩展为自身。这可以通过 Bash 的 来解决set -o nullglob

答案4

ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

将显示所有不包含 txt 文件的目录。

相关内容