溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

shell 命令行參數(shù)(基本)

發(fā)布時(shí)間:2020-05-28 11:26:41 來(lái)源:網(wǎng)絡(luò) 閱讀:1175 作者:騎士救兵 欄目:系統(tǒng)運(yùn)維

命令行參數(shù)

$0 表示程序名。
$1 至 $9則是位置參數(shù)。
$# 表示參數(shù)的個(gè)數(shù)。
$* 將所有參數(shù)當(dāng)做一個(gè)整體來(lái)引用
$@ 把每個(gè)參數(shù)作為一個(gè)字符串返回,可以使用for循環(huán)來(lái)遍歷
$? 最近一個(gè)執(zhí)行的命令的退出狀態(tài)。0表示執(zhí)行成功
$_ 上一個(gè)命令的最后一個(gè)參數(shù)。使用快捷鍵 ESC+. 也是這個(gè)效果

位置參數(shù)

位置參數(shù)不止9個(gè),更多的參數(shù)也是一樣支持的。只是要使用\${10}這樣的形式引用。
\$1 和 \${1}的效果是一樣的。
不用花括號(hào)的話,\$10 會(huì)被認(rèn)為是 \$1 和一個(gè)字符 0。

帶空格的參數(shù)值
每個(gè)參數(shù)都是用空格分隔的。要在參數(shù)值中包含空格,必須要使用引號(hào)(單引號(hào)或雙引號(hào)都可)。

將文本字符串作為參數(shù)傳遞時(shí),引號(hào)并非數(shù)據(jù)的一部分。它們只是表明數(shù)據(jù)的起止位置。

獲取腳本名

\$0 表示腳本名,但是不同的調(diào)用方法返回的結(jié)果也是不同的。下面的腳本就是簡(jiǎn)單的打印\$0的值:

$ cat filename.sh 
#!/bin/bash
echo $0
$ ./ filename.sh
-bash: ./: 是一個(gè)目錄
$ cat filename.sh 
#!/bin/bash
echo $0
$ ./filename.sh
./filename.sh
$ bash filename.sh 
filename.sh
$ bash /root/filename.sh 
/root/filename.sh
$ 

使用 basename 命令
如果要使用腳本名稱(chēng)來(lái)進(jìn)行判斷,可以先用命令 basename 把路徑的信息給過(guò)濾掉。命令的效果如下:

$ basename /var/log/messages 
messages
$ 

所以上面的腳本可以修改成這樣:

$ cat filename.sh 
#!/bin/bash
echo $(basename $0)
$ ./filename.sh
filename.sh
$ bash filename.sh 
filename.sh
$ bash /root/filename.sh 
filename.sh
$ 

測(cè)試參數(shù)

在腳本中使用參數(shù)要確保參數(shù)存在,否則運(yùn)行時(shí)有可能會(huì)報(bào)錯(cuò):

$ cat add.sh 
#!/bin/bash
echo $1 + $2 = $[ $1 + $2 ]
$ ./add.sh 1 2
1 + 2 = 3
$ ./add.sh 1
./add.sh:行2: 1 +  : 語(yǔ)法錯(cuò)誤: 期待操作數(shù) (錯(cuò)誤符號(hào)是 "+  ")
$ 

如果只是當(dāng)做字符串引用,也不會(huì)報(bào)錯(cuò)。沒(méi)有傳參的參數(shù)默認(rèn)都是空:

$ cat hello.sh 
#!/bin/bash
echo Hello $1 $2.
$ ./hello.sh Tom Jerry
Hello Tom Jerry.
$ ./hello.sh Jerry
Hello Jerry .
$ ./hello.sh
Hello .
$ 

判斷參數(shù)是否存在
在 shell 中利用 -n 來(lái)判定字符串非空,-z 則正好相反,空即是真。上面已經(jīng)測(cè)試過(guò)了,未定義的參數(shù)默認(rèn)是空:

$ cat hello.sh 
#!/bin/bash
if [ -n "$1" ]
then
    echo Hello $1.
else
    echo Hello Nobody.
fi
$ ./hello.sh Tom
Hello Tom.
$ ./hello.sh
Hello Nobody.
$ 

這里的判斷的 \$1 要加上雙引號(hào),否則會(huì)被認(rèn)為是字符串。一個(gè)字符串當(dāng)然非空,所以結(jié)果會(huì)永遠(yuǎn)為真。

判斷參數(shù)的個(gè)數(shù)
上面的例子的腳本也可以通過(guò)判斷參數(shù)數(shù)量是否大于0來(lái)實(shí)現(xiàn):

$ cat hello.sh 
#!/bin/bash
echo 參數(shù)數(shù)量: $#
if [ $# -gt 0 ]
then
    echo Hello $1.
else
    echo Hello Nobody.
fi
$ ./hello.sh
參數(shù)數(shù)量: 0
Hello Nobody.
$ ./hello.sh Tom
參數(shù)數(shù)量: 1
Hello Tom.
$ ./hello.sh Tom Jerry
參數(shù)數(shù)量: 2
Hello Tom.
$ 

這里 -gt 比較的是前后兩個(gè)數(shù)字(INT),所以\$#是不加引號(hào)的。
用這種方法也能判斷參數(shù)是否存在。兩種方法,效果一樣,不同的書(shū)上都看到有人使用。

這里是一樣加法的例子,必須要傳入2個(gè)參數(shù):

$ cat add.sh 
#!/bin/bash
if [ $# -eq 2 ]
then
    echo $1 + $2 = $[ $1 + $2 ]
else
    echo 需要參數(shù): 2, 實(shí)際參數(shù): $#.
fi
$ ./add.sh 1 2
1 + 2 = 3
$ ./add.sh 1 2 3
需要參數(shù): 2, 實(shí)際參數(shù): 3.
$ ./add.sh 1
需要參數(shù): 2, 實(shí)際參數(shù): 1.
$ 

如果要表示不相等,就是 if [ $# -ne 2 ]

獲取最后一個(gè)參數(shù)
這是一個(gè)使用 \$# 的小技巧。使用${$#}似乎就是參數(shù)的最后一個(gè)變量了。
但是其實(shí)不然,花括號(hào)里不能這樣用\$,這里要把里面的換成感嘆號(hào):

$ cat hello.sh 
#!/bin/bash
if [ $# -gt 0 ]
then
    echo Hello ${!#}.
else
    echo Hello Nobody.
fi
$ ./hello.sh Tom Jerry
Hello Jerry.
$ 

如果沒(méi)有任何命令行參數(shù),那么就是返回\$0,也就是腳本名。

上面感嘆號(hào)的問(wèn)題,效果是引用變量的值而不是變量自身。類(lèi)似于指針的取值。把#號(hào)換成一個(gè)有名字的變量來(lái)說(shuō)明比較直觀:

$ cat parameter.sh 
#!/bin/bash
paramater=key
key=value
echo "${paramater}"
echo "${!paramater}"
echo "${key}"
$ ./parameter.sh 
key
value
value
$ 

不加感嘆號(hào),就是直接去該變量的值。加上感嘆號(hào),就是去變量值所對(duì)應(yīng)的變量名的那個(gè)變量的值。

獲取所有參數(shù)

\$* 和 \$@ 都是表示所有的字符串,但是在遍歷的時(shí)候會(huì)有區(qū)別:

$ cat all.sh 
#!/bin/bash
echo '$* 的效果:'
count=1
for i in "$*"
do
    echo $count: $i
    count=$[ $count + 1 ]
done
echo '$@ 的效果:'
count=1
for i in "$@"
do
    echo $count: $i
    count=$[ $count + 1 ]
done
$ ./all.sh Oliver Barry Kara Sara Kane
$* 的效果:
1: Oliver Barry Kara Sara Kane
$@ 的效果:
1: Oliver
2: Barry
3: Kara
4: Sara
5: Kane
$ 

\$*就一個(gè)整體的值,無(wú)法遍歷。要遍歷每一個(gè)變量要使用\$@。這里的雙引號(hào)很重要。

不加引號(hào)的話,就是把 \$* 和 \$@ 的內(nèi)容(變量解析后就是多個(gè)詞)傳遞給for循環(huán)遍歷,這樣兩個(gè)參數(shù)的效果是一樣的。和直接傳不加引號(hào)的字符串的效果一樣。
加上引號(hào),引號(hào)里的內(nèi)容就是一個(gè)整體。如果是\$*,這個(gè)整體里的所有內(nèi)容還是一個(gè)詞,不會(huì)拆。如果是\$@,這個(gè)整體里會(huì)按空格拆分成多個(gè)詞。
下面是演示的效果:

$ cat all2.sh 
#!/bin/bash
echo '$* 不加引號(hào)的效果:'
count=1
for i in $*
do
    echo $count: $i
    count=$[ $count + 1 ]
done
echo '$@ 不加引號(hào)的效果:'
count=1
for i in $@
do
    echo $count: $i
    count=$[ $count + 1 ]
done
echo '直接遍歷不加引號(hào)的字符的效果:'
count=1
for i in Oliver Barry Kara Sara Kane
do
    echo $count: $i
    count=$[ $count + 1 ]
done
echo '加引號(hào)遍歷的效果:'
count=1
for i in "Oliver Barry Kara Sara Kane"
do
    echo $count: $i
    count=$[ $count + 1 ]
done
$ ./all2.sh Oliver Barry Kara Sara Kane
$* 不加引號(hào)的效果:
1: Oliver
2: Barry
3: Kara
4: Sara
5: Kane
$@ 不加引號(hào)的效果:
1: Oliver
2: Barry
3: Kara
4: Sara
5: Kane
直接遍歷不加引號(hào)的字符的效果:
1: Oliver
2: Barry
3: Kara
4: Sara
5: Kane
加引號(hào)遍歷的效果:
1: Oliver Barry Kara Sara Kane
$ 

強(qiáng)調(diào):特殊參數(shù)\$@一定要用在雙引號(hào)內(nèi),效果是每個(gè)參數(shù)都擴(kuò)展為分隔的單詞。在使用for循環(huán)遍歷的時(shí)候會(huì)體現(xiàn)出效果。

移動(dòng)變量 shift

shift 命令能夠用來(lái)操作命令行參數(shù)。默認(rèn)情況下將每個(gè)參數(shù)向左移動(dòng)一個(gè)位置。被移出的參數(shù)就被丟棄了,無(wú)法恢復(fù)。
先掌握這個(gè)命令的使用,使用這個(gè)命令可以方便地解析命令行參數(shù)。

使用示例

下面是一個(gè)簡(jiǎn)單的示例:

$ cat pop.sh 
#!/bin/bash
count=1
while [ -n "$1" ]
# while [ $# -ne 0 ]
do
    echo "$count: $1"
    count=$[ $count + 1 ]
    shift
done
$ ./pop.sh Oliver Barry Kara Sara Kane
1: Oliver
2: Barry
3: Kara
4: Sara
5: Kane
$ 

這里有2中判斷方法來(lái)判斷是否還有參數(shù),效果是一樣的。

移動(dòng)多個(gè)位置

帶參數(shù)執(zhí)行shift,指明要移動(dòng)幾個(gè)位置就可以了:

$ cat pop.sh 
#!/bin/bash
count=1
# while [ -n "$1" ]
while [ $# -ne 0 ]
do
    if [ -n "$2" ]
    then
        echo "$count: $1, $2"
        shift 2
    else
        echo "$count: $1"
        shift
    fi
    count=$[ $count + 1 ]
done
$ ./pop.sh Oliver Barry Kara Sara Kane
1: Oliver, Barry
2: Kara, Sara
3: Kane
$ 

簡(jiǎn)單修改下上面的腳本,一次輸出2個(gè)參數(shù),然后移動(dòng)2個(gè)位置。

處理選項(xiàng)

當(dāng)shell腳本需要多個(gè)命令行參數(shù)時(shí),在調(diào)用腳本的時(shí)候就必須將所有參數(shù)按固定的順序。
或者還可以使用選項(xiàng)來(lái)指定參數(shù)的值。

case 配合 shift

這個(gè)例子里有帶值的選項(xiàng)也有不帶值的選項(xiàng):

$ cat format.sh 
#!/bin/bash
prefix=""    # 前綴
base="test"  # 默認(rèn)字符串
suffix=""    # 后綴
upper=off    # 是否大寫(xiě)
# 解析命令行參數(shù)
while [ -n "$1" ]
do
    case "$1" in
        -a) suffix="$2"
            shift ;;
        -b) prefix="$2"
            shift ;;
        -s) base="$2"
            shift ;;
        -u) upper=on ;;
         *) echo "$1 is not an option"
            exit 1 ;;  # 發(fā)現(xiàn)未知參數(shù),直接退出
    esac
    shift
done
# 添加前綴和后綴
output="${prefix:+${prefix}_}${base}${suffix:+_${suffix}}"
# 判斷是否要全大寫(xiě)輸出
if [ $upper = on ]
then
    output=${output^^}
fi
# 輸出結(jié)果
echo "$output"
$ ./format.sh -a after
test_after
$ ./format.sh -s hello -b befor
befor_hello
$ ./format.sh -s hello -u -a after -b befor
BEFOR_HELLO_AFTER
$ ./format.sh -s hello -u -a after -b befor -l
-l is not an option
$ 

case語(yǔ)句找到一個(gè)選項(xiàng)就處理一個(gè)選項(xiàng)。如果還需要在命令行提供其他參數(shù),可以在通用情況的處理部分中處理。而這里因?yàn)椴恍枰峁┤魏螀?shù),凡是解析不正確的就報(bào)告錯(cuò)誤并退出(exit 1)。

能解析參數(shù)的版本
這個(gè)版本匹配所有的參數(shù)進(jìn)行格式化輸出:

$ cat format.sh 
#!/bin/bash
prefix=""    # 前綴
base="test"  # 默認(rèn)字符串
suffix=""    # 后綴
upper=off    # 是否大寫(xiě)
# 顯示聲明一下這是個(gè)數(shù)組變量,其實(shí)沒(méi)有必要
declare -a names  # 需要格式化輸出的所有原始字符串
# 解析命令行參數(shù)
while [ -n "$1" ]
do
    case "$1" in
        -a) suffix="$2"
            shift ;;
        -b) prefix="$2"
            shift ;;
        -s) base="$2"
            shift ;;
        -u) upper=on ;;
         *) names=("${names[@]}" "$1") ;;
    esac
    shift
done
names[0]=${names[0]:-$base}
for name in "${names[@]}"
do
    # 添加前綴和后綴
    output="${prefix:+${prefix}_}${name}${suffix:+_${suffix}}"
    # 判斷是否要全大寫(xiě)輸出
    if [ $upper = on ]
    then
        output=${output^^}
    fi
    # 輸出結(jié)果
    echo "$output"
done
$ 
$ ./format.sh -a after -b befor -u value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u -v value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_-V_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ 

看最后的兩項(xiàng)的結(jié)果,提供的命令行參數(shù)有問(wèn)題,但是程序無(wú)法發(fā)現(xiàn)。
倒數(shù)第二項(xiàng)可以認(rèn)為提供的參數(shù)是對(duì)的,但是選項(xiàng)和參數(shù)交替出現(xiàn)。
而最后一項(xiàng)提供了一個(gè)錯(cuò)誤的選項(xiàng),但是無(wú)法識(shí)別出來(lái)。
解決這個(gè)問(wèn)題,需要更加規(guī)范的方法來(lái)分離參數(shù)和選項(xiàng)。下一小節(jié)的內(nèi)容。

數(shù)組帶空格的問(wèn)題
數(shù)組添加元素有很多方法,這里是一種重新創(chuàng)建數(shù)組的做法:

array_name=("${array_name[@]}" value1 ... valueN)

可以一次添加多個(gè)元素,如果字符串包含空格,就要加上引號(hào)。

和命令行參數(shù)的\$@與\$*一樣,數(shù)組所有的元素也有這兩個(gè)類(lèi)似的符號(hào)。最嚴(yán)謹(jǐn)?shù)姆椒ㄊ鞘褂?"${names[@]}" 使用帶雙引號(hào)的@。
添加元素和取出元素的時(shí)候都要注意,否則存在帶空格的元素的時(shí)候就會(huì)破壞數(shù)組原本的元素分隔。
添加元素這里使用:

names=("${names[@]}" "$1")

不單是數(shù)組里的元素,被添加的元素也要加上雙引號(hào),否則如果有空格,就會(huì)按多個(gè)元素被添加進(jìn)數(shù)組。

遍歷元素使用:

for name in "${names[@]}"

只有添加的時(shí)候正確了,才能正確的遍歷。然后遍歷的時(shí)候也要保證正確。
驗(yàn)證效果:

$ ./format.sh -a after -b befor -u value1 "value2 value3" value4
BEFOR_VALUE1_AFTER
BEFOR_VALUE2 VALUE3_AFTER
BEFOR_VALUE4_AFTER
$ 

完美。

分離參數(shù)和選項(xiàng)

這里的參數(shù)就是命令行參數(shù)中除了定義的選項(xiàng)之外,其他額外的參數(shù)。要同時(shí)處理參數(shù)和選項(xiàng),就要用特殊字符(雙破折線--)將二者分開(kāi)。雙破折線表明選項(xiàng)列表結(jié)束,雙破折線后面的都是參數(shù)。基于這個(gè)邏輯,只要在case語(yǔ)句中加一項(xiàng)判斷就行了。
把上面的腳本做一些修改:

$ cat format.sh 
#!/bin/bash
prefix=""    # 前綴
base="test"  # 默認(rèn)字符串
suffix=""    # 后綴
upper=off    # 是否大寫(xiě)
# 顯示聲明一下這是個(gè)數(shù)組變量,其實(shí)沒(méi)有必要
declare -a names  # 需要格式化輸出的所有原始字符串
# 解析選項(xiàng)
while [ -n "$1" ]
do
    case "$1" in
        -a) suffix="$2"
            shift ;;
        -b) prefix="$2"
            shift ;;
        -s) base="$2"
            shift ;;
        -u) upper=on ;;
        --) shift
            break ;;
         *) echo "$1 is not an option"
            exit 1 ;;  # 發(fā)現(xiàn)未知參數(shù),直接退出
    esac
    shift
done
# 解析參數(shù)
while [ -n "$1" ]
do
    names=("${names[@]}" "$1")
    shift
done
names[0]=${names[0]:-$base}
for name in "${names[@]}"
do
    # 添加前綴和后綴
    output="${prefix:+${prefix}_}${name}${suffix:+_${suffix}}"
    # 判斷是否要全大寫(xiě)輸出
    if [ $upper = on ]
    then
        output=${output^^}
    fi
    # 輸出結(jié)果
    echo "$output"
done
$ 

基于這個(gè)版本,在使用的時(shí)候,需要先輸入選項(xiàng),然后使用雙破折線隔開(kāi),再輸入?yún)?shù)。當(dāng)腳本遇到雙破折線時(shí),它會(huì)停止處理選項(xiàng),并將剩下的參數(shù)都當(dāng)作參數(shù):

$ ./format.sh -a after -b befor -u value1 value2 value3
value1 is not an option
$ ./format.sh -a after -b befor -u -- value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after -b befor -v -u -- value1 value2 value3
-v is not an option
$ 

第一次沒(méi)有使用雙破折線,所以報(bào)錯(cuò)。
第二次正確的用雙破折號(hào)分隔了參數(shù)和選項(xiàng)。
第三次在選項(xiàng)部分出現(xiàn)了未定義的選項(xiàng),也能發(fā)現(xiàn)錯(cuò)誤。

小結(jié)
這一小節(jié)的內(nèi)容也是為下面的getopt命令做鋪墊。getopt就是可以幫我們完成命令行參數(shù)的解析,返回一個(gè)用雙破折線隔開(kāi)選項(xiàng)和參數(shù)的規(guī)整的參數(shù)列表。
另外這里還不支持選項(xiàng)合并:

$ ls -al

這些問(wèn)題,用getopt都能解決,而且還支持長(zhǎng)選項(xiàng)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI