我今天在午休期间编写了一个 bash 脚本,该脚本在目录中查找无扩展名文件并向这些文件附加文件扩展名。
该脚本相对较长,因为我添加了一堆标志和目录选择以及是否复制或覆盖文件之类的内容,但其功能的主要功能可以简单地通过以下方式复制:
#recursively find files in current directory that have no extension
for i in $(find . -type f ! -name "*.*"); do
#guess that extension using file
extfile=$(file --extension --brief $i)
#select the first extension in the event file spits something weird (e.g. jpeg/jpe/jfif)
extawk=$(echo $extfile | awk -F/ '{print $1}')
#copy the file to a file appended with the extension guessed from the former commands
cp -av $i $i.$extawk
done
在我的实际脚本中,它有点整洁——我只是想在这里分割命令,这样我就可以评论我这样做的原因。
我的问题:以我选择的方式find
结合使用file
可能不是最简单的方法——什么是最好的如何在多个目录中递归猜测和附加大量不同文件类型的扩展名?
答案1
for x in $(find …)
失败了包含空格(常见)或通配符(不太常见)的文件名。永远不要解析find
.使用-exec
。
让我们构建一个 zmv 命令来执行您想要的操作。首先,让我们构建搜索模式:
autoload zmv
zmv -C -o -a -n -Q '(*/)#^*.*(.)' …
-C
导致文件被复制而不是移动。-o -a
传递-a
到cp
.-n
意味着不采取行动,只打印将要做什么。一旦你感到高兴就将其删除。-v
如果您想执行操作但也想打印正在执行的操作,请将其替换为。-Q
使全局限定符在模式中。(*/)#
匹配零个或多个目录。它使用#
全局运算符(extended_glob
在 zmv 中始终启用)。^*.*
使用^
glob 运算符来匹配.
名称中不含 a 的文件。(.)
是一个 glob 限定符,它将匹配限制为常规文件。…
将被替换文本替换。这可以用来$f
指代原始名称。
zmv
在执行任何替换之前计算所有替换名称,如果任何替换名称已存在或存在冲突,则会抱怨。替换名称与原始名称相同的文件将被跳过。
现在让我们构建替换文本。我们会用到很多参数扩展特征。
- 请求
file
延期:$(file --extension --brief -- $f)
- 前置一个
.
, 准备替换:($(echo -n .; file --extension --brief -- $f)
这也可以通过参数扩展来完成:${:-.$(…)}
。) - 如果有多个建议的扩展名(用斜杠分隔),则仅保留第一个:
${$(echo -n .; file --extension --brief -- $f)%%/*}
- 如果建议的扩展名为空 或
???
,则放弃(将.
或替换.???
为空字符串):${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}
- 将添加的扩展名附加到
$f
(原始名称)。如果我们附加的内容为空,则该文件将保持不变。
结果命令:
zmv -C -o -a -n -Q '(*/)#^*.*(.)' '$f${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}'
这有点神秘,您可能更愿意将生成替换的代码放在函数中并使用zmv … '$(add_extension $f)'
.
答案2
我认为最有效的方法是将文件的 mime 类型与位于 的数据库进行比较/usr/share/mime/globs
。
- 球体在Linux中是文件扩展名。给出的示例,输出来自全局文件
application/x-mswinurl:*.url
text/x-mrml:*.mrl
text/x-erlang:*.erl
audio/x-pn-audibleaudio:*.aa
application/x-bzip-compressed-tar:*.tbz2
application/x-netshow-channel:*.nsc
application/x-hdf:*.h4
application/pgp-keys:*.key
text/x-idl:*.idl
text/x-chdr:*.h
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.visio:*.vsd
application/x-hdf:*.h5
video/vnd.mpegurl:*.m4u
- 在描述了类型示例 --> 后
text/x-erlang
,它告诉 Linux 将所有文件识别*.
为埃尔兰带有扩展名.erl
[glob],这就是为什么 -->*.erl
- 您可以添加自己的扩展名以计入
/etc/magic
文件中
所以运行命令:
mimetype -bM file
b
论证只是告诉你type-app/extension
(简短)M
论证手段魔法Linux 以字节码、十六进制、二进制检查文件的方式,以验证文件是否确实如其所声称的那样。模仿型不返回
/jpg/png/webp
只返回一种类型,并且它比file --mime-type file
返回:
image/webp
最后的想法
mimetype
最适合与二进制文件例如 PDF、图像、视频。这是因为它可以检查二进制文件,相反,text plain
只是这样,你需要识别一些东西,这更复杂,这就是为什么文本编辑器可以识别不同的编程语言,它需要用户和服务器语言的帮助每种编程语言。
对于递归,我认为树很好:
tree -FIi '*.*' | grep -v /$
- 参数
F
是将/
[slash] 添加到目录,例如folder
→folder/
- 参数
I
是选择模式的相反*.*
[这意味着选择所有具有扩展名的文件],所以相反的不是扩展名 - 参数
i
是从树输出中删除空格 grep -v
是选择反向,这就是为什么你添加 -F/
参数树命令位于开头,因此您可以删除目录并仅获取文件,扩展名为/$
.
在这里查看更多信息哑剧类型