大括号扩展和 * 扩展

大括号扩展和 * 扩展

假设工作目录中只有一个 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'

相关内容