Skip to content

Commit 5b64556

Browse files
committed
docs: edit expansion
1 parent afec8fe commit 5b64556

File tree

1 file changed

+92
-52
lines changed

1 file changed

+92
-52
lines changed

docs/expansion.md

Lines changed: 92 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ c*.txt
165165

166166
## 方括号扩展
167167

168-
方括号模式是`[...]`可以匹配方括号之中的任意一个字符,比如`[aeiou]`可以匹配五个元音字母中的任意一个。该模式属于文件名匹配,即匹配后的结果必须符合现有的文件路径,如果不存在匹配,就会保持原样,不进行扩展
168+
方括号扩展的形式是`[...]`只有文件确实存在的前提下才会扩展。如果文件不存在,就会原样输出。括号之中的任意一个字符。比如,`[aeiou]`可以匹配五个元音字母中的任意一个。
169169

170170
```bash
171171
# 存在文件 a.txt 和 b.txt
@@ -175,28 +175,35 @@ a.txt b.txt
175175
# 只存在文件 a.txt
176176
$ ls [ab].txt
177177
a.txt
178+
```
179+
180+
上面例子中,`[ab]`可以匹配`a``b`,前提是确实存在相应的文件。
181+
182+
方括号扩展属于文件名匹配,即扩展后的结果必须符合现有的文件路径。如果不存在匹配,就会保持原样,不进行扩展。
178183

184+
```bash
179185
# 不存在文件 a.txt 和 b.txt
180186
$ ls [ab].txt
181187
ls: 无法访问'[ab].txt': 没有那个文件或目录
182188
```
183189

184-
上面命令中,`[ab]`表示可以扩展成`a``b`。具体的执行结果,取决于当前目录是否包含指定的文件
190+
上面例子中,由于扩展后的文件不存在,`[ab].txt`就原样输出了,导致`ls`命名报错
185191

186-
方括号模式还有两种变体`[^...]``[!...]`。它们表示匹配不在方括号里面的字符,这两种写法是等价的。比如,`[^abc]``[!abc]`表示匹配除了`a``b``c`以外的字符。
192+
方括号扩展还有两种变体`[^...]``[!...]`。它们表示匹配不在方括号里面的字符,这两种写法是等价的。比如,`[^abc]``[!abc]`表示匹配除了`a``b``c`以外的字符。
187193

188194
```bash
195+
# 存在 aaa、bbb、aba 三个文件
189196
$ ls ?[!a]?
190197
aba bbb
191198
```
192199

193-
上面命令中,`[!a]`表示文件名第二个字符不是`a`的文件名。
200+
上面命令中,`[!a]`表示文件名第二个字符不是`a`的文件名,所以返回了`aba``bbb`两个文件
194201

195202
注意,如果需要匹配`[`字符,可以放在方括号内,比如`[[aeiou]`。如果需要匹配连字号`-`,只能放在方括号内部的开头或结尾,比如`[-aeiou]``[aeiou-]`
196203

197204
## [start-end] 扩展
198205

199-
方括号模式`[start-end]`可以表示一个连续的范围
206+
方括号扩展有一个简写形式`[start-end]`,表示匹配一个连续的范围。比如,`[a-c]`等同于`[abc]``[0-9]`匹配`[0123456789]`
200207

201208
```bash
202209
# 存在文件 a.txt、b.txt 和 c.txt
@@ -213,7 +220,7 @@ report3.txt
213220
...
214221
```
215222

216-
下面是更多的例子
223+
下面是一些常用简写的例子
217224

218225
- `[a-z]`:所有小写字母。
219226
- `[a-zA-Z]`:所有小写字母与大写字母。
@@ -222,7 +229,7 @@ report3.txt
222229
- `program.[co]`:文件`program.c`与文件`program.o`
223230
- `BACKUP.[0-9][0-9][0-9]`:所有以`BACKUP.`开头,后面是三个数字的文件名。
224231

225-
方括号模式的否定形式,也可以使用连续范围的写法`[!start-end]`,表示匹配不属于这个范围的字符。比如,`[!a-zA-Z]`表示匹配非英文字母的字符。
232+
这种简写形式有一个否定形式`[!start-end]`,表示匹配不属于这个范围的字符。比如,`[!a-zA-Z]`表示匹配非英文字母的字符。
226233

227234
```bash
228235
$ echo report[!1–3].txt
@@ -233,7 +240,7 @@ report4.txt report5.txt
233240

234241
## 大括号扩展
235242

236-
大括号扩展`{...}`表示分别输出大括号里面的所有值,各个值之间使用逗号分隔。这种扩展跟上面的扩展都不一样,不是文件名扩展,即不会扩展成文件名,而是扩展成所有给定的值
243+
大括号扩展`{...}`表示分别扩展成大括号里面的所有值,各个值之间使用逗号分隔。比如,`{1,2,3}`扩展成`1 2 3`
237244

238245
```bash
239246
$ echo {1,2,3}
@@ -246,17 +253,31 @@ $ echo Front-{A,B,C}-Back
246253
Front-A-Back Front-B-Back Front-C-Back
247254
```
248255

249-
注意,大括号内部的逗号前后不能有空格。否则,大括号模式会失效。
256+
注意,大括号扩展不是文件名扩展。它会扩展成所有给定的值,而不管是否有对应的文件存在。
257+
258+
```bash
259+
$ ls {a,b,c}.txt
260+
ls: 无法访问'a.txt': 没有那个文件或目录
261+
ls: 无法访问'b.txt': 没有那个文件或目录
262+
ls: 无法访问'c.txt': 没有那个文件或目录
263+
```
264+
265+
上面例子中,即使不存在对应的文件,`{a,b,c}`依然扩展成三个文件名,导致`ls`命令报了三个错误。
266+
267+
另一个需要注意的地方是,大括号内部的逗号前后不能有空格。否则,大括号扩展会失效。
250268

251269
```bash
252270
$ echo {1 , 2}
253271
{1 , 2}
254272
```
255273

256-
如果逗号前面没有值,就表示扩展的第一项为空。
274+
上面例子中,逗号前后有空格,Bash 就会认为这不是大括号扩展,而不是两个独立的参数。
275+
276+
逗号前面可以没有值,表示扩展的第一项为空。
257277

258278
```bash
259279
$ cp a.log{,.bak}
280+
260281
# 等同于
261282
# cp a.log a.log.bak
262283
```
@@ -278,16 +299,16 @@ $ echo {cat,d*}
278299
cat dawg dg dig dog doug dug
279300
```
280301

281-
上面代码中,会先进行大括号扩展,然后进行`*`扩展。
302+
上面例子中,会先进行大括号扩展,然后进行`*`扩展。
282303

283-
大括号可以用于多字符的模式,方括号不行。
304+
大括号可以用于多字符的模式,方括号不行(只能匹配单字符)
284305

285306
```bash
286307
$ echo {cat,dog}
287308
cat dog
288309
```
289310

290-
大括号模式`{...}`与方括号模式`[...]`有一个很重要的区别。如果匹配的文件不存在,`[...]`会失去模式的功能,变成一个单纯的字符串,而`{...}`依然可以展开,因为大括号不是文件名扩展
311+
由于大括号扩展`{...}`不是文件名扩展,所以它总是会扩展的。这与方括号扩展`[...]`完全不同,如果匹配的文件不存在,方括号就不会扩展。这一点要注意区分
291312

292313
```bash
293314
# 不存在 a.txt 和 b.txt
@@ -298,19 +319,16 @@ $ echo {a,b}.txt
298319
a.txt b.txt
299320
```
300321

301-
上面代码中,如果不存在`a.txt``b.txt`,那么`[ab].txt`就会变成一个普通的文件名,而`{a,b}.txt`可以照样展开
322+
上面例子中,如果不存在`a.txt``b.txt`,那么`[ab].txt`就会变成一个普通的文件名,而`{a,b}.txt`可以照样扩展
302323

303324
## {start..end} 扩展
304325

305-
大括号里面两个点的`{start..end}`模式,表示扩展成一个连续序列,然后分别输出。比如`{a..z}`可以扩展成26个小写英文字母,然后输出
326+
大括号扩展有一个简写形式`{start..end}`,表示扩展成一个连续序列。比如`{a..z}`可以扩展成26个小写英文字母。
306327

307328
```bash
308329
$ echo {a..c}
309330
a b c
310331

311-
$ echo {c..a}
312-
c b a
313-
314332
$ echo d{a..d}g
315333
dag dbg dcg ddg
316334

@@ -321,24 +339,24 @@ $ echo Number_{1..5}
321339
Number_1 Number_2 Number_3 Number_4 Number_5
322340
```
323341

324-
大括号的双点号支持逆序
342+
这种简写形式支持逆序
325343

326344
```bash
345+
$ echo {c..a}
346+
c b a
347+
327348
$ echo {5..1}
328349
5 4 3 2 1
329-
330-
$ echo {E..A}
331-
E D C B A
332350
```
333351

334-
如果遇到无法解释的扩展,模式会原样输出
352+
注意,如果遇到无法理解的简写,大括号模式就会原样输出,不会扩展
335353

336354
```bash
337355
$ echo {a1..3c}
338356
{a1..3c}
339357
```
340358

341-
这种模式与逗号联用,可以写出复杂的模式
359+
这种简写形式可以嵌套使用,形成复杂的扩展
342360

343361
```bash
344362
$ echo .{mp{3..4},m4{a,b,p,v}}
@@ -353,7 +371,18 @@ $ mkdir {2007..2009}-{01..12}
353371

354372
上面命令会新建36个子目录,每个子目录的名字都是”年份-月份“。
355373

356-
整数前面可以使用前导`0`,然后输出的每一项都有前导`0`
374+
这个写法的另一个常见用途,是直接用于`for`循环。
375+
376+
```bash
377+
for i in {1..4}
378+
do
379+
echo $i
380+
done
381+
```
382+
383+
上面例子会循环4次。
384+
385+
如果整数前面有前导`0`,扩展输出的每一项都有前导`0`
357386

358387
```bash
359388
$ echo {01..5}
@@ -363,7 +392,7 @@ $ echo {001..5}
363392
001 002 003 004 005
364393
```
365394

366-
大括号里面还可以使用第二个双点号`start..end..step`),用来指定扩展的步长。
395+
这种简写形式还可以使用第二个双点号`start..end..step`),用来指定扩展的步长。
367396

368397
```bash
369398
$ echo {0..8..2}
@@ -372,22 +401,13 @@ $ echo {0..8..2}
372401

373402
上面代码将`0`扩展到`8`,每次递增的长度为`2`,所以一共输出5个数字。
374403

375-
多个大括号连用,会有循环处理的效果。
404+
多个简写形式连用,会有循环处理的效果。
376405

377406
```bash
378407
$ echo {a..c}{1..3}
379408
a1 a2 a3 b1 b2 b3 c1 c2 c3
380409
```
381410

382-
这个写法可以直接用于`for`循环。
383-
384-
```bash
385-
for i in {1..4}
386-
do
387-
echo $i
388-
done
389-
```
390-
391411
## 变量扩展
392412

393413
Bash 将美元符号`$`开头的词元视为变量,将其扩展成变量值,详见《Bash 变量》一章。
@@ -404,7 +424,7 @@ $ echo ${SHELL}
404424
/bin/bash
405425
```
406426

407-
`${!...*}``${!...@}`可以返回所有匹配的变量名
427+
`${!string*}``${!string@}`返回所有匹配给定字符串`string`的变量名
408428

409429
```bash
410430
$ echo ${!S*}
@@ -415,7 +435,7 @@ SECONDS SHELL SHELLOPTS SHLVL SSH_AGENT_PID SSH_AUTH_SOCK
415435

416436
## 子命令扩展
417437

418-
`$(...)`可以扩展成另一个命令的运行结果,内部命令的所有输出都会作为返回值
438+
`$(...)`可以扩展成另一个命令的运行结果,该命令的所有输出都会作为返回值
419439

420440
```bash
421441
$ echo $(date)
@@ -431,7 +451,7 @@ $ echo `date`
431451
Tue Jan 28 00:01:13 CST 2020
432452
```
433453

434-
`$(...)`还可以嵌套,比如`$(ls $(pwd))`
454+
`$(...)`可以嵌套,比如`$(ls $(pwd))`
435455

436456
## 算术扩展
437457

@@ -444,7 +464,7 @@ $ echo $((2 + 2))
444464

445465
## 字符类
446466

447-
`[[:class:]]`表示一个字符类,匹配某一类特定字符之中的一个。常用的字符类如下。
467+
`[[:class:]]`表示一个字符类,扩展成某一类特定字符之中的一个。常用的字符类如下。
448468

449469
- `[[:alnum:]]`:匹配任意英文字母与数字
450470
- `[[:alpha:]]`:匹配任意英文字母
@@ -475,6 +495,16 @@ $ echo [![:digit:]]*
475495
476496
上面命令输出所有不以数字开头的文件名。
477497
498+
字符类也属于文件名扩展,如果没有匹配的文件名,字符类就会原样输出。
499+
500+
```bash
501+
# 不存在以大写字母开头的文件
502+
$ echo [[:upper:]]*
503+
[[:upper:]]*
504+
```
505+
506+
上面例子中,由于可匹配的文件,字符类就原样输出了。
507+
478508
## 使用注意点
479509
480510
通配符有一些使用注意点,不可不知。
@@ -490,9 +520,9 @@ ab.txt
490520
491521
上面命令的执行过程是,Bash 先将`a*.txt`扩展成`ab.txt`,然后再执行`ls ab.txt`
492522
493-
**(2)通配符不匹配,会原样输出。**
523+
**(2)文件名扩展在不匹配时,会原样输出。**
494524
495-
Bash 扩展通配符的时候,发现不存在匹配的文件,会将通配符原样输出
525+
文件名扩展在没有可匹配的文件时,会原样输出
496526
497527
```bash
498528
# 不存在 r 开头的文件名
@@ -509,21 +539,21 @@ $ ls *.csv
509539
ls: *.csv: No such file or directory
510540
```
511541
512-
另外,前面已经说过,这条规则对大括号模式`{...}`不适用
542+
另外,前面已经说过,大括号扩展`{...}`不是文件名扩展
513543
514544
**(3)只适用于单层路径。**
515545
516-
上面所有通配符只匹配单层路径,不能跨目录匹配,即无法匹配子目录里面的文件。或者说,`?``*`这样的通配符,不能匹配路径分隔符(`/`)。
546+
所有文件名扩展只匹配单层路径,不能跨目录匹配,即无法匹配子目录里面的文件。或者说,`?``*`这样的通配符,不能匹配路径分隔符(`/`)。
517547
518548
如果要匹配子目录里面的文件,可以写成下面这样。
519549
520550
```bash
521551
$ ls */*.txt
522552
```
523553
524-
**(4)可用于文件名**
554+
**(4)文件名可以使用通配符**
525555
526-
Bash 允许文件名使用通配符,即文件名包括通配符字符。这时,引用文件名的时候,需要把文件名放在单引号里面。
556+
Bash 允许文件名使用通配符,即文件名包括特殊字符。这时引用文件名,需要把文件名放在单引号里面。
527557
528558
```bash
529559
$ touch 'fo*'
@@ -535,7 +565,7 @@ fo*
535565
536566
## 量词语法
537567
538-
量词语法用来控制模式匹配的次数。它需要在 Bash 的`extglob`参数打开的情况下,才能使用。不过,一般是默认打开的,可以用下面的命令查询
568+
量词语法用来控制模式匹配的次数。它只有在 Bash 的`extglob`参数打开的情况下才能使用,不过一般是默认打开的。下面的命令可以查询
539569
540570
```bash
541571
$ shopt extglob
@@ -578,11 +608,21 @@ abc.txt abc.txt.txt
578608
579609
上面例子中,`+(.txt)`匹配文件有一个或多个`.txt`后缀名。
580610
611+
量词语法也属于文件名扩展,如果不存在可匹配的文件,就会原样输出。
612+
613+
```bash
614+
# 没有 abc 开头的文件名
615+
$ ls abc?(def)
616+
ls: 无法访问'abc?(def)': 没有那个文件或目录
617+
```
618+
619+
上面例子中,由于没有可匹配的文件,`abc?(def)`就原样输出,导致`ls`命令报错。
620+
581621
## shopt 命令
582622
583623
`shopt`命令可以调整 Bash 的行为。它有好几个参数跟通配符扩展有关。
584624
585-
它的命令格式如下
625+
`shopt`命令的使用方法如下
586626
587627
```bash
588628
# 打开某个参数
@@ -597,9 +637,9 @@ $ shopt [optionname]
597637
598638
**(1)dotglob 参数**
599639
600-
`dotglob`参数可以让文件通配符包括隐藏文件(即点开头的文件)。
640+
`dotglob`参数可以让扩展结果包括隐藏文件(即点开头的文件)。
601641
602-
正常情况下,通配符扩展不包括隐藏文件
642+
正常情况下,扩展结果不包括隐藏文件
603643
604644
```bash
605645
$ ls *
@@ -651,14 +691,14 @@ bash: 无匹配: b*
651691
652692
**(4)extglob 参数**
653693
654-
`extglob`参数使得 Bash 支持 ksh 的一些扩展语法。它默认情况下面,应该是打开的
694+
`extglob`参数使得 Bash 支持 ksh 的一些扩展语法。它默认应该是打开的
655695
656696
```bash
657697
$ shopt extglob
658698
extglob on
659699
```
660700
661-
它的主要应用是支持量词语法。如果不希望支持,可以用下面的命令关闭。
701+
它的主要应用是支持量词语法。如果不希望支持量词语法,可以用下面的命令关闭。
662702
663703
```bash
664704
$ shopt -u extglob

0 commit comments

Comments
 (0)