#### # 特殊变量
* $0 当前脚本文件
* $n 传递给脚本或函数的参数,n 代表是一个数字,表示第几个参数.例如,第一个参数是$1,第二个参数是$2。
* $# 传递给脚本或函数的参数个数
* $@ 传递给脚本或函数的所有参数
* $* 传递给脚本或函数的所有参数,(当使用双引号括起来时,表示一个单词,其他情况和 $@ 一样)
* $$ 当前 shell 进程ID
* $! 在后台运行的最后的工作的PID
* $? 上一个命令退出状态.或者函数的返回值
* $_ 保存之前执行的命令的最后一个参数
- 比如: git clone https://github.com/xxx/xx.git xx && cd $_
* 当前函数的名字 $FUNCNAME
* 当前行号 $LINENO
* BASH_SOURCE[0] 当前脚本文件
#### # sh 妙用
````
# 将 sh -s --,后面的全部参数,赋值给前面脚本,分别为 $1,$2,$3.....
curl -fsSL https://raw.githubusercontent.com/zhangyouliang/dockerfile/master/tags.sh | sh -s -- nginx
# 校验
echo 'echo $@' | sh -s -- 1 2 3 4 5 6
# output: 1 2 3 4 5 6
````
#### # IFS
> Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符
> 默认情况下,bash shell会将下面的字符当做字段分隔符:空格、制表符、换行符。
```bash
IFS="," read -r -a test <<< `echo 1,2,3,4,5,6`
# test 变量则为数组
declare -p test # declare -a test=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6")
```
#### # 变量间接引用
```bash
# a 变量的值是 b 变量的名字
a=letter_of_alphabet
letter_of_alphabet=z
eval a=\$$a
echo "Now a = $a"
# 引用位置参数
args=$#
lastarg=${!args}
echo $lastarg
lastarg=${!#}
echo $lastarg
```
#### # 变量替换
> https://www.cnblogs.com/gaochsh/p/6901809.html
* ${var} 变量本来的值
* ${var-DEFAULT} 如果变量没有声明,那么返回 DEFAULT ,但不改变 var 的值。
* ${var:-DEFAULT} 如果变量 var 为空或已被删除(unset),那么返回 DEFAULT ,但不改变 var 的值。
* ${var=DEFAULT} 如果变量没有声明,那么返回 DEFAULT ,同时将 $var 赋值为 word 。
* ${var:=DEFAULT} 如果变量 var 为空或已被删除(unset),那么返回 DEFAULT ,同时将 $var 赋值为 word 。
* ${var?ERR_MSG} 如果变量没有声明,那么返回 DEFAULT
* ${var:?ERR_MSG} 如果变量 var 为空或已被删除(unset),那么将消息 ERR_MSG 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。
若此替换出现在Shell脚本中,那么脚本将停止运行
* ${var+OTHER} 如果变量 OTHER 被定义,那么返回 OTHER ,否则返回空字符串,但不改变 var 的值。
* ${var:+OTHER} 如果变量 OTHER 被设置了,那么返回 OTHER ,否则返回空字符串,但不改变 var 的值。
* ${#value} 变量字符个数
* ${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
* ${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量
![image](./img/20150620201642326.png)
#### # Shell中的${}、##和%%使用范例
* ${file#pattern}
* ${file%pattern}
* ${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
* ${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
* ${file#*.}:删掉第一个 . 及其左边的字符串:file.txt
* ${file##*.}:删掉最后一个 . 及其左边的字符串:txt
记忆方法
* `# 是 去掉左边(键盘上#在 $ 的左边)`
* %是去掉右边(键盘上% 在$ 的右边)
* 单一符号是最小匹配;两个符号是最大匹配
* ${file:0:5}:提取最左边的 5 个字节:/dir1
* ${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
```
# 例子
str="abbc,def,ghi,abcjkl"
echo ${str#a*,}
# output: def,ghi,abcjkl
echo ${str##a*,}
# output: abcjkl
str="dir1/dir2/dir3/my.file.txt"
# output: dir1/dir2/dir3
echo ${str%/*}
# output: dir1
echo ${str%%/*}
```
参考地址: [Bash Reference Manual](http://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#What-is-Bash_003f)
#### # $() 与 ``
在 bash shell 中 $() 与`` 都是用来做命令替换用的
* ${} 用来做变量的替换
* $(()) 可以做一些运算,抛弃 expr 等操作
#### # (),(()),[],[[]],{},$(),$(()) 区别
##### # $()和 \`\`
> 命令替换
- $() 并不是所有shell都支持, `` 几乎都支持,但是容易看错
```
echo `uname -a`
echo $(uname -a)
```
##### # ${ }
- `${ }`用于变量替换。一般情况下,`$var` 与 `${var}` 并没有什么不一样,但是用 `${ }` 会比较精确的界定变量名称的范围。
```shell script
name=abc
echo "name: ${name}"
```
##### # $[] $(())
> 主要数学运算
> If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1
`$[]`和 `$(())` 是一样的,都是进行数学运算的。支持 `+ - * / %(“加、减、乘、除、取模”)`。但是注意,bash只能作整数运算,**对于浮点数是当作字符串处理的**。
```shell script
echo $[1*4]
echo $[1+4]
echo $[1-4]
echo $((1+2))
```
> let,expr 也支持数学运算
```shell script
let a=5+4
echo $a
```
##### # ()
> 主要处理数学,数组,子进程
- 命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。
- 命令替换。等同于`cmd`,shell扫描一遍命令行,发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,得到其标准输出,再将此输出放到原来命令。有些shell不支持,如tcsh。
- 用于初始化数组。如:array=(a b c d)
```shell script
# 子进程内的变量,不能再外面使用
(name=1)
echo $name # 输出为空
# () 创建 subshell
for i in `seq 100`;do
(
curl -s http://ip.wlwz620.com -o /dev/null -w "http_code:%{http_code}:%{time_total}s\n";sleep 1;
)
done
# 验证(pid 不断变化)
ps -ef | grep curl
```
##### # (())
> 主要针对数学处理
* 数学运算: + - * / % (bash只能作整数运算,对于浮点数是当作字符串处理的)
* 逻辑运算: && ||
* 位移运算: <<,>>
* 异或运算: ~
* 位布尔: | &
* 其他: var++(后增),var--(后减),++var(先增),--var(先减) , ! 逻辑求反,** 幂运算
* ==
```shell script
# 整数比较
if (( 1 == 2 )); then
echo true
else
echo false
fi
# 字符串比较
if (( "hello" == "world" )); then
echo true
else
echo false
fi
```
##### # [[]]
> [] 的加强版
- bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。
- 数字比较: -eq, -ne, -gt, -ge, -lt, -le
- 字符串比较: =, != , -z 字符串, -n 字符串,>,<
使用`[[ ... ]]`条件判断结构,而不是`[ ... ]`,能够防止脚本中的许多逻辑错误。比如,`&&、||、<和>` 操作符能够正常存在于`[[ ]]`条件判断结构中,但是如果出现在`[ ]`结构中的话,会报错。
比如可以直接使用`if [[ $a != 1 && $a != 2 ]]`, 如果不使用双括号, 则为`if [ $a -ne 1] && [ $a != 2 ]`或者`if [ $a -ne 1 -a $a != 2 ]`。
````shell script
### 数字比较
if [[ 11 -gt 2 ]];then echo true;else echo false;fi
# output: true
# !!! 字符串 ascii 比较
if [[ 11 < 2 ]];then echo true;else echo false;fi
# output: true
### 字符串比较
if [[ '11' == 11 ]];then echo true;else echo false;fi
# output: true
if [[ '11' = 11 ]];then echo true;else echo false;fi
# output: true
if [[ '11' != 11 ]];then echo true;else echo false;fi
# output: false
### && ||
if [[ 1 -gt 2 && 2 -gt 3 ]];then echo true;else echo false;fi
# output: false
if [[ 1 -lt 2 && 2 -gt 3 ]];then echo true;else echo false;fi
# output: false
if [[ 1 -lt 2 && 2 -lt 3 ]];then echo true;else echo false;fi
# output: true
# 前后有空格
if [[ 1!=2 && 1=2 ]];then echo 1; fi
1
# 正确
if [[ 1 != 2 && 1 = 1 ]];then echo 1; fi
# 逻辑真 (regex matching)
# 只有 [[]] 支持 =~ , 右边的当做正则表达式
if [