![查找不包含文件的目录](https://linux22.com/image/825274/%E6%9F%A5%E6%89%BE%E4%B8%8D%E5%8C%85%E5%90%AB%E6%96%87%E4%BB%B6%E7%9A%84%E7%9B%AE%E5%BD%95.png)
是的,我正在整理我的音乐。我按照以下的原则把所有东西都整理得井井有条:/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:您知道要查找的确切文件名
使用find
withtest -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
解释:
- 您需要
sh
为每个目录生成一个 shell,因为使用时无法进行管道传输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
有关更多信息,请查看常用表达。
答案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
快速将它们整理为一个。第二个查找始终是唯一的。dirname
sed
是一个方便的工具,无需借助(等)即可获取文件的目录。find
和comm
的输出都有点混乱。最后一个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
认为它正在处理两个文件,但实际上它正在从两个管道读取。
生成右侧输入的命令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
。
答案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 文件的目录。