Shell基本語法知識

Shell 就是一個命令解釋器,他的做用就是解釋執行用戶輸入的命令及程序等,用戶每輸入一條命令,Shell 就解釋一條。這種從鍵盤一輸入命令,就能夠當即獲得迴應的對話方式,就稱爲交互的方式。python

當命令或程序不在命令下執行,而是經過一個程序來執行時,該程序就稱爲Shell 腳本。shell

Shell 腳本相似於DOS系統下的批處理程序(早期擴展名通常爲「 *.bat」)。ubuntu

用戶可在Shell 腳本中敲入一系列的命令及語句組合,這些命令,變量和流程控制語句等有機地結合起來,就造成了一個功能強大的Shell 腳本。vim

查看當前shell 類型bash

echo $SHELL /bin/bash

因爲歷史緣由,UNIX系統上有不少種Shell:
1.sh(Bourne Shell):由Steve Bourne開發,各類UNIX系統都配有sh。函數

which sh /bin/sh

 

2.csh(C Shell):由Bill Joy開發,隨BSD UNIX發佈,它的流程控制語句很像C語言,支持不少Bourne Shell所不支持的功能:做業控制,命令歷史,命令行編輯。
ui

3.ksh(Korn Shell):由David Korn開發,向後兼容sh的功能,而且添加了csh引入的新功能,是目前不少UNIX系統標準配置的Shell,spa

  在這些系統上/bin/sh每每是指向/bin/ksh的符號連接。命令行

4.tcsh(TENEX C Shell):是csh的加強版本,引入了命令補全等功能,在FreeBSD、MacOS X等系統上替代了csh。code

5.bash(Bourne Again Shell):由GNU開發的Shell,主要目標是與POSIX標準保持一致,同時兼顧對sh的兼容,bash從csh和ksh借鑑了不少功能,

  是各類Linux發行版標準配置的Shell,在Linux系統上/bin/sh每每是指向/bin/bash的符號連接。雖然如此,bash和sh仍是有不少不一樣的,

  一方面,bash擴展了一些命令和參數,另外一方面,bash並不徹底和sh兼容,有些行爲並不一致,因此bash須要模擬sh的行爲:

  當咱們經過sh這個程序名啓動bash時,bash能夠僞裝本身是sh,不認擴展的命令,而且行爲與sh保持一致。

vim /etc/passwd 其中最後一列顯示了用戶對應的shell類型 root:x:0:0:root:/root:/bin/bash nobody:x:65534:65534:nobody:/nonexistent:/bin/sh syslog:x:101:103::/home/syslog:/bin/false itcast:x:1000:1000:itcast,,,:/home/itcast:/bin/bash ftp:x:115:125:ftp daemon,,,:/srv/ftp:/bin/false

守護進程不須要shll類型

一個簡單的shell 腳本 t1.sh

ls pwd ls

執行:

/bin/bash t1.sh t1.sh /home/gec/myshare/Shell t1.sh

 

 用戶在命令行輸入命令後,通常狀況下Shell會fork並exec該命令,可是Shell的內建命令例外執行內建命令至關於調用Shell進程中的一個函數並不建立新的進程。之前學過

cd、alias、umask、exit等命令便是內建命令凡是用which命令查不到程序文件所在位置的命令都是內建命令,內建命令沒有單獨的man手冊

要在man手冊中查看內建命令, 應該

$ man bash-builtins

export、shift、if、eval、[、for、while等等。內建命令雖然不建立新的進程,但也會有Exit Status,一般也用0表示成功非零表示失敗,

雖然內建命令不建立新的進程,但執行結束後也會有一個狀態碼,也能夠用特殊變量$?讀出。

gec@ubuntu:~$ echo $?
0 gec@ubuntu:~$ ls hahha ls: cannot access 'hahha': No such file or directory gec@ubuntu:~$ echo $?
2

 

好比修改  .bashrc 文件PATH=空

gec@ubuntu:~/myshare/Shell$ cd gec@ubuntu:~$ ls .bashrc .bashrc gec@ubuntu:~$ vi .bashrc
ATH=/usr/local/arm/5.4.0/usr/bin:$PATH export PATH=/usr/local/Qt-Embedded-5.7.0/bin:$PATH export PATH=

 

結果

ls 命令將很差使,而cd 命令仍然ok,由於 cd 是內建命令

ls bash: ls: No such file or directory

 

簡單的shell 腳本

#! /bin/sh cd .. ls

Shell腳本中用#表示註釋,至關於C語言的//註釋。但若是#位於第一行開頭,而且是#!(稱爲Shebang)則例外,它表示該腳本使用後面指定的解釋器/bin/sh解釋執行

若是把這個腳本文件加上可執行權限而後執行:

chmod a+x test.sh ./test.sh

運行python 加上python 解釋器路徑

#!/usr/bin/python print("hello world")

 

Shell會fork一個子進程並調用exec執行./test.sh這個程序exec系統調用應該把子進程的代碼段替換成./test.sh程序的代碼段,並從它的_start開始執行

然而test.sh是個文本文件,根本沒有代碼段和_start函數,怎麼辦呢?其實exec還有另一種機制,若是要執行的是一個文本文件,

而且第一行用Shebang指定了解釋器,則用解釋器程序的代碼段替換當前進程,而且從解釋器的_start開始執行

而這個文本文件被看成命令行參數傳給解釋器。所以,執行上述腳本至關於執行程序

$ /bin/sh ./test.sh

若是將命令行下輸入的命令用()括號括起來,那麼也會fork出一個子Shell執行小括號中的命令,一行中能夠輸入由分號;隔開的多個命令,

好比: 執行完命令 工做目錄仍然在 當前目錄

$ (cd ..;ls -l)

和上面兩種方法執行Shell腳本的效果是相同的,cd ..命令改變的是子Shell的PWD,而不會影響到交互式Shell。然而命令

$ cd ..;ls -l

則有不一樣的效果,cd ..命令是直接在交互式Shell下執行的,改變交互式Shell的PWD,然而這種方式至關於這樣執行Shell腳本:

$ source ./test.sh

或者

$ . ./test.sh

source或者.命令是Shell的內建命令,這種方式也不會建立子Shell,而是直接在交互式Shell下逐行執行腳本中的命令。

 

以下面腳本的運行結果:

#!/bin/sh
pwd cd ../
pwd

結果

gec@ubuntu:~/myshare/Shell$ ./t1.sh 
/home/gec/myshare/Shell /home/gec/myshare gec@ubuntu:~/myshare/Shell$ pwd
/home/gec/myshare/Shell gec@ubuntu:~/myshare/Shell$ source t1.sh 
/home/gec/myshare/Shell /home/gec/myshare gec@ubuntu:~/myshare$ pwd
/home/gec/myshare

 


 變量

gec@ubuntu:~/myshare/Shell$ ABC=123 gec@ubuntu:~/myshare/Shell$ echo $ABC 123

注意,Shell變量不須要明肯定義類型,事實上Shell變量的值都是字符串,好比咱們定義VAR=45,其實VAR的值是字符串

45而非整數。Shell變量不須要先定義後使用,若是對一個沒有定義的變量取值,則值爲空字符串。

按照慣例,Shell變量由全大寫字母加下劃線組成,有兩種類型的Shell變量:

1.環境變量

環境變量可以從父進程傳給子進程,所以Shell進程的環境變量能夠從當前Shell進程傳給fork出來的子進程。

printenv命令能夠顯示當前Shell進程的環境變量。

2.本地變量

只存在於當前Shell進程,用set命令能夠顯示當前Shell進程中定義的全部變量(包括本地變量和環境變量)和函數。

環境變量是任何進程都有的概念,而本地變量是Shell特有的概念。在Shell中,環境變量和本地變量的定義和用法類似。在Shell中定義或賦值一個變量:

itcast$ VARNAME=value

注意等號兩邊都不能有空格不然會被Shell解釋成命令和命令行參數。一個變量定義後僅存在於當前Shell進程,它是本地變量,

export命令能夠把本地變量導出爲環境變量,定義和導出環境變量一般能夠一步完成:

itcast$ export VARNAME=value

也能夠分兩步:

itcast$ VARNAME=value itcast$ export VARNAME

用unset命令能夠刪除已定義的環境變量或本地變量。

itcast$ unset VARNAME

 

gec@ubuntu:~/myshare/Shell$ ABC=123 gec@ubuntu:~/myshare/Shell$ echo $ABC 123 gec@ubuntu:~/myshare/Shell$ env | grep ABC gec@ubuntu:~/myshare/Shell$ export XYZ=456 gec@ubuntu:~/myshare/Shell$ env | grep XYZ XYZ=456 gec@ubuntu:~/myshare/Shell$ export ABC gec@ubuntu:~/myshare/Shell$ env | grep ABC ABC=123
gec@ubuntu:~/myshare/Shell$ unset ABC
gec@ubuntu:~/myshare/Shell$ echo $ABC
 
 
gec@ubuntu:~/myshare/Shell$
 

 

文件名代換(Globbing):* ? []

這些用於匹配的字符稱爲通配符(Wildcard),具體以下:

通配符 * 匹配0個或多個任意字符 ? 匹配一個任意字符 [若干字符] 匹配方括號中任意一個字符的一次出現 $ ls /dev/ttyS* $ ls ch0?.doc $ ls ch0[0-2].doc $ ls ch[012] [0-9].doc

 

gec@ubuntu:~/myshare$ ls ?.c a.c b.c gec@ubuntu:~/myshare$ ls ???.c 217.c gec@ubuntu:~/myshare$ ls *.c 217.c  a.c  b.c  demo1.c  hello.c  pthread.c  vi.c write_test.c gec@ubuntu:~/myshare$
gec@ubuntu:~/myshare/Shell$ ls t1.sh  t2.py  tt.sh gec@ubuntu:~/myshare/Shell$ ls t[123456789].sh t1.sh gec@ubuntu:~/myshare/Shell$ touch t11.sh gec@ubuntu:~/myshare/Shell$ ls t[123456789].sh t1.sh gec@ubuntu:~/myshare/Shell$ ls t[123456789][123456789].sh t11.sh gec@ubuntu:~/myshare/Shell$ ls t[1-9].sh t1.sh

 

注意,Globbing所匹配的文件名是由Shell展開的,也就是說在參數還沒傳給程序以前已經展開了,

好比上述ls ch0[012].doc命令,若是當前目錄下有ch00.doc和ch02.doc,則傳給ls命令的參數其實是這兩個文件名,而不是一個匹配字符串。


命令代換:‘或 $()

’‘’反引號括起來的也是一條命令,Shell先執行該命令,而後將輸出結果馬上代換到當前命令行中。例如定義一個變量存放date命令的輸出:

itcast$ DATE=`date` itcast$ echo $DATE 命令代換也能夠用$()表示: itcast$ DATE=$(date)
gec@ubuntu:~/myshare/Shell$ ABC=`ls -l` gec@ubuntu:~/myshare/Shell$ echo $ABC total 1 -rwxrwxrwx 1 root root 0 May 25 18:33 t11.sh -rwxrwxrwx 1 root root 26 May 25 17:49 t1.sh -rwxrwxrwx 1 root root 41 May 25 17:35 t2.py -rwxrwxrwx 1 root root 0 May 25 18:32 tt.sh

 

 算術代換:$(())

用於算術計算,(())中的Shell變量取值將轉換成整數,一樣含義的[]等價例如:

itcast$ VAR=45 itcast$ echo $(($VAR+3)) $(())中只能用+-*/和()運算符,而且只能作整數運算。 $[base#n],其中base表示進制,n按照base進制解釋,後面再有運算數,按十進制解釋echo $[2#10+11] echo $[8#10+11] echo $[10#10+11]

 

gec@ubuntu:~/myshare/Shell$ VAR=45 gec@ubuntu:~/myshare/Shell$ echo $(($VAR+3)) 48 gec@ubuntu:~/myshare/Shell$ echo VAR+3 VAR+3 gec@ubuntu:~/myshare/Shell$ echo $VAR+3
45+3
gec@ubuntu:~/myshare/Shell$ echo $[2#1010] 10 gec@ubuntu:~/myshare/Shell$ echo $[8#10] 8 gec@ubuntu:~/myshare/Shell$ echo $[16#10] 16 gec@ubuntu:~/myshare/Shell$ echo $[16#10 + 11] 27

 

轉義字符

和C語言相似,\在Shell中被用做轉義字符,用於去除緊跟其後的單個字符的特殊意義(回車除外),換句話說,緊跟其後的字符取字面值。

例如:

itcast$ echo $SHELL /bin/bash itcast$ echo \$SHELL $SHELL itcast$ echo \\ \

 

好比建立一個文件名爲「$ $」的文件能夠這樣:

itcast$ touch \$\ \$

gec@ubuntu:~/myshare/Shell$ touch $$ gec@ubuntu:~/myshare/Shell$ ls
19265  t11.sh  t1.sh  t2.py  tt.sh gec@ubuntu:~/myshare/Shell$ touch \$\$ gec@ubuntu:~/myshare/Shell$ ls $$ 19265  t11.sh  t1.sh  t2.py  tt.sh

 

還有一個字符雖然不具備特殊含義,可是要用它作文件名也很麻煩,就是-號。若是要建立一個文件名以-號開頭的文件,這樣是不行的:

itcast$ touch -hello touch: invalid option -- h Try `touch --help' for more information.
即便加上\轉義也仍是報錯: itcast$ touch \-hello touch: invalid option -- h Try `touch --help' for more information.

 

由於各類UNIX命令都把-號開頭的命令行參數看成命令的選項,而不會看成文件名若是非要處理以-號開頭的文件名,能夠有兩種辦法:

itcast$ touch ./-hello 或者 itcast$ touch -- -hello

 

\還有一種用法,在\後敲回車表示續行,Shell並不會馬上執行命令,而是把光標移到下一行,給出一個續行提示符>,等待用戶繼續輸入,

最後把全部的續行接到一塊兒看成一個命令執行。例如:

itcast$ ls \ > -l (ls -l命令的輸出)

 

單引號

和C語言不同,Shell腳本中的單引號和雙引號同樣都是字符串的界定符,而不是字符的界定符。單引號用於保持引號內全部字符的字面值

即便引號內的\和回車也不例外,可是字符串中不能出現單引號。若是引號沒有配對就輸入回車,Shell會給出續行提示符,要求用戶把引號配上對。例如:

itcast$ echo '$SHELL' $SHELL itcast$ echo 'ABC\(回車)
> DE'(再按一次回車結束命令)
ABC\ DE

 

雙引號

被雙引號用括住的內容,將被視爲單一字串。它防止通配符擴展,但容許變量擴展。這點與單引號的處理方式不一樣

itcast$ DATE=$(date) itcast$ echo "$DATE" itcast$ echo '$DATE'

 

gec@ubuntu:~/myshare/Shell$ echo "$SHELL"
/bin/bash gec@ubuntu:~/myshare/Shell$ echo "$SHELL + 123"
/bin/bash + 123
相關文章
相關標籤/搜索