本篇文章給大家分享的是有關Shell的本質以及用法是怎樣的,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
要點:命令行、標準輸入和標準輸出、重定向、管道、后臺運行程序、kill:終止后臺作業、文件名生成/路徑名展開、內置命令
Shell是用戶的系統界面,提供了用戶與內核進行交互操作的一種接口。它接收用戶輸入的命令并把它送去內核執行。實際上Shell是一個命令解釋器,它解釋由用戶輸入的命令并且把它們送到內核。
當在命令提示符后鍵入命令回車,shell將執行相應的程序。比如,鍵入ls后回車,shell開始執行名為ls的工具。也可讓shell以同樣的方式執行其他類型的程序,如shell腳本、應用程序或自己編寫的程序。包含命令和參數的行稱為命令行。命令是指在命令行上鍵入的字符,同時還指對應動作所調用的程序。
命令行語法說明了行中各個元素的排列順序和間隔方式。當用戶鍵入命令回車后,shell將掃描命令行進行語法檢查。命令行上基本的語法格式如下:
command [arg1] ... [argn] RETURN
命令行上采用一個或多個空格來隔開每個元素。其中,command為命令的名字,arg1到argn為命令的參數,回車是終止命令的按鍵。語法格式中的方括號表明被括起來的參數為可選項。并不是所有的命令都需要參數,有些命令就沒有參數,有些命令需要可變數目的參數,有些命令則需要特定數目的參數。選項是一種特殊類型的參數,前面通常為一個或兩個連字符(“-”或“--”)。
一些有用的Linux命令行僅由命令名組成而不帶任何參數。例如,不帶任何參數的ls將顯示工作目錄下的文件列表。多數命令都帶一個或多個參數,當使用需要帶參數的命令時,若沒有帶參數,或帶了不正確的參數,或參數數目使用錯誤,系統都會返回用戶簡短錯誤信息提示,這些信息稱為命令的用法消息(usage message)。
命令行上,每一串不含空格字符的字符序列稱為記號或字。參數是一種記號,如文件名、文本串、數字或命令處理的其他對象。
下面展示一個cp的命令行:
[root@QUFENGBIN ~]# cp temp tempcopy
| 參數都有編號,其中命令本身作為參數0,它是命令行參數的開始。在這個例子中,cp為參數0,temp為參數1,tempcopy為參數2。cp至少需要兩個字段(還可帶多個參數,但不能少于兩個),參數1是已存在的文件名,參數2是cp要創建或重寫的文件。這兩個參數都不是可選的,而是命令運行所必需的。 PS:shell一些關于參數的特殊字符 | 特殊字符 | 說明 |
|---|---|---|
| $$ | Shell本身的PID(ProcessID) | |
| $! | Shell最后運行的后臺Process的PID | |
| $? | 最后運行的命令的結束代碼(返回值) | |
| $- | 使用Set命令設定的Flag一覽 | |
| $* | 所有參數列表。如"$*"用「"」括起來的情況、以"$1 $2 … $n"的形式輸出所有參數。 | |
| $@ | 所有參數列表。如"$@"用「"」括起來的情況、以"$1" "$2" … "$n" 的形式輸出所有參數。 | |
| $# | 添加到Shell的參數個數 | |
| $0 | Shell本身的文件名 | |
| $1~$n | 添加到Shell的各參數值。$1是第1參數、$2是第2參數…。 |
[root@aminglinux_01 test]# cat test.sh #!/bin/sh echo "number:$#" echo "scname:$0" echo "first :$1" echo "second:$2" echo "argume:$@" [root@aminglinux_01 test]# sh test.sh aa bb number:2 scname:test.sh first :aa second:bb argume:aa bb
選項(option)是改變命令執行效果的參數??赏ㄟ^指定多個選項使得命令按照不同的方式執行。選項與特定的程序相關,并由命令行上調用的程序解釋,而非由shell解釋。
按照約定,選項是跟在命令之后其他參數(如文件名)之前的單獨的參數。多數命令的選項前面需要加一個連字符,但這個要求是與工具相關的,而與shell無關。GNU程序的選項前通常帶兩個連字符。例如,--help會生成用法消息。
當需要多個選項時,可將多個單字符選項組合成一個參數,以一個連字符開始,在選項前不要加空格。但是,這樣合并后的選項之前不能使用兩個連字符。對于合并選項的具體規則與具體的運行程序有關。多數命令的選項不分前后順序。
有些工具的選項本身也要帶參數。如,gcc(GUN的c編譯器)的-o選項必須后跟gcc產生的可執行文件名,通常選項與其參數間用空格隔開,如下:
[root@QUFENGBIN ~]# gcc -o prog prog.c
按照約定,工具的參數(如文件名)是允許以連字符開始的。這樣當某個文件的名字為-l時,命令的意義將不明確。如果創建了這類文件,那么,一些命令約定使用--(兩個連續的連字符)參數來表示選項的結束(和參數的開始)。如下示例:
[root@QUFENGBIN test]# touch -l touch: invalid option -- 'l' Try 'touch --help' for more information. [root@QUFENGBIN test]# touch -- -l [root@QUFENGBIN test]# ls -l total 4 -rw-r--r-- 1 root root 0 Apr 28 11:41 -l -rw-r--r-- 1 root root 14 Apr 26 16:26 test01 [root@QUFENGBIN test]# ls -- -l -l [root@QUFENGBIN test]# ls -l -- -l -rw-r--r-- 1 root root 0 Apr 28 11:41 -l
當向命令行鍵入命令時,Linux的tty設備驅動程序(Linux操作系統內核的一部分)將檢查每個字符,來確定是否要立即采取動作。當鍵入的字符不需要采取立即的動作時,設備驅動程序將把字符存儲在緩沖區中,等待字符輸入。當按下回車鍵后,設備驅動程序將把命令行傳遞過程shell處理。
當shell處理命令行時,它將把命令行作為一個整體來對待,并將其分成幾個組成部分。接著,shell將查找命令的名稱。命令行中提示符后的第1項(即參數0)通常為命令名,因此shell將把命令行中從第1個字符到第一個空白字符(TAB或空格)之間的字符串作為命令名。命令名(第1個記號)可采用簡單文件名或路徑名的方式指定。例如:
[root@QUFENGBIN test]# ls -l test01 [root@QUFENGBIN test]# /bin/ls -l test01
當在命令行上輸入絕對路徑名或非簡單文件名的相對路徑名時(即輸入至少包含一條斜杠的路徑名),shell將在指定目錄下查找具有執行權限的對應文件。例如,輸入命令/bin/ls,shell將查找/bin目錄下具有執行權限且名為ls的文件。當輸入的是一個簡單文件名時,shell在一組目錄中查找與該文件名匹配且具有執行權限的對應文件。shell并不是在所有目錄下搜索,而只在PATH變量設定的路徑下搜索。
如果shell找到了與命令行上的命令具有相同名字的可執行文件,那么,shell將啟動一個新的進程(進程是指Linux命令的執行),并將命令行上的命令名、參數、選項傳遞給程序(可執行文件)。當命令執行時,shell將等待進程的結束,這時shell處于非活躍狀態,稱為休眠狀態。當程序執行完畢,就將它的退出狀態傳遞給shell,這樣shell就返回到活躍狀態(被喚醒),顯示提示符,等待下一個命令的輸入。
由于shell不處理命令行上的參數,只是將它們傳遞給調用的程序,所以shell不知道選項和參數是否對程序有效。所以關于選項和參數的錯誤消息和用法消息都來自程序自身。也有些命令會忽略無效的選項。
標準輸出(standard output)是指程序輸出信息(如文本)的地方。程序從來都不“知道”它發送到標準輸出的信息究竟送往何處。這些信息可以輸出到打印機、普通文件或屏幕。默認情況下,shell把命令的結果標準輸出到屏幕。shell也可以將輸出重定向到其他文件。
標準輸入(standard input)是程序信息的來源。而對于標準輸出,程序從不“知曉”信息的來源。默認情況下,程序的輸入來自鍵盤輸入。
對一個運行的程序來說,除了具有標準輸入和標準輸出外,通常還有錯誤消息輸出,稱為標準錯誤輸出(standard error)。
命令并不知道標準輸入來自哪,也不知道標準輸出和標準錯誤輸出到哪。
除了普通文件、目錄文件、硬鏈接和軟鏈接之外,Linux還有一種文件類型:設備文件(device file)。設備文件駐留在Linux文件結構中(通常位于目錄/dev中),用來代表外圍設備,如終端模擬器、顯示屏、打印機和硬盤驅動器。
在who工具顯示的內容中,登錄名后的設備名即為屏幕的文件名。如果打開了多個窗口,每個打開的窗口都有對應的設備名。在這些窗口中運行tty工具即可得到它們各自的名稱??梢园堰@個設備文件看作是一個文本文件進行讀寫。向該文件寫入會在屏幕上顯示寫入的內容,而從該文件讀取就是從鍵盤上讀取鍵入的內容。
[root@QUFENGBIN test]# tty /dev/pts/0 [root@QUFENGBIN test]# who root tty1 2018-04-17 15:44 root pts/0 2018-04-28 11:34 (111.113.5.18)
chsh:改變登錄shell
系統管理員在建立用戶賬戶時,將確定用戶第一次登錄系統或打開GUI環境下終端模擬器窗口時使用的shell(見/etc/passwd文件)。
root:x:0:0:root:/root:/bin/bash .... ..... mysql:x:1000:1000::/home/mysql:/bin/false www:x:1001:1001::/home/www:/bin/false apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
但在登錄系統后,用戶可以自己決定運行哪個shell。鍵入要使用的shell名(如bash,tcsh,或另一個shell),然后回車,則出現的提示符即為新設定shell給出的,輸入exit命令可退回到上一個shell。
使用工具chsh可以永久地修改登錄shell。首先輸入命令chsh,然后在提示符后輸入口令和要使用的shell的絕對路徑名(如/bin/bash或者/bin/tcsh,或者另一個shell的路徑名)。
當第一次登錄時,shell將其標準輸出發送到代表屏幕的設備文件中,采用這種方式輸出可以把輸出內容在屏幕上顯示出來。shell還將代表鍵盤的設備文件作為標準輸入的來源,這樣命令會把在鍵盤上鍵入的任何內容作為輸入接收。
重定向(redirection)是指改變shell標準輸入來源和標準輸出去向的各種方式。例如,默認情況下,shell將cat的標準輸入關聯到鍵盤,標準輸出關聯到屏幕。但也可以讓shell重定向任何命令的標準輸入或標注輸出,方法就是將輸入或輸出與某命令或某文件關聯,而不再是與代表鍵盤或屏幕的設備文件進行關聯。
通過重定向符號(>)可以將shell命令的輸出重定向到指定的文件而不再是屏幕。重定向的命令格式為:
command [arguments] >filename
其中,command為可執行程序(如應用程序或者是工具),arguments是可選參數,filename是shell要重定向輸出到的普通文件名。
重定向可能覆蓋文件!在重定向命令執行前,如果該文件已經存在,那么shell將重寫并覆蓋其原來的內容。
與重定向標準輸出一樣,也可以重定向標準輸入。通過重定向標準輸入符號(<)可以使shell將命令的輸入重定向為來自指定的文件而不再是鍵盤。重定向標準輸入的命令格式為:
command [arguments] <filename
其中,command為可執行程序(如應用程序或者是工具),arguments是可選參數,filename是shell要重定向輸入來自的普通文件名。
將文件或者標準輸入作為輸入的工具:將命令cat的輸入重定向到文件的執行結果與命令cat后跟文件名作為參數的執行結果相同。像cat這樣具有這種特性的工具屬于Linux中的一類工具,這類工具還包括lpr、sort和grep。這類工具首先檢測調用它們的命令行。如果命令行上存在文件名,那么這類工具就把指定的文件作為輸入;否則,如果命令行上沒有指定文件名,那么這類工具就把標準輸入作為輸入。這種功能特性是該類工具自身所具有的,而與shell或者是操作系統無關。
shell提供了一種稱為noclobber的特性,該特性可防止重定向時不經意地重寫了已存在的文件。
[root@QUFENGBIN test]# set -o noclobber [root@QUFENGBIN test]# echo "noclobber test" > test01 -bash: test01: cannot overwrite existing file [root@QUFENGBIN test]# set +o noclobber [root@QUFENGBIN test]# echo "noclobber test" > test01 [root@QUFENGBIN test]# cat test01 noclobber test
在重定向輸出符號后跟管道符號,即使用符號組合“>|”可以忽略noclobber的設置。
[root@QUFENGBIN test]# set -o noclobber [root@QUFENGBIN test]# echo "noclobber test" > test01 -bash: test01: cannot overwrite existing file [root@QUFENGBIN test]# echo "noclobber test" >| test01 [root@QUFENGBIN test]# cat test01 noclobber test [root@QUFENGBIN test]# set +o noclobber
使用追加輸出符號(>>)可以向某個文件末尾添加新的內容,并且不改變原來已有內容。
[root@QUFENGBIN test]# cat test01 noclobber test [root@QUFENGBIN test]# echo "noclobber test" > test01 ; cat test01 noclobber test [root@QUFENGBIN test]# echo "noclobber test" >> test01 ; cat test01 noclobber test noclobber test
設備/dev/null是一個數據接收器(data sink),通常被稱為位桶(bit bucket)??梢詫⒉幌肟吹交蛘呤遣幌氡4娴臄祿囟ㄏ虻?dev/null,這樣數據將不留痕跡的消失。
[root@QUFENGBIN test]# echo "noclobber test" > /dev/null
當從/dev/null中讀取數據時,將得到一個空字符串。
[root@QUFENGBIN test]# ls -l test01 -rw-r--r-- 1 root root 30 Apr 28 14:59 test01 [root@QUFENGBIN test]# cat /dev/null > test01 [root@QUFENGBIN test]# ls -l test01 -rw-r--r-- 1 root root 0 Apr 28 15:05 test01
shell使用管道將一個命令的輸出直接連接到另一個命令的輸入。管道(pipe,有時被稱為pipeline)的功能實現類似于下面的過程:首先將一個命令的標準輸出重定向到一個文件,然后將該文件作為另一個命令的標準輸入。管道不會單獨處理每條命令,并且不需要中間文件。管道的符號為一條豎線(|),命令行語法格式為:
command_a [arguments] | command_b [arguments]
上面的命令行得到的結果與下面的這一組命令得到的結果相同:
command_a [arguments] > temp command_b [arguments] < temp rm remp
任何Linux工具都可以使用管道從命令行上指定的文件中接受輸入,也可以從標準輸入接受輸入。
有些使用管道的命令僅從標準輸入接受輸入,如工具tr(transtate,轉換)就只能從標準輸入接受輸入(即tr只能通過標準輸入接受輸入,而無法通過命令行參數來接受輸入)。
使用tr的最簡單格式:tr string1 string2
tr工具從標準輸入接受輸入,查找與string1匹配的字符,找到一個匹配就將string1的字符替換為string2中對應字符。tr工具將它的輸出發送到標準輸出。
[root@QUFENGBIN test]# tr abc ABC < test01 ABCdefgABCDEFG [root@QUFENGBIN test]# cat test01 | tr abc ABC ABCdefgABCDEFG [root@QUFENGBIN test]# tr abc ABC test01 tr: extra operand ‘test01’ Try 'tr --help' for more information.
1.過濾器
過濾器(filter)是將輸入數據流處理后在輸出數據流的一類命令。包含過濾器的命令行用一個管道將某個命令的標準輸出連接到過濾器的標準輸入,用另一個管道將過濾器的標準輸出連接到另一個命令的標準輸入。并不是所有的工具都可以用作過濾器。
2.tee:向兩個方向輸入
tee工具將標準輸入復制到文件和標準輸出。該工具被命名為tee是因為:它只有一個輸入,但輸出到兩個方向。
[root@QUFENGBIN test]# who | tee who.out | grep root root tty1 2018-04-17 15:44 root pts/0 2018-04-28 14:50 (111.113.5.18) [root@QUFENGBIN test]# cat who.out root tty1 2018-04-17 15:44 root pts/0 2018-04-28 14:50 (111.113.5.18)
前臺:當在前臺運行命令時,shell將一直等到命令執行完畢。才會給出提示符使得你可繼續輸入下一個命令。當命令在后臺運行時,就不必等待該命令完成,可直接輸入另一個命令。
作業:作業(job)是由一個或者(可通過管道連接的)多個命令組成的序列。前臺只能有一個作業位于窗口或屏幕中,但可以有多個作業在后臺運行。同一時間運行多個作業是Linux的重要特性,這常被稱為多任務特性。
作業編號與PID號:如果在命令行的末尾輸入與符號(&)后回車,那么shell將在后臺運行這個作業。同時,shell會給這個作業分配一個作業編號(是個小數字),并將其顯示在方括號內。在作業編號之后,shell將顯示進程標識(process identification,PID)號,該號是由操作系統分配的一個大數。每個大數后面都標識了后臺運行的一條命令。然后,shell將顯示另一個提示符,這是便可以鍵入另一個命令。當后臺作業運行結束時,shell將顯示一個消息,這個消息的內容為:已結束作業的作業編號和運行該作業的命令行。
CONTROL+Z:程序掛起鍵,shell把前臺的作業掛起(阻止其繼續運行),并終止作業中的進程,將進程的標準輸入與鍵盤隔開。用bg命令后跟作業編號可以把掛起的作業放到后臺執行。如果僅有一個作業被掛起。那么可以不必指明作業編號。只有前臺作業可以從鍵盤獲得輸入。為了將鍵盤和后臺某個正運行的作業連接起來,必須把該后臺作業移至前臺。用fg命令后跟作業編號可以把后臺的作業移至前臺。不帶任何參數的fg命令可以將后臺唯一的作業移至前臺。
命令行上輸入kill后跟進程的PID號(或者后跟%和作業編號),可以將后臺正在運行的進程(或作業)終止,使用中斷鍵(通常CONTROL+C)是不能實現其功能的。
通配符和通配:當輸入包含特殊字符(也稱為元字符)的部分文件名時,shell可以生成與已有文件的名字匹配的文件名。這些特殊的字符也常常被稱為通配符(wildcard)。當某個特殊字符作為參數出現在命令行上時,shell將該參數擴展為有序的文件名列表,并將列表傳遞給命令行上調用的程序。包含特殊字符的文件名稱為模糊文件引用(ambiguous file reference),因為它們不與任何一個特定文件相關聯。對這些文件名操作的過程稱為路徑名展開(path expansion)或者通配(globbing)。
問號(?)是shell生成文件名的特殊字符,它與已有文件名中的某個單獨字符匹配。
[root@QUFENGBIN test]# ls test0? test01 [root@QUFENGBIN test]# echo wh?.out who.out [root@QUFENGBIN test]# cat wh?.out root tty1 2018-04-17 15:44 root pts/0 2018-04-28 14:50 (111.113.5.18)
星號(*)的功能與問號的類似,不同之處在于,星號可以跟文件名中的任意多個(包括0個)字符匹配。
用方括號將一個字符列表括起來使得shell與包含列表中每個單獨字符的文件名進行匹配。例如,test?可匹配test后跟任何一個字符的文件名,而方括號更嚴格些,test[01]僅與test0、test1匹配。這里,方括號定義了一個字符類(character class),該類由括號內的所有字符組成。
每個定義字符類只能替換文件名中的一個字符。方括號和其中的內容合起來的功能就如同問號一樣,但是只能用字符類中的一個成員替換。
ps:左方括號后直接跟嘆號(!)或脫字符(^)也可以定義字符類,該類與任何不在方括號內的字符匹配。例如,[^ab]*與不以a或b開始的文件名匹配。
[root@QUFENGBIN test]# ls aa ab ac ad ba bb bc bd cc dd [root@QUFENGBIN test]# ls *[^ab] ac ad bc bd cc dd [root@QUFENGBIN test]# ls [b-d]* ba bb bc bd cc dd
下面的例子表示ls工具不能翻譯模糊文件引用。第一個ls命令帶有參數aa*,shell將其擴展為匹配的文件名aa,并將該名字傳遞給ls。第二個命令將*轉義,shell不再將*看作特殊字符,并將其傳遞給ls,ls報錯。大多數工具和程序像ls一樣也不能解釋模糊文件引用,該解釋工作由shell完成。
[root@QUFENGBIN test]# ls aa* aa [root@QUFENGBIN test]# ls aa\* ls: cannot access aa*: No such file or directory [root@QUFENGBIN test]# ls aaa* ls: cannot access aaa*: No such file or directory
注意:模糊文件引用由shell進行擴展,而不是shell調用的程序進行擴展。上面的例子都不能“看到”模糊文件引用。shell對模糊文件引用進行擴展,并將擴展得到的文件列表傳遞給工具。在下面的echo例子中驗證了這一點,因為它顯示了參數而不是模糊文件引用。
[root@QUFENGBIN test]# ls aa ab ac ad ba bb bc bd cc dd [root@QUFENGBIN test]# echo a? aa ab ac ad [root@QUFENGBIN test]# echo * aa ab ac ad ba bb bc bd cc dd [root@QUFENGBIN test]# echo a* aa ab ac ad [root@QUFENGBIN test]# echo .* . .. [root@QUFENGBIN test]# echo [a-m]* aa ab ac ad ba bb bc bd cc dd [root@QUFENGBIN test]# echo [x-z]* [x-z]* [root@QUFENGBIN test]# echo *[a-d] aa ab ac ad ba bb bc bd cc dd [root@QUFENGBIN test]# echo *[x-z] *[x-z] [root@QUFENGBIN test]# echo [a-d]\* [a-d]* [root@QUFENGBIN test]# echo [x-z]\* [x-z]*
外部命令
外部命令,有時候也被稱為文件系統命令,是存在于bash shell之外的程序。它們并不是shell程序的一部分。外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin中。ps是一個外部命令,你可以使用which和type命令找到它。
當外部命令執行時,會創建出一個子進程。這種操作被稱為衍生(forking)。
內置命令(內建命令)
內建命令和外部命令的區別在于前者不需要使用子進程來執行。它們已經和shell編譯成了一體,作為shell工具的組成部分存在。不需要借助外部程序文件來運行??梢岳?type 命令來了解某個命令是否是內建的。每個shell都有自己的內置命令集合。
輸入命令“man bash”,再輸入命令“/^SHELL BUILTIN COMMANDS”,shell將在內置命令部分搜索始于SHELL的行,從而可以查看內置命令的man頁內容。
要點:初始化文件、重定向標準錯誤輸出、編寫簡單的shell腳本、作業控制、操作目錄棧、參數和變量、進程、命令歷史機制、重新執行和編輯命令、別名、函數、控制bash特性和選項、處理命令行
Bourne Again Shell是一種命令解釋器,同時也是一種高級編程語言。作為命令解釋器,它們通過提示符響應并處理用戶在命令行界面上輸入的命令。而作為一種編程語言,它們將處理存放在所謂shell腳本文件中的命令。
內容包括編寫和使用初始化文件、重定向標準錯誤輸出、編寫和執行簡單的shell腳本、命令分割和分組、實現作業控制和操作目錄棧。
當啟動shell時,它將運行初始化文件初始化自己。具體運行哪個文件取決于該shell是一個登錄shell還是一個非登錄shell的交互式shell(比如通過命令bash),又或者是一個非交互式shell(用來執行shell腳本)。要想運行初始化文件中的命令,用戶必須具備讀權限。
登錄shell本來就屬于交互式shell。
/etc/profile:shell首先執行/etc/profile中的命令。通過設置這個文件,超級用戶可以為全系統內的所有bash用戶建立默認特征。
.bash_profile、.bash_login和.profile:然后shell依次查找~/.bash_profile、~/.bash_login和~/.profile,并執行它找到的首個文件中的命令??梢詫⒚罘胖迷谶@些文件中的某個里以覆蓋掉/etc/profile文件中的默認設置。
.bash_logout:當用戶注銷時,bash執行文件~/.bash_logout中的命令。這個文件包含了退出會話時需要執行的清理任務(比如刪除臨時文件)常用到的命令。
在交互式非登錄shell中并不執行前面提到的初始化文件中的命令。然而,交互式非登錄shell從登錄shell繼承了由這些初始化文件設置的shell變量。
/etc/bashrc:盡管不是通過bash直接調用,許多~/.bashrc文件還是調用/etc/bashrc。這種安排使得超級用戶可以為全系統內的非登錄bash shell建立默認特性。
.bashrc:交互式非登錄shell執行~/.bashrc文件中的命令,而登錄shell的初始化文件(比如.bash_profile)通常會運行這個文件。這樣,登錄shell和非登錄shell都可以使用.bashrc中的命令。
非交互式shell(如那些運行shell腳本的shell)并不執行前面描述的初始化文件中的命令。然而,這些shell從登錄shell那里繼承了由這些初始化文件設置的shell變量。
BASH_ENV:非交互式shell查找環境變量BASH_ENV(或者當shell作為sh調用時為ENV),并執行由該變量命名的文件中的命令。
盡管有很多種初始化文件和shell,但是用戶通常只需要主目錄下的.bash_profile和.bashrc文件。.bash_profile中通過以下命令將為登錄shell執行.bashrc(如果該文件存在)中的命令。
if [ -f ~/.bashrc ]; then . ~/.bashrc fi
在編輯諸如.bashrc這類的初始化文件之后,要使這些修改起作用,用戶沒有必要注銷然后再次登錄,可以使用內置命令“.”(句點)或者source(這是兩個相同的命令)。與其他命令一樣,在命令行上,“.”后面必須有一個空格。內置命令“.”或者source用起來類似于運行一個shell腳本,但是這些命令將該腳本作為當前進程的一部分運行。因此,當使用“.”或者source運行腳本的時候,在腳本中改變的變量也將影響到運行該腳本的shell??梢允褂谩?”或者source命令來運行任何shell腳本,而不僅僅是初始化文件,但是可能會帶來副作用(比如可能修改用戶依賴的shell變量的值)。如果將初始化文件作為常規shell腳本運行,并且不使用“.”或source內置命令,那么啟動腳本中創建的變量將只在運行該腳本的子shell中起作用。
| Bourne Again Shell以多種方式使用符號(、)、[、]和$。為了避免混淆,下表列出了每種符號最通用的用法。 | 符號 | 命令 |
|---|---|---|
| () | 子shell | |
| $() | 命令替換 | |
| (()) | 算術表達式計算,let的同義詞(當被括起來的值中包含等號時使用) | |
| $(()) | 算術展開(不用于被括起來的值中包含等號的情形) | |
| [] | test命令 | |
| [[]] | 條件表達式,類似于[],但是添加了字符串比較 |
除了標準輸出之外,命令還可以將輸出發送到標準錯誤輸出(standard error)。命令將錯誤消息發送到標準錯誤輸出,這樣就可以避免與發送到標準輸出的信息混淆在一起。
與處理標準輸出一樣,默認情況下,shell 將命令的標準錯誤輸出發送到屏幕上。除非將標準輸出和標準錯誤輸出中的某一個重定向,否則不能區分命令的輸出到底是標準輸出還是標準錯誤輸出。
文件描述符:文件描述符(file descriptor)是程序發送輸出和獲取輸入的地方。當執行一個程序時,運行該程序的進程打開了 3 個文件描述符,分別是:0(標準輸入)、1(標準輸出)和 2(標準錯誤輸出)。重定向標準輸出符號(>)是 1> 的簡寫,它通知 shell 將標準輸出重定向。類似地,< 是 0< 的簡寫,表示將標準輸入重定向。符號 2> 將標準錯誤輸出重定向。
下面是個例子:
當運行 cat 時,如果所帶參數中的某個文件不存在,而另一個文件存在,那么 cat 將發送一條錯誤消息到標準錯誤輸出,同時還將已存在的那個文件復制一份到標準輸出。除非將它們重定向,否則兩條消息都將出現在屏幕上。
[root@QUFENGBIN test]# cat y This is y [root@QUFENGBIN test]# cat x cat: x: No such file or directory [root@QUFENGBIN test]# cat x y cat: x: No such file or directory This is y
將命令的標準輸出重定向時,發送到標準錯誤輸出的輸出結果將不受影響,仍然出現在屏幕上。
[root@QUFENGBIN test]# cat x y > hold cat: x: No such file or directory [root@QUFENGBIN test]# cat hold This is y [root@QUFENGBIN test]# cat x y 1> hold1 2>hold2 [root@QUFENGBIN test]# cat hold1 This is y [root@QUFENGBIN test]# cat hold2 cat: x: No such file or directory
復制文件描述符:在下一個例子中,1> 將標準輸出重定向到文件hold。然后,2>&1 聲明文件描述符 2 為文件描述符 1 的副本。結果是, 標準輸出和標準錯誤輸出均被重定向到文件hold中。
root@QUFENGBIN test]# cat x y 1>hold 2>&1 [root@QUFENGBIN test]# cat hold cat: x: No such file or directory This is y
在上面這個示例中,1>hold 放在了 2>&1 的前面。如果將它們的順序顛倒的話,在標準輸出重定向到文件 hold 之前,標準錯誤輸出就已經拷貝了標準輸出的一個副本。這樣一來,就只有標準輸出被重定向到文件hold。
[root@QUFENGBIN test]# cat x y 2>&1 1>hold cat: x: No such file or directory [root@QUFENGBIN test]# cat hold This is y [root@QUFENGBIN test]# cat x y 2>&1 cat: x: No such file or directory This is y
Bourne Again Shell所支持的重定向操作符如下表。
| 操作符 | 含義 |
|---|---|
| <filename | 將標準輸入重定向為文件 filename |
| >filename | 除非文件 filename 已存在并且設置了 noclobber 標記,否則標準輸出將被重定向到文件 filename 。如果文件 filename 不存在且沒有設置 noclobber 標記,那么重定向操作將創建該文件 |
| >|filename | 即使文件 filename 已存在并且設置了 noclobber 標記,仍將標準輸出重定向到該文件 |
| >>filename | 除非文件 filename 已存在并且設置了 noclobber 標記,否則標準輸出將被重定向到文件 filename,并將內容添加到原文件的末尾。如果文件 filename 不存在且沒有設置 noclobber 標記,那么將創建該文件 |
| <&m | 從文件描述符m復制標準輸入 |
| [n]>&m | 從文件描述符m復制標準輸出或者文件描述符n(如果命令中指定了n) |
| [n]<&- | 關閉標準輸入或者文件描述符n(如果指定了n) |
| [n]>&- | 關閉標準輸出或者文件描述符n(如果指定了n) |
shell 腳本是包含 shell 可執行命令的文件。shell 腳本中的命令可以是用戶在 shell 提示符后面輸入的任何命令。除了可以使用用戶在命令行下面輸入的命令之外,shell 腳本還可以使用控制流(control flow)命令(亦稱為控制結構(control structure))。使用這組命令可以改變腳本中命令的執行順序。
任何用戶想把文件名作為命令行執行,都必須具備執行訪問權。如果該文件是一個 shel l腳本,用戶嘗試執行這個文件時,還必須具備讀訪問權限。而在執行一個二進制可執行文件(已編譯程序)時,并不需要讀訪問權限。
[root@QUFENGBIN test]# ll whoson -rw-r--r-- 1 root root 41 Apr 30 20:09 whoson [root@QUFENGBIN test]# cat whoson date echo "User Currently Logged In" who [root@QUFENGBIN test]# whoson -bash: whoson: command not found [root@QUFENGBIN test]# ./whoson -bash: ./whoson: Permission denied [root@QUFENGBIN test]# chmod u+x whoson [root@QUFENGBIN test]# ./whoson Mon Apr 30 21:36:18 CST 2018 User Currently Logged In root tty1 2018-04-17 15:44 root pts/0 2018-04-30 11:55 (118.74.57.231) [root@QUFENGBIN test]# whoson -bash: whoson: command not found [root@QUFENGBIN test]# PATH=$PATH:. [root@QUFENGBIN test]# whoson Mon Apr 30 21:36:53 CST 2018 User Currently Logged In root tty1 2018-04-17 15:44 root pts/0 2018-04-30 11:55 (118.74.57.231)
在 shell 腳本文件的第一行可以放置一行特殊的字符串,告訴操作系統使用哪個 shell 來執行這個文件。因為操作系統在試圖 exec 文件之前檢查該程序的開頭字符串,這些字符讓操作系統不必進行失敗的嘗試。如果腳本的前兩個字符是 #! ,那么系統將這兩個字符后面的那些字符作為用來執行該腳本的命令解釋器的絕對路徑名。它可以是任何程序的路徑名,而不僅僅是 shell 。
fork 和 exec 系統調用:用戶在命令行上輸入一條命令之后,shell 將 fork 一個新的進程,以創建當前 shell 進程的一個副本(子shell)。這個新的進程將試圖 exec(execute,執行)該命令。與 fork 一樣,exec 例程也是由操作系統執行(系統調用)。如果該命令是一個二進制可執行程序,比如編譯好的 C 程序,那么 exec 執行成功,系統調用該可執行程序將新創建的子 shell 覆蓋掉。而如果這個命令是一個 shell 腳本,exec 執行失敗。當 exec 失敗時,將會假設該命令是一個 shell 腳本,子 shell 將執行腳本中的命令。與登錄 shell 期望從命令行讀取輸入不同,子 shell 從文件(shell 腳本)中獲取輸入。所以,如果不具備 shell 腳本文件的執行權限,那么,用戶可以使用 bash 命令來 exec 一個 shell 直接運行該腳本,這樣就可以運行腳本中的命令。
fork ( ~/test/test01.sh) :如果 shell 中包含執行命令,那么子命令并不影響父級的命令,在子命令執行完后再執行父級命令。子級的環境變量不會影響到父級。
說明:fork 是最普通的, 就是直接在腳本里面用 ~/test/test01.sh 來調用 test01.sh 這個腳本。運行的時候開一個 sub-shell 執行調用的腳本,sub-shell 執行的時候, parent-shell 還在。sub-shell 執行完畢后返回 parent-shell.。sub-shell 從 parent-shell 繼承環境變量,但是 sub-shell 中的環境變量不會帶回 parent-shell。
exec (exec ~/test/test01.sh):執行子級的命令后,不再執行父級命令。
說明:exec 與 fork不同,不需要新開一個 sub-shell 來執行被調用的腳本。被調用的腳本與父腳本在同一個 shell 內執行。但是使用 exec 調用一個新腳本以后, 父腳本中 exec 行之后的內容就不會再執行了。這是 exec 和source 的區別。
source (source ~/test/test01.sh):執行子級命令后繼續執行父級命令,同時子級設置的環境變量會影響到父級的環境變量。
說明:與 fork 的區別是不新開一個 sub-shell 來執行被調用的腳本,而是在同一個 shell 中執行。 所以被調用的腳本中聲明的變量和環境變量,都可以在主腳本中得到和使用。
[root@QUFENGBIN test]# chmod u+x test01.sh [root@QUFENGBIN test]# chmod u+x test02.sh [root@QUFENGBIN test]# cat test01.sh #!/bin/bash A=B echo "PID for test01.sh before exec/source/fork:$$" export A echo "test01.sh: \$A is $A" case $1 in exec) echo "using exec…" exec ./test02.sh ;; source) echo "using source…" . ./test02.sh ;; *) echo "using fork by default…" ./test02.sh ;; esac echo "PID for test01.sh after exec/source/fork:$$" echo "test01.sh: \$A is $A" [root@QUFENGBIN test]# cat test02.sh #!/bin/bash echo "PID for test02.sh: $$" echo "test02.sh get \$A=$A from test01.sh" A=C export A echo "test02.sh: \$A is $A" [root@QUFENGBIN test]# ./test01.sh PID for test01.sh before exec/source/fork:1259 test01.sh: $A is B using fork by default… PID for test02.sh: 1260 test02.sh get $A=B from test01.sh test02.sh: $A is C PID for test01.sh after exec/source/fork:1259 test01.sh: $A is B [root@QUFENGBIN test]# ./test01.sh exec PID for test01.sh before exec/source/fork:1261 test01.sh: $A is B using exec… PID for test02.sh: 1261 test02.sh get $A=B from test01.sh test02.sh: $A is C [root@QUFENGBIN test]# ./test01.sh source PID for test01.sh before exec/source/fork:1262 test01.sh: $A is B using source… PID for test02.sh: 1262 test02.sh get $A=B from test01.sh test02.sh: $A is C PID for test01.sh after exec/source/fork:1262 test01.sh: $A is C
什么是環境變量
bash shell 通過一個叫做環境變量(environment variable)的特性來存儲有關 shell 會話和工作環境的信息(這也是它們被稱為環境變量的信息)。這項特性允許你在內存中存儲數據,一遍程序或 shell 在運行的腳本能夠輕松的訪問它們。在 bash shell 中,環境變量分為兩類:全局變量、局部變量。
全局環境變量
全局環境變量對于 shell 會話和所有生成的子 shell 都是可見的。局部變量則只對于創建它們的 shell 可見。Linux 系統在開始 bash 會話前就設置了一些全局環境變量。系統環境變量基本上都使用全大寫字母,以區別于普通用戶的環境變量。要查看全局環境變量,可使用 env 命令或者 printenv 命令。
變量
在 shell 中,shell 參數(shell parameter)與用戶可訪問的某個值相關,有幾種不同的 shell 參數。參數的名字由字母、數字和下劃線組成,常被稱為 shell 變量(shell variable),或者簡稱為變量(variable)。變量名必須以字母或者下劃線開頭,而不能是數字。
用戶創建的變量
用戶命令或賦值的 shell 變量稱為用戶創建的變量(user-created variable)。用戶可以在任何時候修改用戶創建的變量的值,或者將其設置為只讀。還可以將用戶創建的變量變成全局的。全局變量(又稱為環境變量)可以被任何shell和從最初shell創建的其它程序訪問。這里有一個命名約定,即全局變量只使用大寫字母,而其它變量則使用大小寫混合命名。
關鍵字變量
關鍵字 shell 變量(keyword shell variable,簡稱為關鍵字變量)對于 shell 而言,具有特殊的意義,它們的名字一般比較短而且有助于記憶。當用戶啟動 shell 的時候(比如登錄),shell 將從環境中繼承幾個關鍵字變量。HOME 和 PATH 就屬于這樣的變量。
位置參數和特殊參數
位置參數和特殊參數的名字并不像變量名。其中,大多數參數的名字都只由一個字符組成(比如1、?和#等),并且像其他所有變量一樣,在引用它們時一般在其名字前面加上美元符號(如$1、$?和$#)。這些參數的值反映了用戶與 shell 交互的不同方面。無論何時,用戶輸入的一行命令中的每個參數都將成為位置參數(positional parameter)的值。用戶使用位置參數可以訪問命令行參數,在編寫 shell 腳本時將用到這項功能。內置命令 set 可以用來對位置參數賦值。其他經常需要用到的 shell 腳本值,比如最后一次執行的命令名、命令行參數的個數以及最近執行命令的狀態,這些值均保存在特殊參數(special parameter)中。用戶不能對特殊參數賦值。
[root@aminglinux_01 ~]# person=alex [root@aminglinux_01 ~]# echo person person [root@aminglinux_01 ~]# echo $person alex
命令echo $person顯示變量person的值,而不是顯示$person,這是因為不會將$person作為參數傳遞給echo。由于名字開頭出現$,因而shell識別出這是一個變量的名字,并將變量的值代入,同時將該值傳遞給echo。內置命令echo顯示該變量的值,而不是它的名字,然而echo絕不會知道用戶調用它時使用了變量
引用$:如果將開頭的$用單引號引起來,就可以阻止shell代入變量的值。雙引號不能阻止代入(會把引號之間的所有字符代入,包括單引號),而單引號和反斜杠符號\都可以阻止代入。
[root@aminglinux_01 ~]# echo $person alex [root@aminglinux_01 ~]# echo "$person" alex [root@aminglinux_01 ~]# echo '$person' $person [root@aminglinux_01 ~]# echo \$person $person [root@aminglinux_01 ~]# echo "'$person'" 'alex' [root@aminglinux_01 ~]# echo "'\$person'" '$person'
空格符:雙引號不能阻止變量替換,但是可以關閉大多數其他字符的特殊含義。
[root@aminglinux_01 ~]# person="alex and jenny" [root@aminglinux_01 ~]# echo $person alex and jenny [root@aminglinux_01 ~]# person=alex and jenny -bash: and: 未找到命令
當引用一個包含制表符和多個相連空格符的變量時,需要使用引號來保留這些空格。如果沒有將該變量用引號引起來,在將其傳遞給工具之前,shell將把每個空白字符構成的串壓縮成單個空格符:
[root@aminglinux_01 ~]# person="alex and jenny" [root@aminglinux_01 ~]# echo $person alex and jenny [root@aminglinux_01 ~]# echo "$person" alex and jenny
賦值中的路徑名展開:當引用一個包含未被引號引起來的特殊字符的變量時,所有 shell 均將這些字符解釋為特殊字符。
[root@aminglinux_01 test]# ls 1.txt 2.txt alex.1 alex.2 [root@aminglinux_01 test]# memo=alex* [root@aminglinux_01 test]# echo $memo alex.1 alex.2 [root@aminglinux_01 test]# echo "$memo" alex*
PS:語法 $VARIABLE 是 ${VARIABLE} 的特殊情形,后者要更加通用,它將變量名用${}括起來?;ɡㄌ枌⒆兞棵綦x起來。將一個變量和一個字符串連接起來的時候,花括號是必要的:
[root@aminglinux_01 test]# PREF=counter
[root@aminglinux_01 test]# WAY=$PREFclock
[root@aminglinux_01 test]# FAKE=$PREFfeit
[root@aminglinux_01 test]# echo $WAY $FAKE
[root@aminglinux_01 test]# WAY=${PREF}clock
[root@aminglinux_01 test]# FAKE=${PREF}feit
[root@aminglinux_01 test]# echo $WAY $FAKE
counterclock counterfeitBourne Again Shell使用特殊的變量$1、$2、$3直到$9,通過位置參數引用命令行中的參數。如果需要引用第9個以后的參數,就必須使用花括號:${10}。命令的名字保存在$0中。
unset:刪除變量
可以使用內置命令readonly確保某個變量的值不被改變。
[root@aminglinux_01 test]# person=jenny [root@aminglinux_01 test]# echo $person jenny [root@aminglinux_01 test]# readonly person [root@aminglinux_01 test]# person=alex -bash: person: 只讀變量
當不帶參數使用內置命令readonly時,它會顯示所有只讀變量的列表。
[root@aminglinux_01 test]# readonly declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath" declare -ir BASHPID declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")' declare -ir EUID="0" declare -ir PPID="1018" declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor" declare -ir UID="0" declare -r person="jenny"
| 內置命令 declare 和 typeset(這是同一個命令的兩個名字)可以用來設置 shell 變量的屬性和值。下表列出了5種屬性。 | 屬性 | 含義 |
|---|---|---|
| -a | 聲明一個數組變量 | |
| -f | 聲明一個函數名變量 | |
| -i | 聲明一個整型變量 | |
| -r | 聲明變量為只讀,也可使用 readonly | |
| -x | 輸出變量(設置為全局變量),也可用 export |
列出變量屬性:如果不帶任何參數或者選項,那么內置命令 declare 將列出所有 shell 變量。不帶任何參數運行 set 命令,也會得到同樣結果。如果內置命令 declare 帶有選項,但是沒有變量名作為參數,那么該命令將列出所有具有指定屬性集合的 shell 變量。
關鍵字變量既可以通過繼承而來,也可以在 shell 啟動時聲明并初始化。
默認情況下,用戶主目錄就是用戶登錄之后的工作目錄。當用戶創建其賬號時,其主目錄就已經創建下來了,這個文件名存放在 /etc/passwd 中。
[root@aminglinux_01 test]# grep root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin
代字符(~):shell 使用 HOME 的值來展開路徑名,該路徑名使用簡寫形式(代字符 ~ )來表示用戶的主目錄
[root@aminglinux_01 test]# cd [root@aminglinux_01 ~]# pwd /root [root@aminglinux_01 ~]# echo ~ /root
在向 shell 中輸入一個絕對路徑名或者相對路徑名,而不是一個簡單的文件名作為命令時,shell 就會在指定的這個目錄下,用指定文件名查找可執行文件。如果該路徑名對應的文件不存在,shell 將會報告“ command not found ”(命令找不到)錯誤。如果指定文件存在,但是用戶沒有執行權限,或者是用戶沒有 shell 腳本的讀權限和執行權限,shell 將報告“ Permission denied ”(權限禁止)錯誤。
如果使用簡單的文件名作為命令,shell 將搜索某些目錄,以查找用戶想要執行的程序。shell 在幾個目錄中搜索文件,查找與該命令具有相同的名字、且用戶具有執行權限(對于編譯好的程序)或者具有讀權限和執行權限(對于shell 腳本)的文件。shell 變量 PATH 控制著這些搜索路徑。
[root@QUFENGBIN ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/mysql/bin:/usr/local/openssl/bin:/root/bin
變量 MAIL 包含了保存用戶郵件的文件的路徑名。保存用戶郵件的文件就是該用戶的 mailbox ,通常是 /var/spool/mail/name ,其中 name 是用戶的登錄名。如果設置了 MAIL 但是沒有設置 MAILPATH,那么郵件到達 MAIL 指定的文件時,shell 將提醒用戶。
變量 MAILPATH 包含了一個用冒號隔開的文件名列表。如果設置了這個變量,那么當這個列表中的任何一個文件發生改變的時候(比如郵件到達時),shell 都將提醒用戶??梢栽谠摿斜碇腥魏我粋€文件名后面加上一個問號(?),問號后面跟著一條消息。如果有新郵件,shell 就會顯示該消息。它取代了用戶登錄系統時因有郵件而出現的“ you have mail ”消息。
變量 MAILCHECK 規定了shell以多大的額度(以秒計)檢查新郵件。默認值是60秒。如果將該值設置為0,shell 將在顯示每個提示符之前檢查新郵件。
[root@QUFENGBIN ~]# echo $MAIL /var/spool/mail/root [root@QUFENGBIN ~]# echo $MAILPATH [root@QUFENGBIN ~]# echo $MAILCHECK 60
Bourne Again Shell 默認提示符是一個美元符號($)。如果以 root 身份運行 bash ,那么提示符是 # 號。變量 PS1 保存了 shell 用來提示用戶輸入命令的提示符串。當用戶修改 PS1 的值時,用戶的提示符就會發生改變。
[root@QUFENGBIN ~]# echo $PS1 [\u@\h \W]\$ [root@QUFENGBIN ~]# PS1="[\u@\h \W \!]$ " [root@QUFENGBIN ~ 7]$
PS1="[\u@\h \W !]$ "顯示的提示符的格式為[user@host directory event]$,其中user是用戶名,host為本機域名中第1個點號(.)之前的主機名,directory為工作目錄的基名,event為當前命令的事件編號。
[root@QUFENGBIN ~ 11]$ PS1="\h \$" QUFENGBIN $PS1="\@ \u $ " 10:45 AM root $ PS1="\$ " $
| 下表給出了一些 PS1 符號 | 符號 | 在提示符中的顯示 |
|---|---|---|
| \$ | 如果以root身份運行,就顯示為#,否則就是$ | |
| \w | 工作目錄的路徑名 | |
| \W | 工作目錄的基名 | |
| \! | 當前事件(歷史)編號 | |
| \d | 按照“工作日/月/日期”格式顯示的日期 | |
| \h | 機器的主機名,不包括域名 | |
| \H | 機器全名,包括域名 | |
| \u | 當前用戶的用戶名 | |
| \@ | 按照“12小時,AM/PM”格式顯示的當前時間 | |
| \T | 按照12小時制“HH:MM:SS”格式顯示當前時間 | |
| \A | 按照24小時制“HH:MM”格式顯示當前時間 | |
| \t | 按照24小時制“HH:MM:SS”格式顯示當前時間 |
[root@QUFENGBIN ~]# echo $PS2 > [root@QUFENGBIN ~ 17]$ echo 'hello world > !!!' hello world !!! [root@QUFENGBIN ~ 18]$ PS2="next: " [root@QUFENGBIN ~ 19]$ echo "hello world next: 2" hello world 2
[root@QUFENGBIN ~]# echo $PS3 [root@QUFENGBIN ~]# echo $PS4 +
IFS(Internet Field Separator,內部字段分隔符)shell變量指定了在命令行中用來分隔參數的字符,其默認值為空格符、制表符和換行符。無論IFS的值是什么,用戶總可以使用一個或者多個空格符或者制表符分隔命令行中的不同參數,這里假設這些字符并沒有被引用或者轉義。
當為IFS指派字符值的時候,這些字符也可以分隔字段,但是只有在展開的時候才可以這樣。這種命令行解釋方式稱為分詞(word splitting)。
[root@QUFENGBIN ~]# a=x:y:z [root@QUFENGBIN ~]# cat $a cat: x:y:z: No such file or directory [root@QUFENGBIN ~]# IFS=":" [root@QUFENGBIN ~]# cat $a cat: x: No such file or directory cat: y: No such file or directory cat: z: No such file or directory
shell 根據在 IFS 中發現的分隔符劃分命令行中所有被展開的詞。如果不進行展開,就不會進行分詞。
[root@QUFENGBIN ~]# IFS="p" [root@QUFENGBIN ~]# export VAR [root@QUFENGBIN ~]# aa=export [root@QUFENGBIN ~]# echo $aa ex ort
盡管連續出現的多個空格或制表符被視為一個分隔符,但是只要出現別的字段分隔符字段,那么每一次出現就被視為一個分隔符。
使用 CDPATH 變量,用戶可以用一個簡單的文件名作為參數傳遞給內置命令 cd ,就將工作目錄改變到某個目錄,而這個目錄并不是工作目錄的子目錄。
如果沒有設置 CDPATH,而只是指定了一個簡單的文件名作為調用 cd 的參數,那么 cd 將在工作目錄中查找具有與該參數相同的文件名的子目錄。而如果設置了 CDPATH,cd 將在 CDPATH 列表中的目錄搜索具有與該參數相同的文件名的子目錄。如果 cd 找到了一個子目錄,那么該目錄就成為工作目錄。
如果 CDPATH 中沒有包含工作目錄,并且在 CDPATH 中均搜索失敗,那么 cd 將在工作目錄下搜索。如果希望 cd 首先搜索工作目錄,那么可將空字符串作為 CDPATH 的第1項??兆址脙蓚€冒號(::)表示。如果內置命令cd 的參數是一個絕對路徑——以斜杠(/)開頭——則 shell 不考慮 CDPATH 。
| 字符 | 用途 | 字符 | 用途 |
|---|---|---|---|
| 換行符 | 啟動命令的執行 | ; | 分隔命令 |
| () | 將命令分組給子shell執行,或者是標示函數 | & | 在后臺執行命令 |
| | | 管道 | > | 重定向標準輸出 |
| >> | 追加標準輸出 | < | 重定向標準輸入 |
| << | Here文檔 | * | 模糊文件引用中的零個或者多個字符組成的串 |
| ? | 模糊文件引用中的任何單個字符 | \ | 引用后面的字符 |
| ' | 引用字符串,阻止所有替換 | " | 引用字符串,只允許變量替換和命令替換 |
| `...` | 執行命令替換 | [] | 模糊文件引用中的字符類別 |
| $ | 引用某個變量 | .(內置句點) | 執行命令(只在行首) |
| # | 開始一行注釋 | {} | 用來封裝函數體 |
| ;(內置空串) | 返回true | &&(布爾“與”) | 只有左邊的命令成功(返回的退出狀態值為0)才執行右邊的命令 |
| ||(布爾“或”) | 只有左邊的命令失?。ǚ祷胤橇阃顺鰻顟B值)才執行右邊的命令 | !(布爾“非”) | 反轉命令的退出狀態值 |
| $() | 執行命令替換(優先形式) | [] | 計算算術表達式的值 |
進程(process)是一條命令在Linux上的執行。用戶登錄時啟動的shell與其他一樣,也是一條命令,或者進程。當用戶在命令行中輸入一個Linux工具名時,就啟動了一個進程。當用戶運行一個shell腳本的時候,系統創建另一個shell進程,并為腳本的每行命令創建另外的進程。根據用戶調用腳本方式的不同,腳本既可以由當前shell運行,也可由當前shell的一個子shell運行。后一種方式更加普遍。如果用戶輸入一個shell內置命令如cd,此時系統并不會創建進程。
fork系統調用:進程結構類似于文件結構,它也是一個層次式結構,有父進程、子進程甚至根進程(root)。父進程可以創建(fork)子進程,子進程又可以創建其他進程。術語fork,就像道路中的岔道口一樣,將一個進程變成兩個。創建一個新的進程的系統操作例程(或者系統調用)叫做fork。
當系統啟動時,Linux開始執行,它首先啟動init進程。這個進程稱為自發進程(spontaneous process),其PID編號為1。這個進程在進程結構中的地位與文件結構中根目錄的地位相同:它是系統進程和所有用戶進程的祖先。
用于登錄某個虛擬控制器終端或在GUI中運行終端仿真器時所啟動的默認的交互shell,是一個父shell。在CLI提示符后輸入 /bin/bash 命令或其他等效的 bash 命令時,會創建一個新的shell程序。這個shell程序被稱為子shell(child shell)。子shell也擁有CLI提示符,同樣會等待命令輸入。運行shell腳本也能夠創建出子shell。
可以在一行中指定要依次運行的一系列命令。這可以通過命令列表來實現,只需要在命令之間加入分號(;)即可。
在上面的例子中,所有的命令依次執行,不存在任何問題。不過這并不是進程列表。命令列表要想成為進程列表,這些命令必須包含在括號里。
盡管多出來的括號看起來沒有什么太大的不同,但起到的效果確是非同尋常。括號的加入使命令列表變成了進程列表,生成了一個子shell來執行對應的命令。
說明 進程列表是一種命令分組(command grouping)。另一種命令分組是將命令放入花括號中,并在命令列表尾部加上分號(;)。語法為 { command; } 。使用花括號進行命令分組并不會像進程列表那樣創建出子shell。
要想知道是否生成了子shell,得借助一個使用了環境變量的命令。這個命令就是 echo $BASH_SUBSHELL 。如果該命令返回 0 ,就表明沒有子shell。如果返回1 或者其他更大的數字,就表明存在子shell。
歷史機制是一項對C shell的改造功能,它維護了用戶最近發出的命令行(亦稱為事件)的列表。內置命令history顯示了歷史列表的內容
HISTSIZE變量的值決定了在某次會話期間歷史列表保存的事件數目。該值的范圍正常情況下是100到1000。
當從shell中退出時,最近執行的命令將保存在HISTFILE變量指定的文件中(默認是~/.bash_history)。下一次啟動shell時,將用這個文件來初始化歷史列表。變量HISTFILESIZE的值決定了保存在HISTFILE中的歷史行數(不一定與HISTSIZE相同)。HISTSIZE保存了會話期間記憶的事件數目,HISTFILESIZE則保存了會話之間記憶的數目,HISTFILE指定了保存歷史列表的文件。
[root@aminglinux_01 ~]# echo $HISTSIZE #在一次會話期間保存的最大事件數目 1000 [root@aminglinux_01 ~]# echo $HISTFILE #歷史文件的位置 /root/.bash_history [root@aminglinux_01 ~]# echo $HISTFILESIZE #會話之間保存的事件最大數目 1000
事件編號:Bourne Again Shell連續地為每行命令指派了一個事件編號(event number)。如果在PS1中包含“!”,則可以將事件編號作為bash提示符的一部分顯示出來。
[root@aminglinux_01 ~]# echo $PS1 [\u@\h \W]\$ [root@aminglinux_01 ~]# PS1="[\u@\h \W \!]\$" [root@aminglinux_01 ~ 193]$
輸入history命令可以顯示歷史列表中的事件。事件列表按照最早的事件排在列表頂部的順序排列。
[root@aminglinux_01 ~ 194]$history | tail 185 info bash builtin 186 info bash 187 man bash 188 echo $HISTSIZE 189 echo $HISTFILE 190 echo $HISTFILESIZE 191 echo $PS1 192 PS1="[\u@\h \W \!]\$" 193 history 194 history | tail
可以重新執行歷史列表中的任何事件??梢圆捎?種方式來瀏覽、修改和重新執行前面已執行的命令,即使用內置命令fc、使用感嘆號(!)命令以及Readline庫(暫不介紹),該庫使用vi編輯器來編輯和執行事件。
| !!命令重新執行前一個事件,!\$符號表示前一個命令行中最后一個字??梢允褂檬录慕^對編號、相對編號或者事件中包含的文本來引用該事件。所有的事件引用(稱為事件標志符)均以感嘆號(!)開頭。感嘆號后面的一個或多個字符指定某個事件。 事件標志符:事件標志符指定了命令歷史中的某條命令。如下表: | 標志符 | 含義 |
|---|---|---|
| ! | 除非后面緊跟著空格符、換行符、=或者(,否則立即開始某個歷史事件 | |
| !! | 前一個命令 | |
| !n | 歷史列表中編號為n的命令 | |
| !-n | 往前第n條命令 | |
| !string | 最近以string開頭的命令行 | |
| !?string[?] | 包含string的最近命令行,最后的?是可選的 | |
| !# | 當前命令(目前敲入的部分) | |
| !{event} | event為一個事件標示符?;ɡㄌ枌vent與左右的文本隔開。比如,!{-3}3 表示后面跟著3的第3個最近執行的命令 |
[root@aminglinux_01 ~ 205]$ls -l 3.txt -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt [root@aminglinux_01 ~ 206]$!! ls -l 3.txt -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt [root@aminglinux_01 ~ 206]$!205 ls -l 3.txt -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt [root@aminglinux_01 ~ 206]$!-1 ls -l 3.txt -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt [root@aminglinux_01 ~ 206]$history 10 197 fc -l 198 type -a fc 199 ll /usr/bin/fc 200 type -a fc 201 history | tail 202 history 203 history | tail 204 ll 205 ls -l 3.txt 206 history 10 [root@aminglinux_01 ~ 207]$!l ls -l 3.txt -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt [root@aminglinux_01 ~ 208]$!?ll? ll 總用量 8 -rw-r--r--. 1 root root 25 5月 7 13:18 3.txt -rw-------. 1 root root 1418 4月 14 21:55 anaconda-ks.cfg drwxr-xr-x. 2 root root 60 5月 7 15:45 test
| 字標志符(word designator)指定了事件中的某個字或一組字。如下表: | 字標志符 | 含義 |
|---|---|---|
| n | 第n個字。一般情況下,字0就是命令名 | |
| ^ | 第1個字。(緊隨命令名) | |
| $ | 最后那個字 | |
| m-n | 最后那個字從編號m到n的所有字,如果忽略m,那么m默認為0(0-n) | |
| n* | 從第n個到最后那個之間的所有字 | |
| * | 除命令名之外的所有字。與1*相同 | |
| % | 最近的匹配?string?搜索的那個字 |
這些字的編號從0開始(命令行上的第一個字,通常指命令名),接著是1(緊隨命令名的第1個字),直到n(命令行上的最后一個字)。
為了指定前一個事件中某個特定的字,在事件標志符(如!14)后面跟著一個冒號和一個表示該字在這個命令中的標號。如,!14:3指定了事件14中命令名后第3個字。使用一個用連字符隔開的兩個字標志符,可以指定字范圍。
[root@aminglinux_01 ~ 209]$echo 1 2 3 4 5 1 2 3 4 5 [root@aminglinux_01 ~ 210]$echo !209:2 echo 2 2 [root@aminglinux_01 ~ 211]$echo !209:^ echo 1 1 [root@aminglinux_01 ~ 212]$!209:0 !209:$ echo 5 5 [root@aminglinux_01 ~ 213]$echo !209:2-4 echo 2 3 4 2 3 4 [root@aminglinux_01 ~ 214]$!209:0-$ echo 1 2 3 4 5 1 2 3 4 5
如果一個事件只包含了單個命令,那么字編號就與參數編號對應。如果事件包含了多條命令,那么對于第一條之后的命令來說,這種對應關系不再成立。
[root@aminglinux_01 ~ 215]$!209 ; echo 6 7 8 9 echo 1 2 3 4 5 ; echo 6 7 8 9 1 2 3 4 5 6 7 8 9 [root@aminglinux_01 ~ 216]$echo !215:7 echo echo echo [root@aminglinux_01 ~ 217]$echo !215:4-8 echo 4 5 ; echo 6 4 5 6 [root@aminglinux_01 ~ 218]$echo !215:6-7 echo ; echo [root@aminglinux_01 ~ 219]$echo !$ echo echo echo
有時候需要改變正要執行的命令的某些方面。通過在字標志符后面或者事件標志符后面(如果沒有字標志符)放置一個或多個修飾符,就可以修改事件或事件的某個字。每個修飾符前面必須有一個冒號(:)。
替換修飾符:替換修飾符(substitute modifier)要比其他修飾符復雜。下面有個例子:
[root@aminglinux_01 ~ 222]$cad ~/3.txt -bash: cad: 未找到命令 [root@aminglinux_01 ~ 223]$!!:s/cad/cat cat ~/3.txt this is a test test THIS
替換修飾符的語法:[g]s/old/new/
old為原字符串(并非正則表達式),new表示代替old的那個字符串。替換修飾符用new替換old的第1次出現。在s前面放上g將造成全局替換,即將old的所有出現都替換掉。
其他修飾符:除替換修飾符外,修飾符還可以對由事件標志符和可選字標志符選取的事件部分進行簡單的編輯??梢允褂枚鄠€修飾符,彼此之間用冒號(:)隔開。
[root@aminglinux_01 ~ 227]$ls ~/3.txt /root/3.txt [root@aminglinux_01 ~ 228]$!!:p ls ~/3.txt [root@aminglinux_01 ~ 228]$!!:h:p ls ~
| 下表列出了除替換修飾符之外的事件修飾符 | 修飾符 | 功能 |
|---|---|---|
| e(extension) | 刪除掉除文件擴展名之外的所有內容 | |
| h(head) | 刪除路徑名的最后部分 | |
| p(print-not) | 顯示命令,但不要執行 | |
| q(quote) | 引用該替換,以防止對其進行進一步的替換 | |
| r(root) | 刪除文件擴展名 | |
| t(tail) | 刪除掉路徑名中除末尾之外的所有元素 | |
| x | 與q類似,除了單獨引用替換中的每個字 |
shell函數類似于shell腳本,里面存放了一系列可在稍后執行的命令。然而,因為shell將函數存放在物理內存(RAM)而不是磁盤文件中,所以,shell訪問函數的速度要比訪問腳本的速度快得多。shell還對函數預處理(解析),因此其啟動速度也要比腳本快得多。最后,shell函數的執行和調用是在同一個shell中進行的。
shell函數的聲明可以放在~/.bash_profile初始化文件中,或者放在使用該函數的腳本中,又或者直接放在命令行??梢允褂脙戎妹顄nset刪除函數。一旦用戶注銷,shell將不再保持這些函數。
PS:如果某個shell變量和函數名稱相同,那么可用unset刪除shell變量。再次用相同的命令,就會刪除這個函數。
聲明一個shell函數的語法如下:
[function] function_name()
{
commands
)
以上就是Shell的本質以及用法是怎樣的,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。