/

Bash Shell 腳本編寫入門

Bash Shell 腳本編寫入門

這是關於編寫Bash Shell腳本的詳細概述。

腳本編寫是一種自動化在計算機上定期執行的任務的強大方式。

在本教程中,將詳細介紹腳本編寫,並將成為進一步深入和高級教程創建實際腳本的基本參考。

查看我的Bash介紹文章。

Bash提供了一組命令,這些命令組合在一起可以用來創建小程序,按照慣例我們稱之為腳本。

請注意區別。我們不說Bash編程,而說Bash腳本,我們也不稱Bash腳本為”Bash程序”。這是因為在您的腳本變得失控之前,通常可以達到一定的複雜性。

但是Bash腳本非常棒,因為您執行它們時不需要任何其他東西,只需要Bash本身。沒有編譯器,沒有解釋器,只需要您的shell。

與Perl、JavaScript或Python等編程語言相比,您會錯過很多東西。

變量沒有作用域,它們全部是全局的,沒有標准庫,也沒有模塊系統,例如。但優點也非常大:您可以非常輕鬆地像在shell中那樣調用任何CLI工具,且Unix的許多小型實用程序命令的方法確實使腳本編寫能力出眾。使用wget執行網絡請求,使用awk處理文本等等。

Shell腳本是您最好了解的工具之一,至少知道如何在看到它時讀取程序以及它能為您的日常工作帶來的好處。

本教程將指導您了解Bash腳本的理論和概念。將來我將發布更詳細的教程,介紹特定技術或如何解決特定問題。

基礎知識

腳本存儲在文件中。您可以給shell腳本任何名稱和擴展名,這無關緊要。重要的是它必須在第一行上以”shebang”開始:

1
#!/bin/bash

並且它必須是一個可執行文件。

使用chmod這個命令使文件變為可執行:

1
chmod u+x myscript

myscript文件設置為您的用戶可執行(我將不會深入探討權限問題,但我將很快介紹它們)。

現在,如果您在同一文件夾中,可以通過調用./myscript或使用其完整路徑來執行腳本。

在學習時,我建議您,如果可能的話,使用在線環境進行測試,像這樣的https://rextester.com/l/bash_online_compiler

註釋

註釋是編寫程序時最重要的事情之一。以“#”符號開頭的行是註釋(除了上面這裡看到的shebang行)。

例如:

1
2
#!/bin/bash
# this is a comment

註釋也可以在行末開始:

1
2
#!/bin/bash
echo ok # this is a comment

變量

您可以使用“=”運算符設置變量:

1
name=value

示例:

1
2
3
ROOM_NUMBER=237
name=Flavio
nickname="Flavio"

您可以使用 echo 內建命令打印變量並在變量名前加上 $

1
2
echo $ROOM_NUMBER
echo $name

運算符

Bash實現了一些在編程語言中常用的算術運算符:

  • +
  • -
  • *
  • /
  • % 取餘數
  • ** 指數

您可以使用以下方式進行比較:

  • < 小於
  • <= 小於等於
  • == 等於
  • >= 大於等於
  • > 大於

您還可以使用這些比較運算符:

  • -lt 小於
  • -gt 大於
  • -le 小於等於
  • -ge 大於等於
  • -eq 等於
  • -ne 不等於

例如:

1
2
3
4
5
6
7
#!/bin/bash
age=23
minimum=18
if test $age -lt $minimum
then
echo "Not old enough"
fi

邏輯運算符:

  • && 邏輯“且”
  • || 邏輯“或”

這些快捷方式允許執行算術操作,然後進行分配:

  • +=
  • -=
  • *=
  • /=
  • %=

還有一些其他運算符,但這些是最常見的。

輸出到屏幕

您可以使用 echo 命令將任何內容輸出到屏幕:

1
2
3
4
#!/bin/bash
echo "test"
echo test
echo testing something

邏輯條件

AND:如果commandanothercommand都執行並返回0,則求值為0(零)。在shell命令中,返回零表示命令成功執行。非零返回值表示錯誤消息。

1
command && anothercommand

OR:如果commandanothercommand中至少有一個執行並返回0,則求值為0(零)。

1
command || anothercommand

NOT:反轉command邏輯返回值:

1
! command

控制結構

您可以使用幾個您可能熟悉的控制結構:

if/else語句

簡單if

1
2
3
4
if condition
then
command
fi

ifelse

1
2
3
4
5
6
if condition
then
command
else
anothercommand
fi

嵌套的if - else

1
2
3
4
5
6
7
8
if condition
then
command
elif
anothercommand
else
yetanothercommand
fi

使用分號將else放在同一行上可以達到與上面示例相同的效果:

1
2
3
if condition ; then
command
fi

示例:

1
2
3
4
5
6
7
#!/bin/bash
DOGNAME=Roger
if [ "$DOGNAME" == "" ]; then
echo "Not a valid dog name!"
else
echo "Your dog name is $DOGNAME"
fi

請注意括號?在進行任何類型的布爾表達式評估時,我們必須添加括號。

循環

while循環

condition成立時,運行command

1
2
3
4
while condition
do
command
done

until

直到condition成立時,運行command

1
2
3
4
until condition
do
command
done

for循環

遍歷列表,對每個項目運行一個命令

1
2
3
4
for item in list
do
command
done

跳出和繼續

在循環內部,您可以使用breakcontinue語句完全退出循環,或者只是跳過當前迭代。

Case

case控制結構根據值選擇不同的路徑。

1
2
3
4
5
6
7
8
9
10
case value in
a)
command
#...
;;
b)
command
#...
;;
esac

fi一樣,esac結束case結構,注意它的拼寫是反過來的。

我們在每個case後面添加雙分號。

*)用於處理未明確表示的所有情況。

使用|符號可以表示同一個case的多個選項。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
read -p "How many shoes do you have?" value
case $value in
0|1)
echo "Not enough shoes! You can't walk"
;;
2)
echo "Awesome! Go walk!"
#...
;;
*)
echo "You got more shoes than you need"
#...
;;
esac

Select

select控制結構向用戶顯示可選菜單:

1
2
3
4
select item in list
do
command
done

示例:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
select breed in husky setter "border collie" chiwawa STOP
do
if [ "$breed" == "" ]; then
echo Please choose one;
else
break
fi
done

echo "You chose $breed"

條件測試

在上面的控制結構中,我使用condition這個詞。

您可以使用test Bash內建命令檢查條件並返回true(0)或false(非0)的值。

下面是一個示例:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
if test "apples" == "apples"
then
echo Apples are apples
fi

if ! test "apples" == "oranges"
then
echo Apples are not oranges
fi

從命令行讀取輸入

您可以使用read內建命令使腳本變得互動。該命令從標準輸入讀取一行,並且可以以非常靈活的方式格式化輸入。

最簡單的用法是:

1
2
echo "Age:"
read age

這會打印“Age:”,然後您可以輸入一個數字,按Enter鍵,該數字將被賦值給age變量。

read-p選項提供了內建的提示符,並將輸入放入age變量中:

1
read -p "Age: " age

read命令還有更多選項,可以通過在Bash中輸入help read來檢查。

添加選項

選項使用連字符和字母指定,例如:

1
2
ls -a
driveCar -b "Ford"

在代碼中,您使用內建命令getopts來解析選項並獲取值。

如果我們接受選項ab,我們將getopts ab提供給while循環。

如果選項b接受值,則在之後添加冒號,所以我們的getopts調用的格式是這樣的:getopts ab: arg

以下是代碼片段:

1
2
3
4
5
6
7
while getopts xy: arg
do
case $arg in
x) echo "Option $arg enabled" ;;
y) echo "The value of $arg is $OPTARG" ;;
esac
done

在此腳本中,$OPTARG自動分配為選項字母。我們告訴getopts我們接受哪些選項,並分別處理每種情況。

getopts會自動處理錯誤,我們可以選擇自己處理錯誤消息,只需在參數定義之前加冒號:xy:getopts :xy: arg

然後,我們還需要處理\?:情況。當我們傳入無效選項時,將調用第一個,而當我們錯過選項時,將調用第二個:

1
2
3
4
5
6
7
8
9
10
while getopts :xy: arg
do
case $arg in
x) echo "Option $arg enabled" ;;
y) echo "The value of $arg is $OPTARG" ;;
:) echo "$0: Must supply an argument to -$OPTARG." >&2
exit 1 ;;
\?) echo "Invalid option -$OPTARG ignored." >&2 ;;
esac
done

現在,如果您忘記添加參數,錯誤消息將不同。

處理參數

在腳本內,您可以訪問在調用時傳遞的任何參數。您使用$0$1$2等來訪問它們,具體取決於位置,其中$0是命令的名稱,並且增加數字則是參數位置的增加。在位置9之後,您需要使用大括號:${10}${11}……

例如,運行此startCar腳本:

1
2
3
#!/bin/bash
echo $0
echo $1

作為./startCar fiesta運行將打印:

1
2
./startCar
fiesta

特殊的$*語法打印所有參數,$#是傳遞的參數數量:

1
2
#!/bin/bash
echo $# $\*
1
2
./test.sh Milan Florence Rome
3 Milan Florence Rome

分離選項和參數

如果您既有選項又有參數,您需要將它們分開。您可以使用連字符:

1
driveCar -m "Ford Fiesta" - Milan Florence Rome

使用“getopts”內置命令解析選項並獲取值。

功能

就像在JavaScript或其他任何編程語言中一樣,您可以創建可重複使用的小代碼塊,給它們命名,在需要時調用它們。

Bash稱之為函數

您使用以下方式定義一個函數:

1
2
3
function name {

}

示例:

1
2
3
function cleanFolder {

}

並且您可以像這樣調用它:

1
cleanFolder

您可以將參數傳遞給文件夾,而不需要聲明它們 - 只需像腳本參數一樣引用它們:$1$2等。例如:

1
2
3
4
function cleanFolder {
echo "Clean folder $1"
}
cleanFolder "/Users/flavio/Desktop"