日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747


Linux技巧:使用 bash function 命令自定義函數(shù)(完整版)

 

定義函數(shù)的兩種格式

在 bash 中,定義函數(shù)時(shí),function 關(guān)鍵字是可選的,查看man bash手冊(cè),里面提到定義函數(shù)的兩種格式如下:

name () compound-command [redirection]

function name [()] compound-command [redirection]

從中可以看到,當(dāng)不寫(xiě) function 關(guān)鍵字時(shí),函數(shù)名后面一定要跟著小括號(hào)(),而寫(xiě)了 function 關(guān)鍵字時(shí),小括號(hào)是可選的。

關(guān)于 compound-command 的說(shuō)明,同樣可以查看 man bash 手冊(cè),里面提到下面幾種形式:

A compound command is one of the following:

(list) list is executed in a subshell environment. Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.

{ list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. The return status is the exit status of list.

常見(jiàn)的是 { list; } 這種形式,但是寫(xiě)為 (list) 也可以。

舉例說(shuō)明如下:

$ testpwd() (pwd)
$ testpwd
/home/sample/
$ testpwd() ( pwd )
$ testpwd
/home/sample/
$ testpwd() (pwd;)
$ testpwd
/home/sample/

這里定義了一個(gè) testpwd 函數(shù),它自身的代碼是用小括號(hào)()括起來(lái)的 pwd 命令,這個(gè)命令跟 () 之間可以有空格,也可以沒(méi)有空格。

在命令后可以加分號(hào),也可以不加分號(hào)。

注意:使用 { list; } 這個(gè)寫(xiě)法時(shí),在 list 后面一定要跟著分號(hào)';',否則會(huì)報(bào)錯(cuò)。而且 list; 和左邊的大括號(hào) { 之間要有空格。

如果寫(xiě)為 {list;} 會(huì)報(bào)錯(cuò),而 { list;} 不會(huì)報(bào)錯(cuò),建議還是寫(xiě)為 { list; } 的形式。

舉例說(shuō)明如下:

$ lsfunc() {ls}
-bash: syntax error near unexpected token `{ls}'
$ function lsfunc() {ls;}
-bash: syntax error near unexpected token `{ls'
$ lsfunc() { ls;}
$ lsfunc
hello.c

調(diào)用 bash shell 的函數(shù)時(shí),不需要寫(xiě)小括號(hào)(),只寫(xiě)函數(shù)名即可

例如執(zhí)行上面的 lsfunc() 函數(shù),直接寫(xiě) lsfunc 就可以。

如果寫(xiě)成 lsfunc() 反而變成重新定義這個(gè)函數(shù)。

在函數(shù)名后面可以提供要傳入的參數(shù)列表,不同參數(shù)之間用空格隔開(kāi)。

如果某個(gè)參數(shù)需要帶有空格,要用引號(hào)把該參數(shù)括起來(lái)。

從函數(shù)中返回內(nèi)容

Bash 要求函數(shù)的返回值必須為一個(gè)整數(shù),不能用 return 語(yǔ)句返回字符串變量。

一般來(lái)說(shuō),該整數(shù)返回值為 0,表示函數(shù)執(zhí)行成功,非0 表示執(zhí)行失敗。

在自定義的函數(shù)里面,執(zhí)行 return 語(yǔ)句會(huì)退出函數(shù),不會(huì)退出整個(gè)腳本。

在函數(shù)里面執(zhí)行 exit 語(yǔ)句則會(huì)退出整個(gè)腳本,而不是只退出函數(shù)。

由于在函數(shù)內(nèi)部用 return 返回,只能返回整數(shù)。

如果想從函數(shù)內(nèi)部把字符串傳遞到函數(shù)之外,可以用 echo 命令來(lái)實(shí)現(xiàn),就是在函數(shù)內(nèi)部打印字符串,然后調(diào)用者獲取標(biāo)準(zhǔn)輸出獲取到打印的字符串。

具體舉例如下:

$ function foo() { echo "foo"; return 0; }
$ var=$(foo)
$ echo ${var}, $?
foo, 0

可以看到,打印結(jié)果是 "foo, 0"。

此時(shí)看起來(lái),這個(gè)函數(shù)像是返回了兩個(gè)值,一個(gè)是通過(guò) $(foo) 獲取 foo 函數(shù)的標(biāo)準(zhǔn)輸出,另一個(gè)是 $? 會(huì)獲取函數(shù)通過(guò) return 語(yǔ)句返回的 0。

如果在函數(shù)中寫(xiě)為 return 1,那么上面的 $? 打印的出來(lái)的值是 1。

下面再舉例說(shuō)明如下:

$ foo() { echo "foo"; }
$ bar() { foo; }
$ foobar() { a=$(foo); }
$ var=$(foo); echo first: ${var}
first: foo
$ var=$(bar); echo second: ${var}
second: foo
$ var=$(foobar); echo third: ${var}
third:

可以看到, foo 函數(shù)將字符串寫(xiě)到標(biāo)準(zhǔn)輸出,var=$(foo); 語(yǔ)句把 foo 函數(shù)的標(biāo)準(zhǔn)輸出賦值給 var 變量,打印 var 變量的值是 "foo"。

bar() 函數(shù)調(diào)用了 foo 函數(shù),但是沒(méi)有讀取 foo 函數(shù)打印的標(biāo)準(zhǔn)輸出,則這個(gè)標(biāo)準(zhǔn)輸出會(huì)被 bar 函數(shù)繼承,就好象這個(gè)標(biāo)準(zhǔn)輸出是由 bar 函數(shù)輸出一樣,var=$(bar); 語(yǔ)句也會(huì)把 var 變量賦值為 "foo"。

而 foobar 函數(shù)讀取了 foo 函數(shù)的標(biāo)準(zhǔn)輸出,foobar 函數(shù)自身沒(méi)有用 echo 命令來(lái)輸出內(nèi)容,此時(shí)再通過(guò) $(foobar) 來(lái)獲取該函數(shù)的輸出,會(huì)獲取到空,因?yàn)?foo 函數(shù)中的標(biāo)準(zhǔn)輸出給 foobar 讀走了。

注意:這種在函數(shù)內(nèi)部通過(guò) echo 命令輸出字符串的做法有個(gè)缺陷,就是不能再在函數(shù)里面執(zhí)行 echo 語(yǔ)句來(lái)打印調(diào)試信息,這些調(diào)試信息會(huì)被函數(shù)外的語(yǔ)句一起讀取到,有用的結(jié)果和調(diào)試信息都混在一起,如果函數(shù)外的語(yǔ)句沒(méi)有打印這些結(jié)果,就會(huì)看不到調(diào)試信息。

執(zhí)行某個(gè)函數(shù)后,可以使用 $? 表達(dá)式來(lái)獲取函數(shù)的 return 返回值,但是要注意下面的一種情況:

var=$(foo)
if [ "$?" == "0" ]; then
    echo success
fi

此時(shí),不要在 var=$(foo) 和 if [ "$?" == "0" ]; then 之間添加任何語(yǔ)句。

否則,$? 獲取到將不是 $(foo) 的 return 值,判斷就有問(wèn)題,特別是不要添加 echo 調(diào)試語(yǔ)句。

換句話來(lái)說(shuō),這種先執(zhí)行一個(gè)語(yǔ)句,再判斷 $? 的方法不是很可靠,會(huì)受到各種影響,要特別注意代碼語(yǔ)句的順序。

使用函數(shù)名作為函數(shù)指針

在 bash 中,可以通過(guò)如下的方式來(lái)達(dá)到類(lèi)似C語(yǔ)言函數(shù)指針的效果。

假設(shè)有一個(gè) test.sh 腳本,內(nèi)容如下:

#!/bin/bash

# 注意$1后面有一個(gè)分號(hào)';', 少這個(gè)分號(hào)會(huì)報(bào)錯(cuò)
echo_a() { echo aaaa $1; }
echo_b() { echo bbbb $1; }
if [ "$1" == "-a" ]; then
    # 這里的 echo_a 沒(méi)有加雙引號(hào)
    echo_common=echo_a
elif [ "$1" == "-b" ]; then
    # 上面的echo_a沒(méi)加雙引號(hào), 這里加了.
    # 實(shí)際上, 可加可不加, 都可以正確執(zhí)行.
    echo_common="echo_b"
else
    echo ERROR; exit 1
fi
${echo_common} common

這個(gè)腳本通過(guò) echo_common 變量來(lái)保存函數(shù)名,相當(dāng)于是函數(shù)指針,再通過(guò) ${echo_common} 來(lái)調(diào)用它保存的函數(shù)。

在 bash shell 中執(zhí)行 ./test.sh -a 命令,會(huì)輸出 "aaaa common";執(zhí)行 ./test.sh -b 命令,會(huì)輸出 "bbbb common"。

函數(shù)內(nèi)執(zhí)行cd命令的影響

在函數(shù)里面執(zhí)行 cd 命令,切換到某個(gè)目錄后,函數(shù)退出時(shí),當(dāng)前工作目錄還是會(huì)保持在那個(gè)目錄,而不會(huì)自動(dòng)恢復(fù)為原先的工作目錄,需要手動(dòng)執(zhí)行 cd - 命令再切換回去。

假設(shè)有一個(gè) testcd.sh 腳本,里面的內(nèi)容如下:

#!/bin/bash

echo "now, the pwd is: $(pwd)"
cd_to_root() { cd /usr/; }
cd_to_root
echo "after execute the cd_to_root, pwd is: $(pwd)"

這個(gè)函數(shù)先打印出執(zhí)行腳本時(shí)工作目錄路徑,然后執(zhí)行自定義的 cd_to_root 函數(shù),在函數(shù)內(nèi)部切換工作目錄到 "/usr/",最后在 cd_to_root 函數(shù)外面打印工作目錄路徑。

執(zhí)行 ./testcd.sh 腳本,會(huì)輸出下面的內(nèi)容:

[~/sample]$ ./testcd.sh
now, the pwd is: /home/sample
after execute the cd_to_root, pwd is: /usr
[~/sample]$ pwd
/home/sample

可以看到,如果在函數(shù)里面執(zhí)行過(guò) cd 命令,函數(shù)退出后,當(dāng)前工作目錄還是 cd 后的目錄。

但是腳本執(zhí)行結(jié)束后,當(dāng)前 shell 的工作目錄還是之前的工作目錄,不是腳本里面 cd 后的目錄。

在每個(gè) shell 下面,當(dāng)前工作目錄 (working directory ) 是全局的狀態(tài),一旦改變,在整個(gè) shell 里面都會(huì)改變。

而 bash 執(zhí)行腳本時(shí),是啟動(dòng)一個(gè)新的子 shell 來(lái)執(zhí)行,所以腳本內(nèi)部執(zhí)行 cd 命令,會(huì)影響運(yùn)行這個(gè)腳本的子 shell 的工作目錄,但不影響原先父 shell 的工作目錄。

聲明函數(shù)內(nèi)的變量為局部變量

在 bash 中,沒(méi)有使用 local 命令來(lái)聲明的變量都是全局變量,在函數(shù)內(nèi)部定義的變量也是全局變量。

如果沒(méi)有注意到這一點(diǎn),在函數(shù)內(nèi)操作變量可能會(huì)影響到的外部同名變量,造成不預(yù)期的結(jié)果。

為了避免對(duì)外部同名變量造成影響,函數(shù)內(nèi)的變量最好聲明為局部變量,使用 local 命令來(lái)聲明。

查看 man bash 里面對(duì) local 命令的說(shuō)明如下:

local [option] [name[=value] ...]

For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declare.

When local is used within a function, it causes the variable name to have a visible scope restricted to that function and its children. With no operands, local writes a list of local variables to the standard output.

It is an error to use local when not within a function.

The return status is 0 unless local is used outside a function, an invalid name is supplied, or name is a readonly variable.

即,在函數(shù)內(nèi),使用 local 命令聲明的變量是局部變量,這些變量在函數(shù)外不可見(jiàn)。

在函數(shù)外,不能使用 local 命令聲明變量,否則會(huì)報(bào)錯(cuò)

注意:如上面說(shuō)明,local 命令本身會(huì)返回一個(gè)值,正常的返回值是 0。

假設(shè)有個(gè) testlocal.sh 腳本,內(nèi)容如下:

#!/bin/bash

foo() { return 1; }
bar() {
    ret=$(foo); echo first: $?
    local var=$(foo); echo second: $?
}
foobar() { return 0; }
bar
local out=$(foobar); echo third: $?

則執(zhí)行 ./testlocal.sh 腳本,會(huì)輸出:

first: 1
second: 0
./testlocal.sh: 第 x 行:local: 只能在函數(shù)中使用
third: 1

可以看到,ret=$(foo); 語(yǔ)句沒(méi)有使用 local 命令來(lái)定義 ret 變量,執(zhí)行 foo 函數(shù),該函數(shù)的返回值是 1,所以得到的 $? 是 1。

而 local var=$(foo); 語(yǔ)句使用 local 命令來(lái)定義 var 變量,執(zhí)行 foo 函數(shù),得到的 $? 卻是 0,而foo 函數(shù)明明返回的是 1。

原因就是該語(yǔ)句先通過(guò) $(foo) 執(zhí)行 foo 函數(shù),然后用 local var 來(lái)定義 var 變量為局部變量,所以該語(yǔ)句對(duì)應(yīng)的 $? 是 local 命令的返回值,而不是 foo 函數(shù)的返回值。

當(dāng)在函數(shù)外執(zhí)行 local 命令時(shí),它會(huì)報(bào)錯(cuò),可以看到雖然 foobar 函數(shù)返回是 0,但是第三個(gè) echo 語(yǔ)句打印的 $?是 1,正好是 local 命令執(zhí)行出錯(cuò)時(shí)的返回值。

即,對(duì)于 local var=$(func); 這種語(yǔ)句來(lái)說(shuō),它對(duì)應(yīng)的 $? 不是所調(diào)用函數(shù)的返回值,而是local 命令的返回值。

所以執(zhí)行函數(shù)后,想用 $? 來(lái)判斷函數(shù)的 return 返回值時(shí),注意不要用這種寫(xiě)法。

為了避免這種情況,最好在函數(shù)開(kāi)頭就用 local 命令聲明所有局部變量。

使用 local 聲明多個(gè)變量時(shí),變量之間用空格隔開(kāi),不要加逗號(hào)。

舉例說(shuō)明如下:

local a b c;

獲取傳入函數(shù)的所有參數(shù)

在 bash 中,可以使用 $1、$2、$3、...、$n 的方式來(lái)引用傳入函數(shù)的參數(shù),$1 對(duì)應(yīng)第一個(gè)參數(shù)值,$2 對(duì)應(yīng)第二個(gè)參數(shù)值,依次類(lèi)推。

如果 n 的值大于 9,那么需要用大括號(hào){}把 n 括起來(lái)。

例如,${10} 表示獲取第 10 個(gè)參數(shù)的值,寫(xiě)為 $10 獲取不到第 10 個(gè)參數(shù)的值。

實(shí)際上,$10 相當(dāng)于 ${1}0,也就是先獲取 $1 的值,后面再跟上 0,如果 $1 的值是 "first",則 $10 的值是 "first0"。

下面通過(guò)一個(gè) testparams.sh 腳本來(lái)舉例說(shuō)明,該腳本的內(nèi)容如下:

#!/bin/bash

function show_params()
{
    echo $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10 , ${10} , ${11}
}

show_params $@

這個(gè)腳本把傳入自身的參數(shù)傳給 show_params 函數(shù),該函數(shù)再打印出各個(gè)參數(shù),使用 $10、${10} 這兩個(gè)形式來(lái)說(shuō)明它們的區(qū)別。

執(zhí)行 ./testparams.sh 腳本的結(jié)果如下:

$ ./testparams.sh 1a 2b 3c 4d 5e 6f 7g 8h 9i 10j 11k
1a , 2b , 3c , 4d , 5e , 6f , 7g , 8h , 9i , 1a0 , 10j , 11k

可以看到,傳入的第 10 個(gè)參數(shù)值是 "10j",而 $10 打印出來(lái)的結(jié)果是 "1a0",也就是第一個(gè)參數(shù) "1a" 后面再跟上 0。

${10} 打印的結(jié)果才是第 10 個(gè)參數(shù)的值。

相應(yīng)地,${11} 也能正確打印第 11 個(gè)參數(shù)的值。

$1、$2 這種寫(xiě)法在 bash 文檔里面稱(chēng)之為 positional parameter,中文直譯過(guò)來(lái)是 “位置參數(shù)”。

查看 man bash 里面的說(shuō)明如下:

Positional Parameters

A positional parameter is a parameter denoted by one or more digits, other than the single digit 0. Positional parameters are assigned from the shell's arguments when it is invoked, and may be reassigned using the set builtin command.

Positional parameters may not be assigned to with assignment statements. The positional parameters are temporarily replaced when a shell function is executed.

When a positional parameter consisting of more than a single digit is expanded, it must be enclosed in braces.

${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 which is not to be interpreted as part of its name.

The parameter is a shell parameter or an array reference (Arrays).

可以看到,這里面提到了需要用大括號(hào){}把大于9的數(shù)字括起來(lái),{} 的作用是限定大括號(hào)里面的字符串是一個(gè)整體。

例如,有一個(gè) var 變量值是 "Test",現(xiàn)在想打印這個(gè)變量值,并跟著打印 "Hello" 字符串,也就是打印出來(lái) "TestHello" 字符串,那么獲取 var 變量值的語(yǔ)句和 "Hello" 字符串中間就不能有空格,否則 echo 命令會(huì)把這個(gè)空格一起打印出來(lái),但是寫(xiě)為 $varHello 達(dá)不到想要的效果。

具體舉例如下:

$ var="Test"
$ echo $var Hello
Test Hello
$ echo $varHello

$ echo ${var}Hello
TestHello

可以看到,$var Hello 這種寫(xiě)法打印出來(lái)的 "Test" 和 "Hello" 中間有空格,不是想要的結(jié)果。

而 $varHello 打印為空,這其實(shí)是獲取 varHello 變量的值,這個(gè)變量沒(méi)有定義過(guò),默認(rèn)值是空。

${var}Hello 打印出了想要的結(jié)果,用大括號(hào) {} 把 var 括起來(lái),明確指定要獲取的變量名是 var,避免混淆。

上面貼出的 testparams.sh 腳本代碼里面還用到了一個(gè) $@ 特殊參數(shù),它會(huì)擴(kuò)展為 positional parameter 自身的列表。

查看 man bash 的說(shuō)明如下:

Special Parameters

@ Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word.

That is, "$@" is equivalent to "$1" "$2" ...

注意:$@ 和 "$@" 這兩種寫(xiě)法得到的結(jié)果可能會(huì)有所不同。$@ 是擴(kuò)展為 $1 $2 ...,而 "$@" 是擴(kuò)展為 "$1" "$2" ...

修改上面的 testparams.sh 腳本來(lái)舉例說(shuō)明 $@ 和 "$@" 的區(qū)別:

#!/bin/bash

function show_params()
{
    echo $1 , $2 , $3
}

show_params $@
show_params "$@"

執(zhí)行 testparams.sh 腳本,輸出結(jié)果如下:

$ ./testparams.sh 1a 2b 3c
1a , 2b , 3c
1a , 2b , 3c
$ ./testparams.sh "1 a" "2 b" "3 c"
1 , a , 2
1 a , 2 b , 3 c
$ ./testparams.sh 1a 2b
1a , 2b ,
1a , 2b ,

可以看到,當(dāng)傳入腳本的參數(shù)值不帶空格時(shí),$@ 和 "$@" 得到的結(jié)果相同。

當(dāng)傳入腳本的參數(shù)值自身帶有空格時(shí),$@ 得到的參數(shù)個(gè)數(shù)會(huì)變多,"$@" 可以保持參數(shù)個(gè)數(shù)不變。

上面的 $1 是 "1 a",$@ 會(huì)拆分成 "1" "2" 兩個(gè)參數(shù),然后傳給 show_params 函數(shù);"$@" 會(huì)保持為 "1 a" 不變,然后傳給 show_params 函數(shù)。

即,"1 a" "2 b" "3 c" 這三個(gè)參數(shù),經(jīng)過(guò) $@ 處理后,得到的是 "1" "a" "2" "b" "3" "c" 六個(gè)參數(shù)。

經(jīng)過(guò) "$@" 處理后,得到的還是 "1 a" "2 b" "3 c" 三個(gè)參數(shù)。

同時(shí)也看到,即使只傳入兩個(gè)參數(shù),引用 $3 也不會(huì)報(bào)錯(cuò),只是會(huì)獲取為空。

分享到:
標(biāo)簽:bash function
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定