假设工作目录中只有一个 djvu 文件。我想将文件备份到文件名.bk
末尾带有额外内容的文件中。cp *.djvu{,.bk}
将 djvu 文件复制到名为*.djvu.bk
.为什么*
备份文件名没有扩展?怎么会这样?
答案1
发生的情况是 bash 首先扩展*.djvu{,.bk}
为*.djvu *.djvu.bk
,然后对它们进行 glob 扩展。这可以解释您所观察到的情况:在您的情况下,*.djvu
, 匹配现有文件,例如foo.djvu
并扩展为该文件,但*.djvu.bk
不匹配任何文件,因此扩展为自身,*.djvu.bk
。
bash 文档中指定了扩展顺序:
The order of expansions is: brace expansion, tilde expansion, parame‐
ter, variable and arithmetic expansion and command substitution (done
in a left-to-right fashion), word splitting, and pathname expansion.
我建议将您的复制命令重写为:
for f in *.djvu; do cp -- "$f" "$f".bk; done
或者,为了避免显式 for 循环的语法开销:
parallel -j1 cp -- {} {}.bk ::: *.djvu
(再想一想……这并没有短多少。)
要回答您的子问题“如何扩展它”,可以使用子命令(例如在仅包含foo.djvu
和 的 目录中bar.djvu
):
$ echo $(echo *.djvu){,.bk}
bar.djvu foo.djvu bar.djvu foo.djvu.bk
但这并不像上面的 for 循环或并行调用那么安全。它将在包含空格的文件名上崩溃。
答案2
大括号扩展发生在通配符扩展之前,因为大括号扩展会创建单独的单词,而当通配符模式已经在单独的单词中时,通配符扩展发生在最后。 (无论如何,如果反过来发生,则cp *.djvu{,.bk}
不会匹配任何内容,因为没有名称以 结尾的文件.djvu{,.bk}
,然后大括号扩展将产生两个单词*.dvju
和*.djvu.bk
。)因此,第一个cp *.djvu{,.bk}
扩展为cp
*.djvu
*.djvu.bk
,然后每个包含通配符的单词是如果通配符至少匹配一个文件,则展开。由于您有匹配的文件*.djvu
但没有匹配的文件*.djvu.bk
,因此您最终会得到类似cp
a.djvu
b.djvu
*.djvu.bk
.
如果有匹配的文件*.djvu.bk
,那么这个通配符模式也会被扩展,所以你最终会得到类似cp
a.djvu
b.djvu
a.djvu.bk
c.djvu.bk
.如果目的是在和文件cp
上运行,但允许任一模式不匹配任何内容,那么您可以使用 ksh 扩展模式:*.djvu
*.djvu.bk
shopt -s extglob
cp *.djvu?(.bk) somedir/
但这不是您想要做的。您无法通过对 的参数进行任何形式的扩展来获得所需的内容cp
:您需要cp
为每个.djvu
文件发出单独的命令,每个文件都有正确的目标。一个命令就足够的唯一情况cp
是只有一个.djvu
文件,因此*.djvu
会扩展为单个文件。在这种情况下,输入命令的最简单方法是输入模式(开始输入cp *.djvu
),然后告诉 bash 按Ctrl+展开它X *,按Backspace删除尾随空格并输入{,.bk}
。
如果要复制多个文件,则需要以cp
一种或另一种方式循环调用,或者使用可以根据名称模式复制文件的不同工具。
在 bash 中编写循环非常简单:
for x in *.djvu; do cp "$x" "$x.bk"; done
您可以使用的另一个工具是帕克斯。在这里打字并没有少多少。
pax -rw -s'/$/.bk/' *.djvu .
Zsh 有零点对于这种任务。将其加载到您的.zshrc
:
autoload -U zmv
alias zcp='zmv -C' zln='zmv -L'
然后运行
zcp '*.djvu' '$f.bk'