使用通配符优雅地将数千个 epub 文件转换为 mobi

Dim*_*ris 10 scripting wildcards

我正在对 1000 个文件执行以下命令:

ebook-convert <name-of-first-file>.epub <name-of-first-file>.mobi
ebook-convert <name-of-second-file>.epub <name-of-second-file>.mobi
Run Code Online (Sandbox Code Playgroud)

显然,我们可以为这项工作编写一个 bash 脚本,而不是手动对 1000 个文件执行此操作。

我想知道是否有一种更简单的方法可以在 Linux 中执行类似的操作,一个看起来像这样的小命令

ebook-convert *.epub *.mobi
Run Code Online (Sandbox Code Playgroud)

您能否以类似的方式使用通配符,以适用于上述场景?

Ste*_*itt 27

您可以\xe2\x80\x99t直接使用通配符执行此操作,但循环for可以让您到达那里:

\n
for epub in ./*.epub; do ebook-convert "${epub}" "${epub%.epub}.mobi"; done\n
Run Code Online (Sandbox Code Playgroud)\n

Zsh 支持这种循环的更优雅的形式

\n

如果您的文件名不包含空格字符,并且通常可以由 Make 和 shell 安全地处理,那么您可以使用 GNU Make,而不是使用 shell 脚本;把这个放在一个Makefile

\n
all: $(patsubst %.epub,%.mobi,$(wildcard *.epub))\n\n%.mobi : %.epub\n        ebook-convert ./$< ./$@\n
Run Code Online (Sandbox Code Playgroud)\n

然后运行make​​,这将确保所有.epub文件都转换为一个.mobi文件。您可以根据需要重复运行此命令来更新文件 \xe2\x80\x94 它只会构建丢失或早于源文件的文件。(确保该ebook-convert行以制表符开头,而不是空格。)

\n


Sté*_*las 9

带壳zsh

for f (./*.epub) ebook-convert $f $f:r.mobi
Run Code Online (Sandbox Code Playgroud)

扩展$f:r到根名称(没有扩展名的部分),$fcsh/ vim...

或者:

autoload -Uz zmv # best in ~/.zshrc
zmv -P ebook-convert './(*).epub' './$1.mobi`
Run Code Online (Sandbox Code Playgroud)

(由于ebook-convert似乎无法识别--选项分隔符,我们必须使用./前缀并使用-P而不是-p能够处理以 开头的文件名-

zmv主要用于批量重命名,但也用于使用-C/进行批量复制/链接-L,或者可以扩展到任何形式的更改/转换...通过指定程序使用-p/来执行此操作-P

或者:

autoload -Uz zmv
alias ebc='noglob zmv -WP ebook-convert'
ebc ./*.epub ./*.mobi
Run Code Online (Sandbox Code Playgroud)

使用-W,zmv捕获源模式上的所有通配符,并在替换中使用递增${1}、等转换所有通配符。${2}所以zmv -W './*.epub' './*.mobi'与 相同zmv -W './(*).epub' './${1}.mobi'noglob禁用命令参数中的通配符,从而避免引用。

或者:

autoload -Uz zargs # best in ~/.zshrc
zargs -I@ ./*.epub(:r) -- ebook-convert @.epub @.mobi
Run Code Online (Sandbox Code Playgroud)

zargsbeingzshxargs类似命令来批处理参数列表。./*.epub(:r)获取.epub文件的根名称,并使用运行命令行-I@,并依次替换为每个根名称。zargsebook-convert@

如果您的ebook-convert命令接受被称为ebook-convert file1.epub file1.mobi file2.epub file2.mobi ...,您还可以执行以下操作:

ebook-convert ./*.epub(e['reply=($REPLY $REPLY:r.mobi)'])
Run Code Online (Sandbox Code Playgroud)

由于val glob 限定符为每个匹配文件运行提供的代码,该 glob 扩展到./file1.epub ./file1.mobi ./file2.epub ./file2.mobi... ,其中设置定义了 glob 应扩展到的参数列表。e$reply

或者:

(){epub-convert $^@.{epub,mobi};} ./*.epub(:r)
Run Code Online (Sandbox Code Playgroud)

我们将文件根名列表传递.epub给匿名函数,该函数使用大括号扩展来传递带有.epub.mobi附加参数的参数。

在所有这些中,要将*.epubglob 扩展限制为那些还没有较新的对应.mobi文件的文件,您可以添加该e['[[ ! $REPLY:r.mobi -nt $REPLY ]]']glob 限定符,或将检查添加为循环的一部分:

for epub (./*.epub) {
  mobi=$epub:r.mobi
  [[ $mobi -nt $epub ]] || ebook-convert $epub $mobi
}
Run Code Online (Sandbox Code Playgroud)


小智 6

您能否以类似的方式使用通配符,以适用于上述场景?

不像ebook-convert *epub *mobi,因为通配符 - 真正的“shell 通配符” - 的工作原理。但是,您可以从通配符开始。

从概念上讲,Shell 通配符非常简单:找到与该通配符匹配的所有文件,并将该通配符替换为该文件列表,处理空格和其他“特殊”字符,以便调用的操作(通常是一个程序;此处为 )获取每个ebook-convert文件文件作为单个单独的参数。

因此,给定一个包含a.epubb.epub、 和 的文件夹file with spaces.epub,shell 将扩展*.epuba.epubb.epub、 和file with spaces.epub作为正在调用的任何内容的 3 个独立参数(此处为ebook-convert)。

给定同一个文件夹,*.mobi不会匹配任何内容,因此ebook-convert将收到一个字面上的参数*.mobi。从ebook-convert的角度来看,它得到了一个包含三个 epub 文件和一个不存在的 mobi 文件的列表;它如何处理参数列表取决于它(据猜测,它要么抱怨参数太多,要么依次尝试将每个 epub 转换为字面名为“*.mobi”的 mobi 文件)。

请注意,对于程序(或内置 shell 或函数或脚本等)如何处理它期望是文件名但包含 glob 的参数,没有全局保证。通常,该参数将被视为字符串文字,并且对*.mobi不存在的反应就像对anything_else.mobi不存在的反应一样,但没有法律规定必须发生这种情况。

对于其他 glob 也会发生同样的情况;例如,?.epub将包括a.epubb.epub,但不包括file with spaces.epub

正如其他人所指出的,您可以使用 glob 来驱动循环 - for file in *.epub ; do ...。请注意,需要引用对“文件”的引用来处理空格:通配符for file in *.epub仅确保它file with spaces.epub是循环本身的单个参数for,但不会扩展到循环体中(即,for file in *.epub ; do ebook-convert $file将发送三个单独的参数对于file with spaces.epub: filewithspaces.epub)。

所有这些也是您经常需要为需要 glob 字符的程序引用参数的原因:这样做可以防止 shell 扩展 glob,并且实际上让被调用的程序看到您编写的参数。例如,find . -type f -name '*.epub'将查找(并打印其名称)此目录和任何子目录中的所有 epub 文件;find . -type f -name *.epub会出错,因为它会看到b.epubfile with spaces.epub作为它不知道该怎么做的参数。