在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 n"的形式輸出全部參數;"$@"
會將各個參數分開,以"$1" "n" 的形式輸出全部參數。使用$ 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已經被解釋成實際值了
複製代碼
由參數屬性值組成的字符串
對於@
和*
,此操做會對每個位置參數進行處理
對於下標爲@
或*
的數組,此操做會對每個數組元素進行處理