查找不包含文件的目录
是的,我正在整理我的音乐。 我在以下口头禅中精心安排了所有内容: /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:你想要更灵活
你不确定这个案子,扩展名可能是jPg
, png
……
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.png
,Cover.JPG
或cover.png
。 这个.
必须被转义,否则意味着它匹配任何角色。^
标记该行的开头,$
结束
egrep的其他搜索模式示例 :
用以下内容替换egrep -i -q "^cover\.(jpg|png)$"
部分:
-
egrep -i -q "cover\.(jpg|png)$"
:还匹配cd_cover.png
,album_cover.JPG
…… -
egrep -q "^cover\.(jpg|png)$"
:匹配cover.png
,cover.jpg
,但不是Cover.jpg
(区分大小写未关闭) -
egrep -iq "^(cover|front)\.jpg$"
:匹配例如front.jpg
,Cover.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
(等)。 -
find
和comm
对它们的输出都有点混乱。 最后的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/b
和foo/bar
目录没有cover.jpg
。
有一些破碎的角落情况,默认情况下*
如果没有匹配就会扩展到自身。 这可以通过Bash的set -o nullglob
来解决。