在php-docker中能够发现一个docker-php-entrypoint.sh
脚本,内容以下php
#!/bin/sh
set -e
# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
set -- php-fpm "$@"
fi
exec "$@"
复制代码
简单解释一下这个脚本涉及到的几个知识点,并引出本文要介绍的Shell Parameter Expansion概念html
$@
属于shell脚本中几个特殊参数中的一个,表明了传递给脚本的全部参数,同时还有其余一些特殊变量能够参考文档Special Parameterslinux
我这里列举以下docker
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名 |
$n | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 |
$# | 传递给脚本或函数的参数个数。 |
$* | 传递给脚本或函数的全部参数:"$1", "$2", "$3", 每一个变量是独立的。 |
$@ | 传递给脚本或函数的全部参数:"$1 $2 $3",表明 |
$? | 上个命令的退出状态,或函数的返回值。 |
$$ | 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 |
$*
和 $@
的区别$*
和 $@
都表示传递给函数或脚本的全部参数,不被双引号(" ")包含时,都以"$1" "n" 的形式输出全部参数。shell
可是当它们被双引号(" ")包含时express
"$*"
会将全部的参数做为一个总体,以"$1 "$@"
会将各个参数分开,以"$1" "使用$ sh script.sh
执行脚本时,当前shell是父进程,生成一个子shell进程,在子shell中执行脚本。脚本执行完毕,退出子shell,回到当前shell。编程
$ ./script.sh
与$ sh script.sh
等效。数组
使用$ source script.sh
方式,在当前上下文中执行脚本,不会生成新的进程。脚本执行完毕,回到当前shell。安全
$ . script.sh
与$ source script.sh
等效。bash
使用exec command方式,会用command进程替换当前shell进程,而且保持PID不变。执行完毕,直接退出,不回到以前的shell环境。
set 属于shell内置命令,参考文档#Modifying-Shell-Behavior
当单独执行set命令时会显示全部的环境变量和 Shell 函数
直接使用set
+prams 能够为当前环境设置参数,例如
$ set a b c
$ echo $1
a
$ echo $2
b
$ echo $3
c
复制代码
在"set -e"以后出现的代码,一旦出现了返回值非零,整个脚本就会当即退出,用于保证代码安全性
其实--
是一个单独的shell特性,和set无关,它表明了一个命令的选项(options)已经结束,后面的都已是这个命令的参数了,例如:
grep -- -v file
复制代码
若是你想搜索file中的字符串'-v',直接grep '-v' file
或是其余方法都是致使-v
被识别为grep的选项,当加入--
表明选项已经没有了,-v被理解为第一个参数,file被理解为第二个参数,因而就能够在file搜索'-v'了
对于set --
也是同样--
标志着set的选项已经结束,后面的都是set的参数了。
为何要这么写呢?很明显是为了防止set后面设置的变量里含有-
致使被解释为set自身的选项,如set -- -e
这种状况
因此最开始的set -- php-fpm "$@"
就能够解释为:把当前环境的参数设置成 php-fpm $@
即 $@ = php-fpm $@
对于那句if [ "${1#-}" != "$1" ]
咱们在下面展开讲解,根据注释咱们能够知道它判断的是:传入这个脚本的第一个参数是否是-f
or --some-option
这种类型
因此总结一下:
当咱们sh docker-php-entrypoint.sh -F
即直接在脚本后面使用-
加参数时,实际执行的是php-fpm -F
当咱们sh docker-php-entrypoint.sh ls -a
即直接在脚本后面直接执行命令时,实际执行的就是传入的命令ls -a
下面进入本文的主题
在shell中可使用花括号${}
包裹参数来防止紧跟在参数后面的字符串被看成参数变量名的一部分,因此最基本的参数展开就是${parameter}
。
其中引用的参数并非parameter而是parameter的实际的值
parameter="var"
var="hello"
echo ${!parameter}
hello
复制代码
下面的几种形式如${parameter:-word}
是判断parameter为unset或者parameter=NULL来执行后续的扩展操做,即(!isSet(parameter) || parameter==NULL)
当忽略冒号后的结果${parameter:-}
,判断parameter存在且不为NULL,即(isSet(parameter) && parameter != NULL)
当忽略冒号${parameter-word}
则只判断parameter是否存在,也就是parameter能够为NULL,即isSet(parameter)
当parameter未设置或者为空则替换成word
set a b
echo ${3:-word} # word
echo ${1:-word} # a
echo ${par:-word} # word
par=c
echo ${par:-word} # c
复制代码
同上。也就是给parameter一个默认参数,因此位置参数和特殊参数不能以这种方式分配。即不能${3:=world}
set a b
echo ${3:=word} # -bash: $3: cannot assign in this way
echo ${1:=word} # a
echo ${par:=word} # word
par=c
echo ${par:=word} # c
复制代码
当变量 parameter 未设置或为空,shell 也是可交互时,进行报错而且退出。若是 shell 不可交互,则发生变量替换。
set a b
echo ${3:?word} # -bash: 3: word
echo $? # 1 说明错误
echo ${1:?word} # a
echo ${par:?word} # -bash: par: word
par=c
echo ${par:?word} # c
复制代码
若是 parameter 为空或未设置,那么就什么都不作。否则使用 word 进行替换。
set a b
echo ${3:+word} # 空
echo ${1:+word} # word
echo ${par:+word} # 空
par=c
echo ${par:+word} # word
复制代码
和大部分编程语言字符串切片同样,offset表明偏移值,length表明字符长度,须要注意的有如下几点
string=01234567890abcdefgh
echo ${string: 1} # 1234567890abcdefgh
echo ${string: 1: 2} # 12
复制代码
string=01234567890abcdefgh
echo ${string: -2} # gh
echo ${string:-2} # 01234567890abcdefgh 没有空格被解释成了:-
echo ${string: -3:2} # fg
复制代码
string=01234567890abcdefgh
echo ${string: -7:-2} # bcdef
复制代码
string=01234567890abcdefgh
set -- 1 2 3 4 5 6 7 8 9 0 a b c d e f g h
echo ${string:7} # 890abcdefgh
echo ${@:7} # 7 8 9 0 a b c d e f g h 注意和上面的区别
echo ${@: 0} # 1 2 3 4 5 6 7 8 9 0 a b c d e f g h
echo ${@: 1} # 1 2 3 4 5 6 7 8 9 0 a b c d e f g h 和上面结果同样
echo ${string: -7} # bcdefgh
echo ${@: -7} # b c d e f g h
echo ${@:7:-2} # bash: -2: substring expression < 0
复制代码
@
或者*
的数组,表达式被扩展为从${parameter[offset]}日后length的长度的数组。负数的offset表明从尾部开始计数。length不能为负数array=(0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h)
echo ${array[@]:7} # 7 8 9 0 a b c d e f g h
echo ${array[@]:7:2} # 7 8
echo ${array[@]: -7:2} # b c
echo ${array[@]: -7:-2} # bash: -2: substring expression < 0
复制代码
除了位置参数是按1开始从头计算偏移值,其余都是按0开始计算偏移值的,从尾部都是按-1开始计算偏移值的
var1=abc
var2=def
$ for v in ${!var@};do echo $v;done;
var1
var2
$ for v in ${!var*};do echo $v;done;
var1
var2
# 以上两种状况没有区别
for v in "${!var*}";do echo $v;done;
var1 var2
for v in "${!var@}";do echo $v;done;
var1
var2
# @的被拓展成了两个变量
复制代码
arr=(a b c d e f g h)
echo ${!arr[@]} # 0 1 2 3 4 5 6 7
复制代码
echo ${!string[*]} # 为空
string=01234567890abcdefgh
echo ${!string[@]} # 0
复制代码
$ arr=(a b c d e f g h)
$ for i in "${!arr[*]}";do echo $i; done;
0 1 2 3 4 5 6 7
$ for i in "${!arr[@]}";do echo $i; done;
0
1
2
3
4
5
6
7
复制代码
若是parameter是字符串,表达式扩展为字符串的长度
若是parameter是*或者@,表达式扩展为参数的个数
若是parameter是一个数组名,而且下标为*或者@,表达式扩展为数组的元素个数
set a b
echo ${#@} # 2
echo ${#1} # 1
par=c
echo ${#par} # 1
arr=(1 2 3)
echo ${#arr[@]} # 3
复制代码
若变量内容从头开始的数据符合“关键字”,则将符合的最短(使用#)或者最长(使用##)数据删除
*
、?
等set -- ab-cd-ef ef-gh-ij
echo ${1#-} # ab-cd-ef 只会从头部开始匹配,开头没有-因此就不会匹配上
echo ${1#*-} # cd-ef
echo ${1##*-} # ef ##会匹配最长的数据
复制代码
set -- ab-cd-ef ef-gh-ij
echo ${@#*-} # cd-ef gh-ij
echo ${@##*-} # ef ij
复制代码
arr=(--a --b --c)
echo ${arr[@]#-} # -a -b -c
复制代码
总体逻辑和上面的${parameter#word}
、${parameter##word}
差异不大,只是这二者是从尾开始匹配关键字
set -- abcd-- efgh--
echo ${1%-} # abcd-
echo ${1%%-} # abcd-
echo ${1%-?} # abcd
echo ${@%-} # abcd- efgh-
arr=(a-- b-- c--)
echo ${arr[@]%-} # a- b- c-
复制代码
字符移除能够实现一些很常见的操做:
FILENAME=linux_bash.sh
echo ${FILENAME%.*} # linux_bash
echo ${FILENAME##*.} # sh
复制代码
FILENAME=/home/somebody/linux_bash.sh
echo ${FILENAME##*/} # linux_bash.sh
echo ${FILENAME%/*} # /home/somebody
复制代码
docker-php-entrypoint.sh
,判断某字符串是否以某字符开头$ OPT='-option'
$ if [ ${OPT#-} != ${OPT} ];
> then
> echo "start with -"
> else
> echo "not start with -"
> fi
start with -
复制代码
将parameter中出现的第一个pattern替换为string。
其中:
*
、?
、[]
等通配符string=abceddabceddabcedd
echo ${string/d?a/f} # abcefbceddabcedd 只替换了第一个dda
echo ${string/d*a/f} # abcefbcedd 替换了ddabcedda
复制代码
/
开头, 将会用string替换全部符合的匹配项string=abceddabceddabcedd
echo ${string//d?a/f} # abcefbcefbcedd 第二个dda也被替换成了f
复制代码
若是pattern使用#
开头, 将会用string替换开头的匹配项,这个就是默认的表现
若是pattern使用%
开头, 将会用sting替换结尾处的一个匹配项
string=abceddabceddabcedd
echo ${string/%d?a/f} # abceddabceddabcedd 注意此时并未匹配到最后一个dda,缘由未知
echo ${string/%dd/f} # abceddabceddabcef 替换了尾部的dd
复制代码
/
string=abceddabceddabcedd
echo ${string/dd/} # abceabceddabcedd 删除了第一个dd
复制代码
若是经过Shopt Builtin开启了大小写不敏感,那么则能够按照忽略大小写来匹配
若是参数是@或者*,将会对每个位置参数进行匹配替换操做
set -- abc abd abe
echo ${@/a/f} # fbc fbd fbe
复制代码
arr=(abc abd abe)
echo ${arr[@]/a/f} # fbc fbd fbe
复制代码
注:此操做仅适用于bash4.0往上版本
这些拓展用于修改字符串中的大小写。pattern表示的是匹配的模式,对于能匹配项会进行大小写转换(此处表示不理解)
^
会把开头的小写字母转换成大写,^^
会转换全部小写成大写par='abc'
echo ${par^} # Abc
echo ${par^^} # ABC
复制代码
,
会把开头的大写转换成小写,,,
会把因此大写转换成小写par='ABC'
echo ${par,} # aBC
echo ${par,,} # abc
复制代码
set -- ABC DEF HIJ
echo ${@,} # aBC dEF hIJ
echo ${@,,} # abc def hij
复制代码
arr=(ABC DEF HIJ)
echo ${arr[@],} # aBC dEF hIJ
echo ${arr[@],,} # abc def hij
复制代码
注:此操做仅适用于bash4.0往上版本
此拓展根据操做符(operator)执行参数转换或者,操做符以下
将字符串使用引号包裹
par='abc def'
echo ${par@Q} # 'abc def'
复制代码
对于使用反斜线\
后的字符一概按转义处理
# 对于双引号包裹的字符串
par="abc\"u"
echo ${par} # abc"u 按照转义解释
echo ${par@E} # abc"u 按照转义解释
# 对于双引号包裹的字符串
par="abc\'u"
echo ${par} # abc\'u 此时单引号不须要转义,因此展现了\
echo ${par@E} # abc'u E操做,继续按照转义来解释了\
复制代码
若是parameter含有prompt string时,按照prompt解释(默认按照字符串解释)
par="\@-abcd-\u"
echo ${par} # \@-abcd-\u
echo ${par@P} # 05:09 AM-abcd-I have no name!
复制代码
拓展成参数赋值的语句
a=2
par=$a+1
echo ${par} # 2+1
echo ${par@A} # par='2+1' 此时$a已经被解释成实际值了
复制代码
由参数属性值组成的字符串
对于@
和*
,此操做会对每个位置参数进行处理
对于下标为@
或*
的数组,此操做会对每个数组元素进行处理