5.3 利用 {} 构造参数

在命令行下,我们经常会遇到针对多个参数条目执行操作的使用场景,其中文件名算得上是最常见的情形。为了应对这种情况,bash 和 zsh 都提供了逗号分隔的花括号列表,例如:

xiaodong@codeland:~$ echo {one,two,three}

将参数传递给 echo 命令之前,Shell 会首先展开花括号列表,并生成以下 3 个参数条目:

one two three

利用 {} (花括号),我们可以实现许多有意思的功能,下面是我常用的几个。大家在学习时不妨举一反三,以便灵活运用。

5.3.1 备份文件

我发现很多朋友在备份文件时执行的命令是:

xiaodong@codeland:~$ cp file file.bak

这条命令将 file 备份为 file.bak。通过 {} (花括号),我们可以将该命令改写成:

xiaodong@codeland:~$ cp file{,.bak}

cp 命令的参数 file{,.bak} 展开后将变成 file file.bak。这里的 , (逗号) 必不可少,否则 Shell 就不会将其展开了。

类似的,我们也可以利用 tar 结合 {} (花括号) 来创建存档:

xiaodong@codeland:~$ tar cf docs{.tar,}

这里,tar 命令将 docs 目录存档为 docs.tar

5.3.2 生成序列

对于按照一定顺序排列的条目,代替 , (逗号),Shell 也支持通过 .. (两点) 来指定一个区间。比如:

xiaodong@codeland:~$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

echo 将回显从 a 到 z 所有的小写字母。

又如:

xiaodong@codeland:~$ echo {0..9}
0 1 2 3 4 5 6 7 8 9

echo 将回显 0 到 9 的数字。

在区间开头数字的前面也可以添加前导的 0,比如:

xiaodong@codeland:~$ echo {01..10}

这将罗列出以下数字序列:

01 02 03 04 05 06 07 08 09 10

我们甚至还可以在区间的尾端添加一个步进值:

xiaodong@codeland:~$ echo {1..9..2}

末尾的 2 为步进值,这样就只会罗列奇数:

1 3 5 7 9

在 zsh 中,步进值可以为负数,这种情况下将按倒序罗列数字,例如:

xiaodong@codeland:~$ echo {1..9..-2}
9 7 5 3 1

bash 中想要达到同样的效果需要将区间的首尾端对调,比如:

xiaodong@codeland:~$ echo {9..1..2}
9 7 5 3 1

最后,让我们来看一个实际使用序列的例子。通过生成的序列,将其与路径组合,在下载多个文件时尤其有用。

xiaodong@codeland:~$ wget https://linuxtoy.org/img/{1..5}.png

上述命令中,wget 将从 https://linuxtoy.org 依次下载 1.png、2.png、3.png … 等图片文件。

值得一提的是,除了 .. (两点) 的区间表示法,zsh 也支持 - (减号) 这种区间表示,不过需要启用 braceccl 选项。

xiaodong@codeland:~$ setopt braceccl
xiaodong@codeland:~$ echo {A-Za-z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z

我们在使用 setopt 开启 braceccl 选项后,执行 echo {A-Za-z} 罗列出了所有大写和小写字母。

5.3.3 连用与嵌套

Shell 的 {} (花括号) 结构非常灵活和强大,特别是在连用和嵌套时更是威力无穷。先来让我们看一个 {} (花括号) 连用的例子。

xiaodong@codeland:~$ mkdir -p 2019/{01..12}/{baby,photo}

在本例中,我们连用两个 {} (花括号),这样在每个月份的目录下又分别创建了 babyphoto 两个子目录。这条命令实际上执行的是以下命令:

xiaodong@codeland:~$ mkdir -p 2019/01/baby 2019/01/photo \
2019/02/baby 2019/02/photo \
2019/03/baby 2019/03/photo \
2019/04/baby 2019/04/photo \
2019/05/baby 2019/05/photo \
2019/06/baby 2019/06/photo \
2019/07/baby 2019/07/photo \
2019/08/baby 2019/08/photo \
2019/09/baby 2019/09/photo \
2019/10/baby 2019/10/photo \
2019/11/baby 2019/11/photo \
2019/12/baby 2019/12/photo

我们不妨将两个命令比较一下,如果直接手动输入后者该是多么枯燥和乏味的事情。但是,有了 {} (花括号) 的帮助,我们就变轻松了不少。

{} (花括号) 结构不仅可以连用,而且能够嵌套。例如:

xiaodong@codeland:~$ echo {{A..Z},{a..z},{0..9}}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9

echo 命令的外层 {} (花括号) 中包含 3 个内层 {} (花括号),这样就将所有的大写字母、小写字母以及从 0 到 9 的数字都罗列出来了。