1 Bash 和 Bash 指令碼 #
當今時代,許多人都在使用裝有 GNOME 之類圖形使用者介面 (GUI) 的電腦。儘管 GUI 可提供許多功能,但執行自動化任務時,這些功能會受到限制。外圍程序是對 GUI 的有效補充,本章將會概述外圍程序 (以 Bash 外圍程序為例) 的數個方面。
1.1 什麼是「外圍程序」? #
一般而言,Linux 外圍程序就是 Bash (Bourne again Shell)。本章中提及的「外圍程序」指的是 Bash。可用的外圍程序不止 Bash (還有 ash、csh、ksh、zsh 等等),每個外圍程序都具有不同的功能和特性。如需有關其他外圍程序的詳細資訊,請在 YaST 中搜尋外圍程序。
1.1.1 Bash 組態檔案 #
外圍程序可啟用為:
互動式登入外圍程序: 使用
--login
選項呼叫 Bash 以登入機器時,或透過 SSH 登入遠端機器時,需要使用此方式。「一般」互動式外圍程序: 在啟動 xterm、konsole、gnome-terminal 或類似的指令列介面 (CLI) 工具時,通常會呼叫此外圍程序。
非互動式外圍程序: 當在指令列呼叫外圍程序指令碼時,就會呼叫此外圍程序。
各個外圍程序會讀取不同的組態檔案。下面的表格顯示了登入與非登入外圍程序組態檔案。
Bash 會根據執行組態檔案的外圍程序類型,依特定順序尋找其組態檔案。如需詳細資訊,請參閱 Bash 手冊頁 (man 1 bash
)。搜尋標題 INVOCATION
。
檔案 |
描述 |
---|---|
|
請勿修改此檔案,否則下次更新時,您的修改可能會被損毀。 |
|
如果要擴充 |
|
包含特定程式的泛系統組態檔案 |
|
在此處插入登入外圍程序的使用者特定組態 |
登入外圍程序還會獲取表格 1.2 「非登入外圍程序的 Bash 組態檔案」中所列的組態檔案。
|
請勿修改此檔案,否則下次更新時,您的修改可能會被損毀。 |
|
使用此檔案只能插入 Bash 的泛系統修改 |
|
在此處插入使用者特定的組態 |
Bash 還會使用其他多個檔案:
檔案 |
描述 |
---|---|
|
包含您輸入的所有指令清單 |
|
登出時執行 |
|
使用者為常用指令定義的別名。如需定義別名的更多詳細資料,請參閱 |
非登入外圍程序#
下面兩個特殊外圍程序會阻擋使用者登入系統:/bin/false
和 /sbin/nologin
。當使用者嘗試登入系統時,這兩個外圍程序都將失敗且不會顯示訊息。這是一種針對系統使用者而特意設計的安全措施,雖然新版 Linux 作業系統已提供更有效的工具 (例如 PAM 和 AppArmor) 來控制系統存取。
SUSE Linux Enterprise Desktop 上的預設行為是將 /bin/bash
指定給人類使用者,將 /bin/false
或 /sbin/nologin
指定給系統使用者。但由於歷史原因,nobody
使用者擁有 /bin/bash
,因為該使用者過去是預設用做系統使用者的最低權限使用者。但是,如果有多個系統使用者使用 nobody
,則將失去因使用 nobody 所獲得的任何安全性。應該可以將它變更為 /sbin/nologin
;最快的測試方法是進行此變更,然後看看這樣是否中斷了任何服務或應用程式。
使用以下指令列出 /etc/passwd
中已指定給所有使用者 (系統使用者和人類使用者) 的外圍程序。輸出視您系統上的服務和使用者而有所不同:
>
sort -t: -k 7 /etc/passwd | awk -F: '{print $1"\t" $7}' | column -t
tux /bin/bash
nobody /bin/bash
root /bin/bash
avahi /bin/false
chrony /bin/false
dhcpd /bin/false
dnsmasq /bin/false
ftpsecure /bin/false
lightdm /bin/false
mysql /bin/false
postfix /bin/false
rtkit /bin/false
sshd /bin/false
tftp /bin/false
unbound /bin/false
bin /sbin/nologin
daemon /sbin/nologin
ftp /sbin/nologin
lp /sbin/nologin
mail /sbin/nologin
man /sbin/nologin
nscd /sbin/nologin
polkitd /sbin/nologin
pulse /sbin/nologin
qemu /sbin/nologin
radvd /sbin/nologin
rpc /sbin/nologin
statd /sbin/nologin
svn /sbin/nologin
systemd-coredump /sbin/nologin
systemd-network /sbin/nologin
systemd-timesync /sbin/nologin
usbmux /sbin/nologin
vnc /sbin/nologin
wwwrun /sbin/nologin
messagebus /usr/bin/false
scard /usr/sbin/nologin
1.1.2 目錄結構 #
下表概述了 Linux 系統中最重要的較高層級目錄。下列清單中提供了關於目錄與重要子目錄的更多詳細資訊。
目錄 |
目錄 |
---|---|
|
根目錄 — 目錄樹狀結構的起點。 |
|
基本的二進位檔案,例如系統管理員與一般使用者都需要使用的指令。通常還包含 Bash 等外圍程序。 |
|
開機載入程式的靜態檔案。 |
|
存取主機特定裝置所需的檔案。 |
|
主機特定系統的組態檔案。 |
|
存放系統中所有擁有帳戶之使用者的主目錄。但是, |
|
基本的共享程式庫與核心模組。 |
|
抽取式媒體的定點。 |
|
用於暫時掛接檔案系統的定點。 |
|
附加應用程式軟體套件。 |
|
超級使用者 |
|
基本的系統二進位檔案。 |
|
系統所提供之服務的資料。 |
|
暫存檔案。 |
|
包含唯讀資料的次要階層。 |
|
可變資料,例如記錄檔案。 |
|
僅當系統中同時安裝了 Microsoft Windows* 與 Linux 才可以使用。包含 Windows 資料。 |
以下清單提供了更多詳細資訊,以及目錄中包含的檔案與子目錄的一些範例:
/bin
包含
root
和其他使用者都可使用的基本外圍程序指令。這些指令包括ls
、mkdir
、cp
、mv
、rm
和rmdir
。/bin
還包含 Bash,後者是 SUSE Linux Enterprise Desktop 中的預設外圍程序。/boot
包含開機所需的資料,例如開機載入程式、核心及核心開始執行使用者模式程式之前所使用的其他資料。
/dev
存放代表硬體元件的裝置檔案。
/etc
包含控制 X Window System 等程式的操作的本地組態檔案。
/etc/init.d
子目錄包含可在開機過程中執行的 LSB init 指令碼。/home/USERNAME
存放系統中每個擁有帳戶之使用者的個人資料。只有檔案擁有者或系統管理員才能修改位於此處的檔案。依預設,電子郵件目錄和個人桌面組態以隱藏檔案和目錄的形式存放在此處,例如
.gconf/
和.config
。注意:網路環境中的主目錄如果您是在網路環境中工作,您的使用者主目錄可能會對應至檔案系統中除
/home
以外的其他目錄。/lib
包含啟動系統及執行根檔案系統中指令所需的基本共享程式庫。在 Windows 中,對應的共用程式庫為 DLL 檔案。
/media
包含 CD-ROM、隨身碟及數位相機 (若使用 USB) 等抽取式媒體的掛接點。
/media
通常包含除系統硬碟之外的各類磁碟機。抽取式媒體插入或連接到系統並掛接後,您便可從此處存取該媒體。/mnt
此目錄提供了暫時掛接之檔案系統的定點。
root
可在此處掛接檔案系統。/opt
為安裝協力廠商軟體而保留。這裡有選擇性軟體與大型附加程式套件。
/root
root
使用者的主目錄。此處存放root
的個人資料。/run
systemd
和各個元件使用的 tmpfs 目錄。/var/run
是指向/run
的符號連結。/sbin
如
s
所指示,此目錄存放適用於超級使用者的公用程式。/sbin
不僅包含/bin
中的二進位檔案,還包含系統開機、還原和復原系統所需的其他二進位檔案。/srv
存放系統所提供之服務的資料,例如 FTP 與 HTTP。
/tmp
需要檔案暫時儲存區的程式會使用此目錄。
重要:開機時清理/tmp
無法保證儲存於
/tmp
中的資料在系統重新開機後仍然存在。這取決於/etc/tmpfiles.d/tmp.conf
中的設定及其他因素。/usr
/usr
與使用者無關,是 Unix 系統資源 (Unix system resource) 的縮寫。/usr
中的資料是靜態的唯讀資料,可以在符合Filesystem Hierarchy Standard
(FHS) 的各個主機之間共用。此目錄包含所有應用程式 (包括 GNOME 之類的圖形桌面),並且會在檔案系統中建立次要階層。/usr
包含/usr/bin
、/usr/sbin
、/usr/local
和/usr/share/doc
等多個子目錄。/usr/bin
包含一般情況下可存取的程式。
/usr/sbin
包含為系統管理員保留的程式,例如修復功能。
/usr/local
在此目錄中,系統管理員可安裝獨立於套裝作業系統的本地延伸。
/usr/share/doc
存放系統的各種文件檔案與版本說明。在
manual
子目錄中,可找到此手冊的線上版本。如果安裝了多種語言,此目錄可能會包含不同語言的手冊版本。在
packages
內,可找到系統上安裝之軟體套件所包含的文件。對於每個套件,都會建立一個子目錄/usr/share/doc/packages/PACKAGENAME
,通常使用它來存放相應套件的讀我檔案,有時還會存放範例、組態檔案或其他指令碼。如果系統上安裝了 HOWTO,
/usr/share/doc
還會包含howto
子目錄,其中提供了關於眾多 Linux 軟體安裝和操作任務的其他文件。/var
/usr
用於儲存靜態唯讀資料,而/var
用於儲存系統運作期間寫入的可變資料,例如記錄檔案或多工緩衝處理資料。如需大致瞭解/var/log/
中包含的最重要記錄檔案,請參閱表格 42.1 「記錄檔」。/windows
僅當系統中同時安裝了 Microsoft Windows 與 Linux 才可以使用。包含可在系統的 Windows 分割區上使用的 Windows 資料。您是否可以編輯此目錄中的資料取決於 Windows 分割區使用的檔案系統。如果是 FAT32,則您可以開啟並編輯此目錄中的檔案。對於 NTFS,SUSE Linux Enterprise Desktop 還包含寫入存取權限支援。但是,適用於 NTFS-3g 檔案系統的驅動程式所提供的功能有限
1.2 撰寫外圍程序指令碼 #
使用外圍程序指令碼可以方便地完成各種任務:收集資料、搜尋文字中的單字或片語,以及執行其他有用的操作。以下範例顯示了一個列印文字的小型外圍程序指令碼:
在執行此指令碼之前,必須滿足幾項先決條件:
每個指令碼都應包含 Shebang 行 (如上面的範例所示)。如果缺少該行,您需要手動呼叫直譯器。
您可以將指令碼儲存於任何位置。但是,最好將其儲存於外圍程序可以找到的目錄中。外圍程序中的搜尋路徑由環境變數
PATH
決定。一般使用者對於/usr/bin
沒有寫入權限。因此,建議將指令碼儲存在使用者目錄~/bin/
中。以上範例名為hello.sh
。指令碼需要執行權限。使用下列指令設定權限:
>
chmod +x ~/bin/hello.sh
如果符合上述所有先決條件,則可以採用如下方式執行此指令碼:
做為絕對路徑: 執行指令碼時可以使用絕對路徑。在本例中為
~/bin/hello.sh
。任何位置: 如果
PATH
環境變數包含指令碼所在的目錄,您可以使用hello.sh
來執行指令碼。
1.3 重新導向指令事件 #
每條指令可以使用三個通道用於輸入或輸出:
標準輸出: 這是預設的輸出通道。指令進行列印時會使用標準輸出通道。
標準輸入: 如果指令需要使用者或其他指令的輸入,將會使用此通道。
標準錯誤: 指令使用此通道報告錯誤。
要重新指向這些通道,可以使用以下幾種方式:
Command > File
將該指令的輸出儲存到檔案中,會刪除現有檔案。例如,
ls
指令將輸出寫入到檔案listing.txt
中:>
ls > listing.txtCommand >> File
將指令輸出附加至檔案。例如,
ls
指令將輸出附加至檔案listing.txt
中:>
ls >> listing.txtCommand < File
讀取檔案,將其做為指定指令的輸入。例如,
read
指令會將檔案內容讀取至變數中:>
read a < fooCommand1 | Command2
將左邊指令的輸出重新指向為右邊指令的輸入。例如,
cat
指令會輸出檔案/proc/cpuinfo
的內容。再由grep
使用此輸出內容單獨過濾出包含cpu
的行:>
cat /proc/cpuinfo | grep cpu
每個通道都有一個檔案描述子:0 (零) 代表標準輸入,1 代表標準輸出,2 代表標準錯誤。您可以在 <
或 >
字元前面插入此檔案描述子。例如,下行會搜尋以 foo
開頭的檔案,但透過將檔案重新導向至 /dev/null
隱藏了錯誤:
>
find / -name "foo*" 2>/dev/null
1.4 使用別名 #
別名為一或多條指令的簡短定義。別名的語法為:
alias NAME=DEFINITION
例如,下行定義了一個別名 lt
,它會輸出一份較長的清單 (選項 -l
),將清單依修改時間排序 (-t
),並依排好序的倒序列印 (-r
):
>
alias lt='ls -ltr'
要檢視所有的別名定義,請使用 alias
。若要移除別名,請使用 unalias
和對應的別名名稱。
1.5 在 Bash 中使用變數 #
外圍程序變數可以是全域變數或本地變數。您可以在所有外圍程序中存取全域變數或環境變數。與此相反,本地變數僅顯示於目前的外圍程序中。
要檢視所有環境變數,請使用 printenv
指令。如需瞭解變數的值,則將變數名稱做為引數插入:
>
printenv PATH
無論是全域變數還是本地變數,都可以使用 echo
進行檢視:
>
echo $PATH
要設定本地變數,請使用變數名稱,後面跟上等號,再跟上值:
>
PROJECT="SLED"
請不要在等號兩邊插入空格,否則將會出錯。要設定環境變數,請使用 export
:
>
export NAME="tux"
若要移除變數,請使用 unset
:
>
unset NAME
下表包含可以在外圍程序指令碼中使用的常見環境變數:
|
目前使用者的主目錄 |
|
目前的主機名稱 |
|
工具當地化以後,會使用此環境變數指定的語言。也可將英語設定為 |
|
外圍程序的搜尋路徑,即以冒號分隔的目錄清單 |
|
指定在每條指令前列印的一般提示 |
|
指定執行多行指令時列印的輔助提示 |
|
目前的工作目錄 |
|
目前的使用者 |
1.5.1 使用引數變數 #
例如,如果您有指令碼 foo.sh
,可以按以下格式執行該指令碼:
>
foo.sh "Tux Penguin" 2000
若要存取傳送至指令碼的所有引數,需要使用位置參數。這些參數為 $1
(表示第一個引數)、$2
(表示第二個參數),依此類推。最多可以使用九個參數。若要獲取指令碼名稱,請使用 $0
。
下面的指令碼 foo.sh
可列印從 1 到 4 的所有引數:
#!/bin/sh echo \"$1\" \"$2\" \"$3\" \"$4\"
如果您使用以上引數執行此指令碼,所得結果為:
"Tux Penguin" "2000" "" ""
1.5.2 使用變數替代項 #
變數替代項會從左側或右側將模式套用至變數內容。以下清單包含了可用的語法格式:
${VAR#pattern}
從左側移除最短的相符項:
>
file=/home/tux/book/book.tar.bz2>
echo ${file#*/} home/tux/book/book.tar.bz2${VAR##pattern}
從左側移除最長的相符項:
>
file=/home/tux/book/book.tar.bz2>
echo ${file##*/} book.tar.bz2${VAR%pattern}
從右側移除最短的相符項:
>
file=/home/tux/book/book.tar.bz2>
echo ${file%.*} /home/tux/book/book.tar${VAR%%pattern}
從右側移除最長的相符項:
>
file=/home/tux/book/book.tar.bz2>
echo ${file%%.*} /home/tux/book/book${VAR/pattern_1/pattern_2}
以 PATTERN_2 取代 PATTERN_1 中 VAR 的內容:
>
file=/home/tux/book/book.tar.bz2>
echo ${file/tux/wilber} /home/wilber/book/book.tar.bz2
1.6 分組和組合指令 #
外圍程序允許您組合及分組指令,以便按條件執行。每條指令都會傳回決定操作成功與否的離開碼。如果為 0 (零),則說明指令成功,任何其他離開碼都代表特定於指令的錯誤。
以下清單顯示了可對指令分組的方式:
Command1 ; Command2
以順序執行指令。不檢查離開碼。以下一行指令透過
cat
顯示檔案中的內容,然後透過ls
列印其檔案內容,而不考慮結束碼為何:>
cat filelist.txt ; ls -l filelist.txtCommand1 && Command2
如果左邊的指令成功,即會執行右邊的指令 (邏輯「與」)。下行顯示檔案內容,並且僅在前面的指令成功時才會列印其檔案內容 (將其與此清單中的上一個項目進行比較):
>
cat filelist.txt && ls -l filelist.txtCommand1 || Command2
如果左邊的指令失敗,即會執行右邊的指令 (邏輯「或」)。下行只會在於
/home/tux/foo
中建立目錄失敗時,才會在/home/wilber/bar
中建立目錄:>
mkdir /home/tux/foo || mkdir /home/wilber/barfuncname(){ ... }
建立外圍程序函數。可以使用位置參數存取其引數。下行定義了可列印較短訊息的函數
hello
:>
hello() { echo "Hello $1"; }可以按以下格式呼叫此函數:
>
hello Tux將會列印:
Hello Tux
1.7 使用通用流程建構元 #
為了控制指令碼的流程,外圍程序可以使用 while
、if
、for
和 case
建構元。
1.7.1 if 控制指令 #
if
指令用於檢查運算式。例如,以下程式碼將測試目前的使用者是否為 Tux:
if test $USER = "tux"; then echo "Hello Tux." else echo "You are not Tux." fi
測試運算式可以很複雜,也可以很簡單。下面的運算式會檢查檔案 foo.txt
是否存在:
if test -e /tmp/foo.txt ; then echo "Found foo.txt" fi
測試運算式也可以縮寫到方括弧中:
if [ -e /tmp/foo.txt ] ; then echo "Found foo.txt" fi
如需更多有用的運算式,請參閱 https://bash.cyberciti.biz/guide/If..else..fi。
1.7.2 使用 for
指令建立迴路 #
for
迴路可讓您對一組項目執行指令。例如,以下程式碼會列印關於目前工作目錄中 PNG 檔案的特定資訊:
for i in *.png; do ls -l $i done
1.8 更多資訊 #
手冊頁 man
bash
中提供了關於 Bash 的重要資訊。以下清單中提供了更多關於此主題的資訊:
https://tldp.org/LDP/Bash-Beginners-Guide/html/index.html — Bash 初級使用者指南
https://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html — BASH 程式設計 - 方法介紹
https://tldp.org/LDP/abs/html/index.html — Bash 指令碼進階指南
https://www.grymoire.com/Unix/Sh.html — Sh - Bourne 外圍程序