查找不包含文件的目录

是的,我正在整理我的音乐。 我在以下口头禅中精心安排了所有内容: /Artist/Album/Track - Artist - Title.ext ,如果存在,封面位于/Artist/Album/Track - Artist - Title.ext cover.( /Artist/Album/cover.(jpg|png)

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

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

案例1:您知道要查找的确切文件名

使用find with test -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 

说明:

  • 您需要为每个目录生成一个shell sh ,因为使用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.pngcover.jpg ,但不是Cover.jpg (区分大小写未关闭)
  • egrep -iq "^(cover|front)\.jpg$" :匹配例如front.jpgCover.JPG不是 Cover.PNG

有关详细信息,请查看正则表达式 。

简单,它发生了。 以下内容获取带有封面的目录列表,并将其与所有第二级目录的列表进行比较。 两个“文件”中出现的行都被抑制,留下需要覆盖的目录列表。

 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只接受文件,因此是kooky <(...)输入法。 这通过真实的[临时]文件管理内容。

  • comm需要排序输入或它不起作用, find并不能保证订单。 它也需要是独一无二的。 第一个find操作可以找到多个文件用于cover.*因此可能存在重复的条目。 sort -u迅速将那些减少到一个。 第二个发现总是独一无二的。

  • dirname是一个方便的工具,用于获取文件的目录而无需诉诸sed (等)。

  • findcomm对它们的输出都有点混乱。 最后的sed是为了清理东西,所以你留下了Artist/Album 。 这对您来说可能是也可能不合适。

使用globbing比使用find更好地解决这个问题。

 $ 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程序认为它正在使用两个文件,但实际上它是从两个管道中读取的。

产生diff右手输入的命令, printf "%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来解决。