ITEEDU

3.4. Shell扩展

3.4.1. 概要

在命令被分割成 记号(tokens译做记号) 之后(参见 第 1.4.1.1 节 “Shell语法”), 这些记号或者词就扩展开或者分解开。在下面的章节中我们讨论按照顺序扩展的共计八种扩展实现。

在所有扩展之后,就进行引用的移除。

3.4.2. 括号扩展

括号扩展是一种可以生成任意字符串的机制。Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

括号扩展可以嵌套。每个扩展开的字符串的结果是未分类的;从左至右的顺序是保留的:

franky ~> echo sp{el,il,al}l
spell spill spall

括号扩展在任何其他扩展之前执行,而且在结果中保留对其它扩展有特殊意义的任何字符。完全是严格按照原文。Bash 不对扩展的上下文或者括号之间的文本进行任何句法的解释。为了防止和参数扩展的冲突,字符串 “${” 被认为对于括号扩展是非法的。

一个正确形式的括号扩展必须包含未引用的一对括号,且至少一个未被引用的逗号。错误形式的括号扩展保留不做改变。

3.4.3. ~扩展

如果一个字以非引用的波浪线 (“~”)开始,所有在第一个未引用的斜杠之前的字符(或者所有字符,如果没有未引用的斜杠)被认为是一个 tilde-prefix(tilde前缀)。如果在tilde前缀中的没有字符被引用,那么在tilde前缀中的跟随tilde的字符就被当作一个可能的登陆名。如果这个登陆名是空字符串,这个tilde就使用shell变量 HOME 的值来代替。如果 HOME 没有预先设置,就用用户执行shell的home目录来进行替换。除非,tilde前缀已经用指定的登陆名替换掉了。

如果tilde前缀是 “~+”, 变量 PWD 的值替换tilde前缀。如果tilde前缀是 “~-”,shell变量 OLDPWD的值,如果预先设置的话,就进行替换。

如果跟随在tilde前缀中波浪线If the characters following the tilde in the tilde-prefix consist of a number N, optionally prefixed by a “+” or a “-”, the tilde-prefix is replaced with the corresponding element from the directory stack, as it would be displayed by the dirs built-in invoked with the characters following tilde in the tilde-prefix as an argument. If the tilde-prefix, without the tilde, consists of a number without a leading “+” or “-”, “+” is assumed.

If the login name is invalid, or the tilde expansion fails, the word is left unchanged.

Each variable assignment is checked for unquoted tilde-prefixes immediately following a “:” or “=”. In these cases, tilde expansion is also performed. Consequently, one may use file names with tildes in assignments to PATH, MAILPATH, and CDPATH, and the shell assigns the expanded value.

例子:

franky ~> export PATH="$PATH:~/testdir"

~/testdir 将会扩展成为 $HOME/testdir,所以如果 $HOME/var/home/franky,目录 /var/home/franky/testdir 将会加入到 PATH 变量中去。

3.4.4. Shell参数和变量扩展

$” 字符引出参数扩展,命令替换,或者算术扩展。参数名字或者需要展开的符号可以放入括号内, The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.

当使用括号的时候,匹配的结束括号是第一个没有被反斜杠转义或者在引用字符串中,以及不在一个嵌入式的算术表达式,命令替换或者参数扩展的 “}”。

参数扩展的基本形式是 “${PARAMETER}”。“PARAMETER” 的值是要被替换的。当 “PARAMETER” 是一个含有多于一个数字的位置参数的时候,需要使用括号,或者当 “PARAMETER” 后接一个不会被解释成它名字一部分的字符的时候。

如果 “PARAMETER” 的第一个字符是一感叹号,Bash使用 uses the value of the variable formed from the rest of “PARAMETER” as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of “PARAMETER” itself. 这就是所谓的 indirect expansion.

你应该已经熟悉了直接参数扩展,由于它无时无刻都在发生,甚至在最简单的诸如以下的例子:

franky ~> echo $SHELL
/bin/bash

以下是一个间接扩展的例子:

franky ~> echo ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

注意这个和 echo $N*是不同的。

以下结构允许建立尚不存在的变量:

${VAR:=value}

例子:

franky ~> echo $FRANKY

franky ~> echo ${FRANKY:=Franky}
Franky

但是,特别参数,Special parameters, among others the positional parameters, may not be assigned this way, however.

We will further discuss the use of the curly braces for treatment of variables in 第 10 章 变量进阶. More information can also be found in the Bash info pages.

3.4.5. 命令替换

命令替换允许一个命令的输出来替换这个命令本身。命令替换在一个命令这样封装的时候发生:

$(command)

或者象这样使用:

`command`

Bash以执行COMMAND且以命令的删除任何多余空行的标准输出来替换命令替换以实现这种扩展。嵌在当中的空行不会被删除,不过在字分割阶段可能会被移除。

franky ~> echo `date`
Thu Feb 6 10:06:20 CET 2003

当使用老式风格的反引用替换形式的时候,反斜杠保留它的含义,除非当跟在 “$”,“`”, 或者 “\” 之后。The first backticks not preceded by a backslash terminates the command substitution. When using the “$(COMMAND)” form, all characters between the parentheses make up the command; none are treated specially.

命令替换可以嵌套。To nest when using the backquoted form, escape the inner backticks with backslashes.

如果替换出现在双引号中,字分割和文件名扩展不会在该结果中执行。

3.4.6. 算术扩展

算术扩展允许一个算术表达式的赋值和结果的替换。算术扩展的格式是:

$(( EXPRESSION ))

表达式当作好像在双引用中The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter expansion, command substitution, and quote removal. 算术扩展可以嵌套。

Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow - although division by zero is trapped and recognized as an error. The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:

表 3.4. 算术操作符

操作符 意义
VAR++ and VAR-- 变量自增和变量自减
++VAR and --VAR 变量前置加和前置减
- and + 一元减和加
! and ~ 逻辑和按位取反
** 求幂
*, / and % 乘,除,求余
+ and - 加,减
<< and >> 左移和右移
<=, >=, < and > 比较操作符
== and != 相等和不相等
& 位与
^ 位异或
| 位或
&& 逻辑与
|| 逻辑或
expr ? expr : expr 条件赋值
=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^= and |= 赋值
, 表达式间的分隔符

Shell变量允许 variables are allowed as operands; parameter expansion is performed before the expression is evaluated. 在一个表达式中,shell变量也可以不使用参数扩展语法来通过名字引用。变量的值在被引用时被当作一个算术表达式来求值。一个shell变量在一个表达式中使用时不需要打开它的整数属性。

以0开始的常量会被解释成一个八进制数字。一个以 “0x” 或者 “0X” 开始的表示十六进制。除非,数字采取 “[BASE'#']N”形式,其中 “BASE” 是一个代表is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base. 如果 “BASE'#'” 省略,那么 then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, “@”, and “_”, in that order. If “BASE” is less than or equal to 36, lowercase and uppercase letters may be used interchangably to represent numbers between 10 and 35.

操作数按照优先级来求值。可能忽略优先级而先对在括号中的子表达式进行求值。

无论在什么地方,Bash用户应该尝试带上方括号使用这个语法:

$[ EXPRESSION ]

然而,这样将仅仅计算 EXPRESSION的结果,且不进行测试:

franky ~> echo $[365*24]
8760

参见 第 7.1.2.2 节 “数字的比较”,其他的,可以看实际脚本中的例子。

3.4.7. 过程替换Process substitution

Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. It takes the form of

<(LIST)

or

>(LIST)

The process LIST is run with its input or output connected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current command as the result of the expansion. If the “>(LIST)” form is used, writing to the file will provide input for LIST. If the “<(LIST)” form is used, the file passed as an argument should be read to obtain the output of LIST. Note that no space may appear between the < or > signs and the left parenthesis, otherwise the construct would be interpreted as a redirection.

When available, process substitution is performed simultaneously with parameter and variable expansion, command substitution, and arithmetic expansion.

More information in 第 8.2.3 节 “重定向和文件描述符”.

3.4.8. 字分割

shell扫描不在双引号中发生的参数扩展,命令替换和算术扩展的结果来进行字分割。

shell把每个 $IFS 字符对待成一个分隔符,且基于这些字符把其他扩展的结果分割。如果 IFS 未设置,或者它的值正好是 “'<space><tab><newline>'”,默认,那么任何 IFS 字符的序列就送往分割字。如果 IFS 有一个不同于默认的值,那么hen sequences of the whitespace characters “space” and “Tab” are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character). Any character in IFS that is not IFS whitespace, along with any adjacent IF whitespace characters, delimits a field. A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

Explicit null arguments (“""” or “''”) are retained. Unquoted implicit null arguments, resulting from the expansion of parameters that have no values, are removed. If a parameter with no value is expanded within double quotes, a null argument results and is retained.

[注意] Expansion and word splitting

If no expansion occurs, no splitting is performed.

3.4.9. 文件名扩展

在字分割之后,除非设置了 -f 选项(参见 第 2.3.2 节 “调试部分脚本”),Bash扫描每个字来搜索字符 “*”,“?”,和 “[”。如果出现其中一个字符,那么这个字就被作为一个 PATTERN, and replaced with an alphabetically sorted list of file names matching the pattern. 如果没有找到匹配的文件,而且shell选项 nullglob 被禁用,那么这个字就被保留不做改变。如果设置了 nullglob 选项,而没有找到匹配,那么字就被删除。如果shell选项 nocaseglob 开启,就不对字符的字母顺序进行考虑而直接进来匹配。

When a pattern is used for file name generation, the character “.” at the start of a file name or immediately following a slash must be matched explicitly, unless the shell option dotglob is set. When matching a file name, the slash character must always be matched explicitly. In other cases, the “.” character is not treated specially.

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern. If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches. The file names . and .. are always ignored, even when GLOBIGNORE is set. However, setting GLOBIGNORE has the effect of enabling the dotglob shell option, so all other file names beginning with a “.” will match. To get the old behavior of ignoring file names beginning with a “.”, make “.*” one of the patterns in GLOBIGNORE. The dotglob option is disabled when GLOBIGNORE is unset.