1. 程式人生 > >在 Shell 指令碼中呼叫另一個 Shell 指令碼的三種方式以及返回值問題

在 Shell 指令碼中呼叫另一個 Shell 指令碼的三種方式以及返回值問題

指令碼呼叫:

先來說一下主要以下有幾種方式:

  • fork: 如果指令碼有執行許可權的話,path/to/foo.sh。如果沒有,sh path/to/foo.sh。新開啟子shell,需要在父shell定義環境變數的變數子shell才可以使用可以繼承環境變數。在指令碼中定義環境 變數export a="111"​ 或 delcare -x a="aaaa"​​​​​​
  • execexec path/to/foo.sh 不開新shell使用當前shell,但是父shell的exec之後就不執行了。需要在父shell定義環境變數的變數子shell才可以使用。在指令碼中定義環境 變數export a="111"​ 或 delcare -x a="aaaa"​​​​​​
  • sourcesource path/to/foo.sh 不新開shell使用當前shell所以父shell的環境變數子shell都可以直接使用。

fork

fork 是最普通的, 就是直接在腳本里面用 path/to/foo.sh 來呼叫 
foo.sh 這個指令碼,比如如果是 foo.sh 在當前目錄下,就是 ./foo.sh。執行的時候 terminal 會新開一個子 Shell 執行指令碼 foo.sh,子 Shell 執行的時候, 父 Shell 還在。子 Shell 執行完畢後返回父 Shell。 子 Shell 從父 Shell 繼承環境變數,但是子 Shell 中的環境變數不會帶回父 Shell。

exec

exec 與 fork 不同,不需要新開一個子 Shell 來執行被呼叫的指令碼. 被呼叫的指令碼與父指令碼在同一個 Shell 內執行。但是使用 exec 呼叫一個新指令碼以後, 父指令碼中 exec 行之後的內容就不會再執行了。這是 exec 和 source 的區別.

source

與 fork 的區別是不新開一個子 Shell 來執行被呼叫的指令碼,而是在同一個 Shell 中執行. 所以被呼叫的指令碼中宣告的變數和環境變數, 都可以在主指令碼中進行獲取和使用。

其實從命名上可以感知到其中的細微區別,下面通過兩個指令碼來體會三種呼叫方式的不同:

第一個指令碼,我們命名為 1.sh

:

#!/usr/bin/env bash

A=1

echo "before exec/source/fork: PID for 1.sh = $$"

export A
echo "In 1.sh: variable A=$A"

case $1 in
        --exec)
                echo -e "==> using exec…\n"
                exec ./2.sh ;;
        --source)
                echo -e "==> using source…\n"
                . ./2.sh ;;
        *)
                echo -e "==> using fork by default…\n"
                ./2.sh ;;
esac

echo "after exec/source/fork: PID for 1.sh = $$"
echo -e "In 1.sh: variable A=$A\n"

第二個指令碼,我們命名為 2.sh

#!/usr/bin/env bash

echo "PID for 2.sh = $$"
echo "In 2.sh get variable A=$A from 1.sh"

A=2
export A

echo -e "In 2.sh: variable A=$A\n"

注:這兩個指令碼中的引數 $$ 用於返回指令碼的 PID , 也就是程序 ID。這個例子是想通過顯示 PID 判斷兩個指令碼是分開執行還是同一程序裡執行,也就是是否有新開子 Shell。當執行完指令碼 2.sh 後,指令碼 1.sh 後面的內容是否還執行。

chmod +x 1.sh 2.sh 給兩個指令碼加上可執行許可權後執行情況:

fork

fork

fork 方式可以看出,兩個指令碼都執行了,執行順序為1-2-1,從兩者的PID值(1.sh PID=82266, 2.sh PID=82267),可以看出,兩個指令碼是分成兩個程序執行的。

exec

exec

exec 方式執行的結果是,2.sh 執行完成後,不再回到 1.sh。執行順序為 1-2。從pid值看,兩者是在同一程序 PID=82287 中執行的。

source

source

source方式的結果是兩者在同一程序裡執行。該方式相當於把兩個指令碼先合併再執行。

Command Explanation
fork 新開一個子 Shell 執行,子 Shell 可以從父 Shell 繼承環境變數,但是子 Shell 中的環境變數不會帶回給父 Shell。
exec 在同一個 Shell 內執行,但是父指令碼中 exec 行之後的內容就不會再執行了
source 在同一個 Shell 中執行,在被呼叫的指令碼中宣告的變數和環境變數, 都可以在主指令碼中進行獲取和使用,相當於合併兩個指令碼在執行。

返回值:

Shell函式返回值,一般有3種方式:return,argv,echo

1) return 語句
shell函式的返回值,可以和其他語言的返回值一樣,通過return語句返回。
示例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

#!/bin/bash -

function mytest()

{

echo "arg1 = $1"

if [ $1 = "1" ] ;then

return 1

else

return 0

fi

}

echo

echo "mytest 1"

mytest 1

echo $?     # print return result

echo

echo "mytest 0"

mytest 0

echo $?     # print return result

echo

echo "mytest 2"

mytest 2

echo $?     # print return result

echo

echo "mytest 1 = "`mytest 1`

if mytest 1 ; then

echo "mytest 1"

fi

echo

echo "mytest 0 = "`mytest 0`

if mytest 0 ; then

echo "mytest 0"

fi

echo

echo "if fasle" # if 0 is error

if false; then

echo "mytest 0"

fi

echo

mytest 1

res=`echo $?`  # get return result

if [ $res = "1" ]; then

echo "mytest 1"

fi

echo

mytest 0

res=`echo $?`  # get return result

if [ $res = "0" ]; then

echo "mytest 0"

fi

echo

echo "end"

結果:
mytest 1
arg1 = 1
1

mytest 0
arg1 = 0
0

mytest 2
arg1 = 2
0

mytest 1 = arg1 = 1
arg1 = 1

mytest 0 = arg1 = 0
arg1 = 0
mytest 0

if fasle

arg1 = 1
mytest 1

arg1 = 0
mytest 0

end

先定義了一個函式mytest,根據它輸入的引數是否為1來return 1或者return 0.
獲取函式的返回值通過呼叫函式,或者最後執行的值獲得。
另外,可以直接用函式的返回值用作if的判斷。
注意:return只能用來返回整數值,且和c的區別是返回為正確,其他的值為錯誤。

2) argv全域性變數
這種就類似於C語言中的全域性變數(或環境變數)。
示例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#!/bin/bash -

g_var=

function mytest2()

{

echo "mytest2"

echo "args $1"

g_var=$1

return 0

}

mytest2 1

echo "return $?"

echo

echo "g_var=$g_var"

結果:
mytest2
args 1
return 0

g_var=1

函式mytest2通過修改全域性變數的值,來返回結果。

注: 以上兩個方法失效的時候
以上介紹的這兩種方法在一般情況下都是好使的,但也有例外。
示例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

#!/bin/bash -

function mytest3()

{

grep "123" test.txt | awk -F: '{print $2}' | while read line ;do

echo "$line"

if [ $line = "yxb" ]; then

return # return to pipe only

fi

done

echo "mytest3 here "

return 1      # return to main process

}

g_var=

function mytest4()

{

grep "123" test.txt | awk -F: '{print $2}' | while read line ;do

echo "$line"

if [ $line = "yxb" ]; then

g_var=0

echo "g_var=0"

return # return to pipe only

fi

done

echo "mytest4 here "

return 1

}

mytest3

echo $?

echo

mytest4

echo $?

echo

echo "g_var=$g_var"

其中,test.txt 檔案中的內容如下:
456:kkk
123:yxb
123:test
結果:
yxb
mytest3 here 
1

yxb
g_var=0
mytest4 here 
1

g_var=
可以看到mytest3在return了以後其實沒有直接返回,而是執行了迴圈體後的語句,同時看到mytest4中也是一樣,同時,在mytest4中,對全域性變數的修改也無濟於事,全域性變數的值根本就沒有改變。這個是什麼原因那?
筆者認為,之所以return語句沒有直接返回,是因為return語句是在管道中執行的,管道其實是另一個子程序,而return只是從子程序中返回而已,只是while語句結束了。而函式體之後的語句會繼續執行。
同理,全域性變數在子程序中進行了修改,但是子程序的修改沒有辦法反應到父程序中,全域性變數只是作為一個環境變數傳入子程序,子程序修改自己的環境變數,不會影響到父程序。
因此在寫shell函式的時候,用到管道(cmd &後臺程序也一樣)的時候一定要清楚此刻是從什麼地方返回。

3) echo 返回值
其實在shell中,函式的返回值有一個非常安全的返回方式,即通過輸出到標準輸出返回。因為子程序會繼承父程序的標準輸出,因此,子程序的輸出也就直接反應到父程序。因此不存在上面提到的由於管道導致返回值失效的情況。
在外邊只需要獲取函式的返回值即可。
示例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

#!/bin/bash

##############################################

# Author : IT-Homer

# Date  : 2012-09-06

##############################################

function mytest5()

{

grep "123" test.txt | awk -F: '{print $2}' | while read line; do

if [ $line = "yxb" ]; then

echo "0"  # value returned first by this function

return 0

fi

done

return 1

}

echo '$? = '"$?"

result=$(mytest5)

echo "result = $result"

echo

if [ -z $result ]    # string is null

then

echo "no yxb. result is empyt"

else

echo "have yxb, result is $result"

fi

結果:
$? = 0
result = 0

have yxb, result is 0
這個方式雖然好使,但是有一點一定要注意,不能向標準輸出一些不是結果的東西,比如除錯資訊,這些資訊可以重定向到一個檔案中解決,特別要注意的是,用到比如grep這樣的命令的時候,一定要記得1>/dev/null 2>&1來避免這些命令的輸出。

您可能感興趣的文章:

相關推薦

Shell 指令碼呼叫一個 Shell 指令碼方式

先來說一下主要以下有幾種方式: fork: 如果指令碼有執行許可權的話,path/to/foo.sh。如果沒有,sh path/to/foo.sh。 exec: exec path/to/foo.sh source: source path/to/foo.s

Shell 指令碼呼叫一個 Shell 指令碼方式以及返回問題

指令碼呼叫: 先來說一下主要以下有幾種方式: fork: 如果指令碼有執行許可權的話,path/to/foo.sh。如果沒有,sh path/to/foo.sh。新開啟子shell,需要在父shell定義環境變數的變數子shell才可以使用可以繼承環境變數。在指令碼中

Linux shell指令碼呼叫一個shell(exec、source、fork)

        最近在研究yarn的原始碼,在看到YarnChild的啟動指令碼時,看到啟動的時候用到了shell中得exec命令,比較好奇為什麼使用exec,網上找了一下這個命令和類似命令的使用方法,原文地址:http://qujunorz.blog.51cto

shell指令碼呼叫一個指令碼不同方法

1、fork (fork /directory/script.sh) fork是最普通的, 就是直接在腳本里面用/directory/script.sh來呼叫script.sh這個指令碼. 執行的時候開一個sub-shell執行呼叫的指令碼,sub-she

shell指令碼呼叫一個指令碼不同方法(fork, exec, source)

fork fork(fork /directory/script.sh)如果shell中包含執行命令,那麼子命令並不影響父級的命令,在子命令執行完後再執行父級命令。子級的環境變數不會影響到父級。 fork是最普通的, 就是直接在腳本里面用/directory

shell指令碼呼叫一個指令碼不同方法(fork, exec, source)

fork ( /directory/script.sh) fork是最普通的, 就是直接在腳本里面用/directory/script.sh來呼叫script.sh這個指令碼. 執行的時候開一個sub-shell執行呼叫的指令碼,sub-shell執行的時候, pa

python指令碼啟動一個python指令碼

         有時間,我們需要在一個python指令碼中調起另一個python指令碼,下面我嘗試了一下,          1.父指令碼如下: #coding:utf8 #!/usr/local/bin/python import time import datet

Shell指令碼呼叫另外一個指令碼的方法

       在Linux平臺上開發,經常會在console(控制檯)上執行另外一個指令碼檔案,經常用的方法有:./my.sh 或 source my.sh 或 . my.sh;這三種方法有什麼不同呢?我們先來了解一下在一個shell指令碼中如何呼叫另外一個shell指令

在python程式呼叫一個py檔案

在同一個資料夾下 呼叫函式: A.py檔案:     def add(x,y): print('和為:%d'%(x+y)) B.py檔案:     import A A.add

python檔案呼叫一個python檔案的類

如果是在同一個 module中(也就是同一個py 檔案裡),直接用就可以 如果在不同的module裡,例如 a.py裡有 class A: b.py 裡有 class B: 如果你要在class B裡用class A 需要在 b.py的開頭寫上 from a import A  

安卓的資料共享——從一個APP呼叫一個APP資料的方法

在Android中如何在一個APP中呼叫另一個APP中的資料呢?大致有以下五種方法可以實現 1、首選項資訊-Shared Preferences 2、檔案 3、SQLite 4、Content Provider 5、廣播 下來對上述的五種方法進行詳細的解析: 1、首先對

如何使用spring容器在一個呼叫一個類的方法。

學了spring容器之後,最基本的就是這個在一個類中呼叫另一個類中的方法了。本文講述的就是如何使用spring容器從一個類呼叫另一個類的方法。首先,我們先新建一個Student類,在類中建立一個int型別的方法。如圖: 第一個類Student就完成了,那麼我們

python 在一個py檔案呼叫一個資料夾下py檔案模組

假設現在的資料夾結構如下: -- src     |-- dir1     |    -- file1.py     |-- dir2     | 

在同一個類呼叫一個方法沒有觸發 Spring AOP 的問題

起因 考慮如下一個例子: @Target(value = {ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyMonitor { } @Component @Aspect pub

js檔案呼叫一個js檔案的函式

假設條件是:JS(A)要呼叫JS(B)的函式.那麼要滿足以下條件:1.要保證你所呼叫的JS必須在同一個頁面裡. 也就是JS(A)和JS(B)都要在頁面X裡.2.要保證你所呼叫的JS先於呼叫者本身被解釋.也就是JS(B)要先於JS(A)被解釋.反映在頁面上,就是JS(B)要寫

python ,如何在一個函式呼叫一個函式返回的多個一個

在一個函式中呼叫另一個函式返回的多個值中的一個 首先,定義了一個函式 。 程式碼如下: def createDataSet(): group = array([[1.0,1.1],[1

C++學習筆記-----在一個建構函式呼叫一個建構函式

在建構函式中呼叫另一個建構函式如果一不注意就會有無限遞迴的危險,而且建構函式的引數的不同也決定了呼叫的不同,所以特意總結了一下哪些形式的呼叫時允許的: 首先假設建構函式呼叫時引數都是外部的變數,而不是一個常量。 第一種,也是C++11支援的一種呼叫方式。在函式的初始化列表中

Java一個方法呼叫一個方法

在同一個類中: 對於靜態方法,其他的靜態或非靜態方法都可以直接呼叫它。 而對於非靜態方法,其他的非靜態方法是可以直接呼叫它的。但是其他靜態方法只有通過物件才能呼叫它。 靜態方法不能被非靜態方法覆蓋。 public class Test2 { public static v

Asp.net mvc 動作方法 呼叫 一個控制器的動作方法

public ActionResult 動作方法() { var otherController = DependencyResolver.Current.GetService<另一個控制

以程式設計的方式一個按鈕的Click事件呼叫一個按鈕的Click事件

之前就遇到了一次這樣的問題,我想在當前的按鈕的Click事件中去呼叫另一個按鈕的Click事件,當時沒成功,今天又遇到了,經過好心人的熱心幫忙,現在解決了,所以記錄下來,雖然不是很明白這是為什麼,不過功能是實現了: private void btnupdat