跳转至

shell 字符扩展

「shell 字符扩展」支持用简洁的输入代替冗余的命令,比如 cd ~mkdir foo/bar/{a,b,c},这些技巧是非常有帮助的。

bash 支持以下字符扩展:

  • 大括号扩展
  • 波浪号扩展
  • 变量扩展
  • 命令置换
  • 算术扩展
  • 文件名扩展

大括号扩展

大括号扩展可以帮助批量生成字符串,例如:

$  echo test-{1,2,3,foo}
test-1 test-2 test-3 test-foo
$  
$  mkdir -p ~/test/foo-a ~/test/foo-b
$  mkdir -p ~/test/brace-{a,b,c}
$  ls ~/test
brace-a  brace-b  brace-c  foo-a  foo-b

批量操作文件和目录时,如果它们有相同的前缀或后缀,使用大括号扩展会非常方便。

波浪号扩展

波浪号扩展使用简单的符号取代 home 目录和工作目录的值,常用的有下面这些:

  • ~ 当前用户的 home 目录,等于 $HOME 的值。
  • ~username 特定用户的 home 目录。
  • ~+ 当前工作目录,等于 $PWD 的值。
  • ~- 上一个工作目录,等于 $OLDPWD 的值。

~ 作为扩展使用时必须放在单词开头,放在单词中间将被识别为普通符号。它后面的字符仍然是有特殊含义的,直到斜杠 / 为止。因此,~foo 将被识别为 foo 用户的 home 目录,如果用户不存在则保持其字面值不变。请看下面的例子:

$  echo ~
/home/linux-20
$  echo ~/foo
/home/linux-20/foo
$  echo ~foo
~foo
$  echo ~root
/root

变量扩展

可以截取变量的一部分字符

  • ${var:-word} 当变量为空时,替换为 :- 后面的字符;当变量不为空时替换为变量的值。
  • ${var:=word} 当变量为空时,使用 := 后面的字符为变量赋值;然后替换为变量的值。
  • ${var:offset} ${var:offset:length} 裁剪变量的值。

调用一个空的变量会导致出错,可以使用 ${var:-word}${var:=word} 提供备用值,两者区别是前者不对变量赋值而后者赋值。

${var:offset:length} 可以对变量的值进行裁剪,请看下面的例子:

$  foo=12345abcfoo
$  echo $foo
12345abcfoo
$  echo ${foo:2}
345abcfoo
$  echo ${foo: -3}
foo
$  echo ${foo:2:6}
345abc
$  echo ${foo:2:3}
345
$  echo ${foo: -6:3}
abc
$  echo ${foo: -6:-2}
abcf
$  echo ${foo::5}
12345

命令置换

命令的输出可作为命令的一部分,这叫命令置换,请看下面的例子:

$  ls -l $(which sh)
lrwxrwxrwx 1 root root 4 Jan 18  2019 /bin/sh -> dash
$  ls -l `which sh`
lrwxrwxrwx 1 root root 4 Jan 18  2019 /bin/sh -> dash

ls -l $(which sh) 这里将先执行 which sh,然后将其输出替换到命令中,即 ls -l /bin/sh

$(command)`command` (反引号包裹) 这两种用法都可以,前者便于阅读。

Note

反引号 ` 在键盘上数字 1 前面,阅读时不便于识别,因此不推荐使用。

算术扩展

算术扩展 $(( expression )) 可以获取算术表达式得到的值,请看下面的例子:

$  echo $(( 3+7 ))
10
$  echo $(( 3*7 ))
21
$  i=5; echo $(( i+3 ))
8

文件名扩展

文件名扩展支持「shell 模式匹配」

shell 模式匹配

下列字符在模式匹配中具有特殊含义:

  • * 匹配任意字符,包括空字符。
  • ? 匹配一个任意字符。
  • […] 匹配一个包含在列表中的字符。支持区间写法,例如 [abcde] 可以写成 [a-e]5678 可以写成 [5-8]。

请看下面的例子:

$  touch foo-{a,ab,1}
$  
$  ls foo-*
foo-1  foo-a  foo-ab
$  ls foo-?
foo-1  foo-a
$  ls foo-[a-z]
foo-a
$  ls foo-[a-z]*
foo-a  foo-ab
$  
$  rm foo-*
$  ls foo-*
ls: cannot access 'foo-*': No such file or directory
$  

模式匹配在创建文件时不被支持,因为创建包含任意字符的文件是不合理的;其他文件操作均支持模式匹配。