揭祕 shell 中的那些終極用法

一、“&&” 規則執行命令

一般情況下, 成果物只有一個 (各模塊). 因此開啓服務只需簡單的 ./ 成果物名字 (或加上 &); 但是很多情況下, 我們都會爲自身的模塊服務提供一個看門狗進程, 即當服務出現意外而掛掉時候, 希望看門狗進程能夠立即將服務拉起來, 而不是讓服務處於癱瘓狀態. 這時候服務啓動時候, 則涉及兩個進程. 而且這兩個進程的順序是:

看門狗進程啓動 ok, 則繼續啓動模塊進程服務; 反之, 則啓動服務失敗. 如下圖所示:

如下示例 1, 分別有兩個進程 hello(打印 helo . . .) 和 world(打印 world . . .).

///// hello.c文件內容  gcc hello.c -o hello   (進程名hello)
#include <stdio.h>
#include <stdlib.h>
int main()
{
        printf("hello ...\n");
        return 0;
}
///// world.c文件內容  gcc world.c -o world  (進程名world)
#include <stdio.h>
#include <stdlib.h>
int main()
{
        printf("world ...\n");
        return 0;
}

若不使用 “&&”, 那麼其 shell 腳本應該如下. 若 hello 進程啓動成功, 則繼續啓動 world 進程, 反之, 則結束。

#!/bin/sh
./hello
if [ $? -eq 0 ]
then
    ./world
fi

1.2 命令 1 && 命令 2

現在修改爲使用 “&&” 命令來替換 if. . . else 規則. 如下:

#!/bin/sh
# 如果./hello命令啓動成功(返回真, 即$? = 0), 則繼續啓動 ./world進程; 若失敗, 則結束.
./hello && ./world

爲了測試 “&&” 的使用規則, 現在啓動一個不存在的進程 hello_1. 觀察其結果:

#!/bin/sh
./hello_1 && ./world

因爲不存在 hello_1 進程, 因此直接報錯, hello 和 world 都沒有啓動和打印. 報錯提示如下:

start.sh: line 2: ./hello_1: No such file or directory

二、“||” 規則執行命令

2.1 命令 1 || 命令 2

對於 Shell 環境中 “||” 命令的功能. 若命令 1 執行失敗, 不會退出, 轉而執行命令 2. 若命令 1 執行成功, 則不會執行命令 2。

先來一個命令 1 執行成功的示例.

///// start.sh 腳本
#!/bin/sh
./hello || ./world

打印結果:

hello . . .

現在修改 start.sh 腳本. 其中命令 1 不存在, 然後執行:

///// start.sh 腳本
#!/bin/sh
# hello_1成果物是不存在的. 所以hello_1執行失敗. 觀察world執行情況.
./hello_1 || ./world

打印結果:
start.sh: line 2: ./hello_1: No such file or directory
world . . .

命令 1 執行失敗並報錯. 但並不影響腳本的繼續執行. world 進程仍然執行並打印數據了。

三、進程列表

對於 shell, 可以在一行中指定要依次執行的一系列命令. 各命令之間用分號 (";") 隔開; 這些命令依次執行. 如下示例:

lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ ls
1.sh  hello  hello.c  start.sh  world  world.c
# 使用pwd;ls兩個組合命令來查看當前位置和文件列表.
lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ pwd;ls
/data1/lixiaogang5/work/test/06_29
1.sh  hello  hello.c  start.sh  world  world.c

所謂進程列表, 即命令分組. 若只是簡單的將一個或多個命令使用分號隔開寫在一行中, 並不會成爲進程列表. 想要成爲進程列表, 有兩種方式, 分別是: 使用圓括號 () 或 使用花括號 {} 來將命令放入其中. 並在每個命令的末尾加上分號 (";"). 語法格式如下:

    /* 使用花括號方式 */

     {commond 1; commond 2; commond 3}  

    /* 使用圓括號方式 */  
     (commond 1; commond 2; commond 3}) 

儘管使用圓括號和花括號方式都可以達到進程列表的目的與效果. 但是兩者是有區別的. 圓括號方式會生成一個子 shell 來執行對應的命令; 而使用花括號方式進行命令分組時候, 並不創建子 shell。

3.1 圓括號方式產生子 shell 進程

爲了驗證是否生產子 shell, 可以使用環境變量 BASH_SUBSHELL. 若返回 0, 則表示沒有創建子 shell; 反之, 若返回 1 或是其他數字, 則表示創建了子 shell 進程。

lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ (pwd; ls; echo $BASH_SUBSHELL)
/data1/lixiaogang5/work/test/06_29
1.sh  hello  hello.c  start.sh  world  world.c
1  //echo $BASH_SUBSHELL 返回結果大於0, 表示創建了子shell進程環境.

3.2 花括號方式不產生子 shell 進程

使用花括號方式來創建進程列表, 如下:

lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ {pwd; ls; echo $BASH_SUBSHELL}
/data1/lixiaogang5/work/test/06_29
1.sh  hello  hello.c  start.sh  world  world.c
0  //使用花括號方式, echo $BASH_SUBSHELL 返回結果爲0, 表示沒有創建子shell.

圓括號或花括號創建進程列表時候, 內部還可以嵌套圓括號或花括號. 如下:

lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ (pwd; ls; (echo $BASH_SUBSHELL))
/data1/lixiaogang5/work/test/06_29
1.sh  hello  hello.c  start.sh  world  world.c
2   //這個時候環境變量BASH_SUBSHELL爲2, 表示創建了2個子shell環境》

四、進程列表 ()、{}、搭配 &&、|| 一起使用

很多時候, 對於一些比較耗時的操作, 可以另起一個子 shell 環境並在其中去執行. 模擬多進程的場景效果. 如下所示:

lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ ./hello && ./world || (echo "execute failed.")
hello ...
world ...
# 執行失敗範例
lixiaogang5@Cpl-Backend-General-14-115:~/work/test/06_29$ ./hello_1 && ./world || (echo "execute failed.")
bash: ./hello_1: No such file or directory
execute failed.
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/BWdoRC0oi0giBWAWh9gL2Q