在 Bash 中,大括号扩展不会将通配结果复制到不存在的文件。
例如,在当前目录中,有一个名为 的文件1.epub
,我想将其转换为除扩展名外文件名相同的 pdf 文件。该 pdf 文件1.pdf
尚不存在。
$ ebook-convert *.{epub,pdf}
结果文件是*.pdf
.
在Bash中,我是否需要首先将epub文件的基本名称提取到变量中,然后使用该变量的值来命名pdf文件?
在 Bash 内部或其他 shell 中是否有更优雅的解决方案?
答案1
shell 技术使用循环。
在 sh 和其他类似 sh 的 shell 中,例如 bash 或 zsh:
for f in *.epub; do
ebook-convert -- "$f" "${f%.epub}.pdf"
done
在 zsh 中,可以缩短为:
for f (*.epub(N)) ebook-convert -- $f $f:r.pdf
N
如果没有匹配的文件,ullglob glob 限定符还可以避免错误。bash 没有等效项,但您可以nullglob
预先全局设置该选项;在 ksh93 中,您还可以使用for f in ~(N)*.epub; do...
您可能rename
安装了一个程序:man 1 rename
答案2
zmv
in 中的实用程序可zsh
与任何命令一起使用,并且适用于如下模式:
autoload zmv
zmv -Wn -p ebook-convert '*.epub' '*.pdf'
用于-n
试运行 - 当您对结果感到满意时将其删除。
作为别名,它甚至可以更短,如下所示~/.zshrc
:
autoload zmv
alias ebc='noglob zmv -W -p ebook-convert '
现在命令行很简单:
ebc *.epub *.pdf
例子:
> ls *.epub
1.epub b2.epub
> ebc -n *.epub *.pdf
ebook-convert -- 1.epub 1.pdf
ebook-convert -- b2.epub b2.pdf
更多使用选项zmv
在文档。
答案3
在 Bash 中,大括号扩展不会将通配结果复制到不存在的文件。
根据规范,确实不是。大括号扩展是 Bash 对每个命令执行的第一个扩展,而路径名扩展是最后一个。因此,当收到您的命令时
ebook-convert *.{epub,pdf}
, bash 首先执行大括号扩展以得到
ebook-convert *.epub *.pdf
,并且它仅在稍后执行路径名扩展 - 分别针对每个模式。除非设置了 nullglob shell 选项,否则与任何文件都不匹配的 glob 模式将扩展为自身,因此您无法使用此方法来生成尚不存在的 .pdf 文件的名称。
更重要的是,如果工作目录中有多个.epub
文件,或者存在.pdf
与您想要写入的文件不同的文件,那么该命令将扩展为具有潜在破坏性的文件。
在Bash中,我是否需要首先将epub文件的基本名称提取到变量中,然后使用该变量的值来命名pdf文件?
您可以避免实际将 EPUB 名称存储在变量中,但您确实需要首先找到 epub 名称,然后单独对其应用转换,特别是如果您要使用 glob 来标识 EPUB。
我建议使用 shell 函数:
epub_to_pdf() {
for epub; do
ebook-convert "${epub}" "${epub%.epub}.pdf"
done
}
定义完成后,您只需为其指定 EPUB,而不是相应的 PDF:
epub_to_pdf *.epub
这也将适当地处理多个 EPUB。您可能不想每次都定义该函数,但您可以将定义放入您的函数中.bashrc
以确保它始终可供您使用。
答案4
“优雅的解决方案”是 GNU Make 中的模式规则。如果我们有Makefile
这样的:
%.pdf: %.epub
ebook-convert -- $^ $@
那么你就可以
$ make 1.pdf
GNU Make 会发现它1.pdf
不存在并且它与我们编写的模式规则的左侧匹配%.pdf
。它将实例化该模式并看到右侧生成1.epub
。然后它将运行该命令ebook-convert 1.epub 1.pdf
。
如果我们再次运行该命令,它将经历相同的模式匹配,然后查看1.pdf
更新的命令1.epub
并报告1.pdf
最新的命令。