Shell Expansion
Sources:
- Machtelt Garrels. Chapter 3. The Bash environment. Bash Guide for Beginners.
Brace expansion
Brace expansion is a mechanism by which arbitrary strings may be generated. 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.
Brace expansions may be nested. The results of each expanded string are not sorted; left to right order is preserved:
1 | franky ~> **echo `\*sp{el,il,al}l\*`** spell spill spall |
Brace expansion is performed before any other expansions, and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces. To avoid conflicts with parameter expansion, the string "${" is not considered eligible for brace expansion.
A correctly-formed brace expansion must contain unquoted opening and closing braces, and at least one unquoted comma. Any incorrectly formed brace expansion is left unchanged.
Tilde expansion
If a word begins with an unquoted tilde character ("~"), all of the characters up to the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name. If this login name is the null string, the tilde is replaced with the value of the HOME
shell variable. If HOME
is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.
If the tilde-prefix is "~+", the value of the shell variable PWD
replaces the tilde-prefix. If the tilde-prefix is "~-", the value of the shell variable OLDPWD
, if it is set, is substituted.
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.
Example:
1 | franky ~> **export `PATH`=`\*"$PATH:~/testdir"\*`** |
~/testdir
will be expanded to $HOME``/testdir
, so if $HOME
is /var/home/franky
, the directory /var/home/franky/testdir
will be added to the content of the PATH
variable.
Parameter and variable expansion
The $
character introduces parameter expansion, command substitution, or arithmetic expansion.
The basic form of parameter expansion is
1 | {PARAMETER} |
The value of PARAMETER
is substituted. The braces are required when PARAMETER
is a positional parameter with more than one digit, or when PARAMETER
is followed by a character that is not to be interpreted as part of its name.
To assign a default value if the named variable does not yet exist:
1 | {VAR:=value} |
Example:
1 | > echo $FRANKY |
Indirect expansion
Indirect expansion in the shell refers to the process of using a variable's value to determine another variable's name, and then expanding that new variable to obtain its value. This is useful for dynamic referencing of variables. The most common syntax for indirect expansion in the shell is ${!var}
.
Here's an example to illustrate this:
Define a variable with a value:
1
varname="USER"
Use indirect expansion to reference the value of the variable
USER
:1
echo ${!varname}
In this case, ${!varname}
expands to the value of the variable whose name is the value of varname
. If USER
is set to john
, the output will be john
.
Command substitution
Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:
1 | $(command) |
or like this using backticks:
1 | `command` |
Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting.
example:
1 | for file in $(ls) |
1 | !/bin/sh |
Arithmetic expansion
Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:
1 | (( 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. Arithmetic substitutions may be nested.
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:
1 | !/bin/sh |
Process Substitution
<( CMD )
: 执行 CMD
并将其输出重定向到一个临时文件, 用这个临时文件的名字替换 <()
This is useful when commands expect values to be passed by file instead of by STDIN.
For example,
diff <(ls foo) <(ls bar)
will show differences between files in dirsfoo
andbar
.
operator
Bourne shell didn't originally have any mechanism to perform simple arithmetic operations but it uses external programs, either awk or expr.
1 | !/bin/sh |
- There must be spaces between operators and expressions.
2+2
: wrong2 + 2
: right
- all the conditional expressions should be inside square braces with spaces around them
[$a==$b]
or[$a <= $b]
: wrong[ $a == $b ]
or[ $a <= $b ]
: right
- When performing comparisons in bash, try to use double brackets
[[ ]]
in favor of simple brackets[ ]
- 因为表达式要先执行,因此它必须被包裹在backquote内
Shell Globbing
一些正则操作,用于文件名扩展
Wildcards:
?
: match one character*
: match any amount of charactersFor instance, given files
foo
,foo1
,foo2
,foo10
andbar
, the commandrm foo?
will deletefoo1
andfoo2
whereasrm foo*
will delete all butbar
.
Curly braces
{}
- 相当于笛卡尔积:a.{py,cpp,java}
等价于a.py a.cpp a.java
1 | convert image.{png,jpg} |
Environment virable
shell变量可分为环境变量和自定义变量. 环境变量可以在其进程的子进程中继续有效,而自定义变量则无效.
使用 export
设置的变量就成为了环境变量, 而没有使用 export
设置的则是自定义变量.
$PATH
详见Shell Script Searching Path
常见的环境变量:
环境变量 | 说明 |
---|---|
$HOME | 当前用户的登陆目录 |
$PATH | 以冒号分隔的, 由多个路径所组成的, 用来搜索命令的列表 |
$PS1 | 命令行提示符,通常是”$”字符 (很多主题都会改掉$PS1) |
$PS2 | 辅助提示符,用来提示后续输入,通常是”>”字符 |
$IFS | 输入区分隔符。当shell读取输入数据时会把一组字符看成是单词之间的分隔符,通常是空格、制表符、换行符等 |
1 | 我们在当前Shell进程中指定了var1变量 |
Parameter variable
$0
- Name of the script$1
to$9
- Arguments to the script.$1
is the first argument and so on.- 当
n>=10
时,需要使用${n}
来获取参数
- 当
$@
- 全部参数组成的列表$#
- Number of arguments$?
- Return code of the previous command$*
: 全部参数连接成的字符串,按$IFS
的第一个字符分割$$
- Process identification number (PID) for the current script!!
- Entire last command, including arguments. A common pattern is to execute a command only for it to fail due to missing permissions; you can quickly re-execute the command with sudo by doingsudo !!
$_
- Last argument from the last command. If you are in an interactive shell, you can also quickly get this value by typingEsc
followed by.
orAlt+.