請註明文章來源:http://blog.zjiecode.com/2019/04/15/shell/html
shell 俗稱叫作殼,計算機的殼層,和內核是相對的,用於和用戶交互,接收用戶指令,調用相應的程序。 linux
所以,把shell分爲2大類git
也就是用戶使用GUI和計算機核交互的shell,好比Windows下使用最普遍的Windows Explorer(Windows資源管理器),Linux下的X Window,以及各類更強大的CDE、GNOME、KDE、 XFCE。github
他們都是GUI Shell。正則表達式
也就是經過命令行和計算機交互的shell。 Windows NT 系統下有 cmd.exe(命令提示字符)和近年來微軟大力推廣的 Windows PowerShell。 Linux下有bash / sh / ksh / csh/zsh等 通常狀況下,習慣把命令行shell(CLI shell)直接稱作shell,之後,若是沒有特別說明,shell就是指 CLI shell,後文也是主要講Linux下的 CLI shell。shell
根據交互方式的不同,命令行式shell(CLI shell),又分爲交互式shell和非交互式shell。編程
交互式模式就是shell等待你的輸入,而且執行你提交的命令,而後立刻給你反饋。這種也是咱們大多數時候使用的。segmentfault
非交互式shell,就是把shell放在寫在一個文件裏面,執行的時候,不與用戶交互,從前日後依次執行,執行到文件結尾時,shell也就終止了。數組
在Linux下 ,各類shell百花齊放,種類繁多,不一樣的shell,也有不一樣的優缺點。 咱們要查看當前系統下支持的shell,能夠讀取/etc/shells文件。bash
Bourne Again Shell 用來替代Bourne shell,也是目前大多數Linux系統默認的shell。
Bourne Shell 是一個比較老的shell,目前已經被/bin/bash所取代,在不少linux系統上,sh已是一個指向bash的連接了。 下面是CentOS release 6.5 的系統
C shell 使用的是「類C」語法,csh是具備C語言風格的一種shell,tcsh是加強版本的csh,目前csh已經不多使用了。
最先,bash交互體驗很好,csh做爲非交互式使用很爽,ksh就吸收了2者的優勢。
zsh網上說的目前使用的人不多,可是感受使用的人比較多。 zsh自己是不兼容bash的,可是他可使用仿真模式(emulation mode)來模擬bash等,基本能夠實現兼容。 在交互式的使用中,目前不少人都是zsh,由於zsh擁有很強大的提示和插件功能,炫酷吊炸天。推薦在終端的交互式使用中使用zsh,再安利一個插件Oh My Zsh 其實我我的的理解是,在終端中使用shell,基本上只是調用各類命令,好比:curl cat ls等等,基本不會使用到zsh的編程,因此終端中使用zsh是能夠的。可是在寫shell腳本的時候,須要考慮兼容性, 最主流的仍是bash shell,因此,後文咱們介紹的shell腳本也是bash shell的。
#!/bin/bash echo "Hello World !"
#!:是一個特殊的標記,代表使用啥解釋器來執行,好比這裏使用了:/bin/bash 來執行這個腳本。 #:只用一個#,就是註釋 echo:輸出 咱們把上面的腳本保存成一個文件, 1.sh 後面的這個sh是shell腳本的擴展名。 而後要怎嚒來執行呢?執行一個shell腳本有不少種方式:
這個是由於沒有這個腳本沒有執行權限,運行 chmod a+x 1.sh 加上執行權限便可。 這裏順帶說一下,爲啥直接運行1.sh不行呢?由於他默認是去PATH裏面找程序,當前目錄,通常都不在PATH裏面。因此直接運行1.sh就回報找不到文件。
根據測試,#!/bin/bash 的標記,只是針對第二種方式 ./xxx.sh的方式有效。本文中代碼,第一行均爲這個標記,爲了節約篇幅,已經省略.
執行並獲取返回結果,有點相似JavaScript 的eval函數。
#!/bin/bash dt=`date` #反引號內的字符串會看成shell執行 ,而且返回結果。 echo "dt=${dt}"
shell的使用比較簡單,就像這樣,而且沒有數據類型的概念,全部的變量均可以當成字符串來處理:
#!/bin/bash myName="tom" youName="cat"
不須要申明,直接寫就能夠了,可是有幾個點須要特別注意:
使用變量
ABC="tom" echo $ABC #使用變量前面加$美圓符號 echo "ABC=$ABC" #能夠直接在字符串裏面引用 echo "ABC=${ABC}" #可是建議把變量名字用{}包起來
只讀變量
ABC="tom" echo "ABC=${ABC}" readOnly ABC #設置只讀 ABC="CAT" #會報錯,由於設置了只讀,不能修改
刪除變量
ABC="tom" echo "ABC=${ABC}" unset ABC #刪除 echo "ABC=$ABC" echo "ABC=${ABC}"
從這個例子當中,咱們也發現,使用一個不存在的變量,shell不會報錯,只是看成空來處理。
使用字符串
NAME="tom" A=my #你甚至能夠不用引號,可是字符串當中不能有空格,這種方式也不推薦 B='my name is ${NAME}' #變量不會被解析 C="my name is ${NAME}" #變量會解析 echo $A echo $B echo $C
執行結果
咱們能夠發現,這個字符串的單雙號和PHP的處理很是相似,單引號不解析變量,雙引號能夠解析變量。可是均可以處理轉義符號。
A='my\nname\nis\ntom' B="my\nname\nis\ntom" echo $A echo $B
執行結果
拼接字符串 其實shell拼接字符串,大概就是2種
NAME="TOM" # 使用雙引號拼接 echo "hello, "$NAME" !" #直接寫在一塊兒,沒有字符串鏈接符 echo "hello, ${NAME} !" #填充模版 # 使用單引號拼接 echo 'hello, '$NAME' !' #直接寫在一塊兒,沒有字符串鏈接符 echo 'hello, ${NAME} !' #上面已經提升過,單引號裏面的變量是不會解析的
強大的字符串處理 shell中簡單的處理字符串,能夠直接使用各類標記,只是比較難記憶,要用的時候,能夠查一下。
ABC="my name is tom,his name is cat" echo "字符串長度=${#ABC}" # 取字符串長度 echo "截取=${ABC:11}" # 截取字符串, 從11開始到結束 echo "截取=${ABC:11:3}" # 截取字符串, 從11開始3個字符串 echo "默認值=${XXX-default}" #若是XXX不存在,默認值是default echo "默認值=${XXX-$ABC}" #若是XXX不存在,默認值是變量ABC echo "從開頭刪除最短匹配=${ABC#my}" # 從開頭刪除 my 匹配的最短字符串 echo "從開頭刪除最長匹配=${ABC##my*tom}" # 從開頭刪除 my 匹配的最長字符串 echo "從結尾刪除最短匹配=${ABC%cat}" # 從結尾刪除 cat 匹配的最短字符串 echo "從結尾刪除最長匹配=${ABC%%,*t}" # 從結尾刪除 ,*t 匹配的最長字符串 echo "替換第一個=${ABC/is/are}" #替換第一個is echo "替換全部=${ABC//is/are}" #替換全部的is
運行結構
這裏只是介紹了比較經常使用的一些字符串處理,實際shell支持的還有不少。
Bash Shell 也是支持數組的,與絕大部分語言同樣,數組下標從0開始。不過須要注意的是,它只支持一維數組。 定義一個數組,用小括號闊氣來,當中用「空格」分割,就像下面這樣:
array=("item0" "item1" "item2")
也能夠根據下標來定義元素
array[0]="new_item0" array[1]="new_item1" array[2]="new_item2" array[4]="new_item4" #數組下標能夠是不連續的
讀取數組元素,和變量相似
echo ${array[0]} echo "array[0]=${array[0]}"
獲取數組全部的元素
echo "數組的元素爲: ${array[*]}" echo "數組的元素爲: ${array[@]}"
獲取數組的長度
echo "數組的長度爲: ${#array[*]}" echo "數組的長度爲: ${#array[@]}"
在上文中,其實咱們已經到屢次,就是:echo 「字符串」 來輸出,一個很簡單的例子
echo "Hello world!"
若是當中包含特殊符號,可使用轉義等:
echo "Hello \nworld!" echo "\"Hello\"" echo '"Hello"' #固然,也能夠這樣,單引號不轉義,上文提到過 echo `date` #打印執行date的結果 echo -n "123" #加-n 表示不在末尾輸出換行 echo "456" echo -e "\a處理特殊符號" #-e 處理特殊符號
-n 讓echo輸出結束之後,在默認不輸出換行符 -e 讓echo處理特殊符號,好比:
符號 | 做用 |
---|---|
\a | 發出警告聲 |
\b | 刪除前一個字符 |
\c | 後不加上換行符號 |
\f | 換行但光標仍舊停留在原來的位置 |
\n | 換行且光標移至行首 |
\r | 光標移至行首,但不換行 |
\t | 插入tab |
上面的特殊符號,寫到mac的shell腳本里面要注意,執行的時候,要用bash執行纔有效 ,sh無效。 |
固然,你也能夠玩一點更有趣的,就是咱們隨時在終端中看到的五光十色的文字:
echo -e "\033[31m 紅色前景 \033[0m 缺省顏色" echo -e "\033[41m 紅色背景 \033[0m 缺省顏色"
其中 \033[是一個特殊標記,表示終端轉義開始, 31m表示使用紅色字體,你也可使用其餘顏色,[30-39]是前景顏色,[40-49]是背景顏色。 \033[0m回覆到缺省設置 還能夠有一些其餘的動做
echo -e "\033[2J" #清除屏幕 echo -e "\033[0q" #關閉全部的鍵盤指示燈 echo -e "\033[1q" #設置"滾動鎖定"指示燈(Scroll Lock) echo -e "\033[2q" #設置"數值鎖定"指示燈(Num Lock) echo -e "\033[1m" #設置高亮度 echo -e "\033[4m" #下劃線 echo -e "\033[7m" #反顯 echo -e "\033[y;xH" #設置光標位置
其餘更多的特殊碼請自行查詢。
有輸出,必然有輸入,read命令接收標準輸入的輸入。
read name echo "my name is ${name}"
可使用-p給一個輸入提示
read -p "please input your name:" name echo "my name is ${name}"
若是沒有指定輸入的變量,會把輸入放在環境標量REPLY中
read -p "please input your name:" echo "my name is ${REPLY}"
計時輸入,若是一段時間沒有輸入 ,就直接返回,使用-t 加時間
read -t 3 -p "please input your name in 3 senconds:" name
指定輸入字符個數,使用-n ,後面的是輸入字符個數
read -n 1 -p "Are you sure [Y/N]?" isYes
默讀(輸入再也不監視器上顯示),加一個-s參數。
read -s -p "Enter your password:" password
echo已經比較強大,可是有的時候,咱們須要用到字符串模版輸出,printf就比較好用了,他相似C裏面的printf程序。 語法是:printf format-string [arguments...] 好比咱們要輸出一個表格
printf "%-10s %-8s %-4s\n" 姓名 性別 體重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 printf "%-10s %-8s %-4.2f\n" 楊過 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
運行結果
%s %c %d %f都是格式替代符 %-10s 指一個寬度爲10個字符(-表示左對齊,沒有則表示右對齊),至少顯示10字符寬度,若是不足則自動以空格填充,超過不限制。 %-4.2f 指格式化爲小數,其中.2指保留2位小數。
大多數 UNIX 系統命令從你的終端接受輸入並將所產生的輸出發送回到您的終端。一個命令一般從一個叫標準輸入的地方讀取輸入,默認狀況下,這剛好是你的終端。一樣,一個命令一般將其輸出寫入到標準輸出,默認狀況下,這也是你的終端。
命令 | 做用 |
---|---|
command > file | 將輸出重定向到 file。 |
command < file | 將輸入重定向到 file。 |
command >> file | 將輸出以追加的方式重定向到 file。 |
n > file | 將文件描述符爲 n 的文件重定向到 file。 |
n >> file | 將文件描述符爲 n 的文件以追加的方式重定向到 file。 |
n >& m | 將輸出文件 m 和 n 合併。 |
n <& m | 將輸入文件 m 和 n 合併。 |
<< tag | 將開始標記 tag 和結束標記 tag 之間的內容做爲輸入。 |
須要注意的是文件描述符 0 一般是標準輸入(STDIN),1 是標準輸出(STDOUT),2 是標準錯誤輸出(STDERR)。
輸出到文件
echo "test">text.txt #直接輸出 echo "test">>text.txt #追加在text.txt後面
重定向輸入
read a <<EOF "測試" EOF echo "a=$a"
來個比較過度的
cat < 1.sh > text.txt
把1.sh文件的內容出入到cat,而後cat在輸出到text.txt中,至關於,把1.sh的內容輸出到text.txt中了
還有一種用法,把標準錯誤直接輸出到標準輸出,而且輸出到文件file
command > file 2>&1
/dev/null 文件 這個是一個特殊文件,他是一個黑洞,寫入到它的內容都會被丟棄,若是咱們不關心程序的輸出,能夠這樣
command > /dev/null 2>&1
和其餘語言同樣,shell也有條件判斷
單分支:
if condition then command1 command2 ... fi
雙分支:
if condition then command1 command2 ... else command fi
多分支:
if condition1 then command1 elif condition2 then command2 else commandN fi
好比
if [ "2" == "2" ]; then # "2" 的2邊都有空格,不能省略 ,寫在一行,條件後面加一個分號 echo "2==2" else echo "2!=2" fi
須要特別注意:[ "2" == "2" ] 其中的"=="兩邊都有空格,不能省略,不然結果不正確。 判斷普通文件是否存在
if [ -f "1.sh" ]; then # 判斷一個普通文件是否存在 echo "1.sh 存在" fi
判斷目錄是否存在
if [ -d "1.sh" ]; then # 判斷一個目錄是否存在 echo "1.sh 存在" fi
判斷字符串長度爲0
a="" if [ -z $a ]; then echo "a爲空" fi
在shell中,有幾個符號要很是注意,用的也比較多,不要搞混了,搞混了,邏輯運算很容易出錯
命令組 括號中的命令將會新開一個子shell順序執行,因此括號中的變量不可以被腳本餘下的部分使用。括號中多個命令之間用分號隔開,最後一個命令能夠沒有分號,各命令和括號之間沒必要有空格。
a="123" (echo "123";a="456";echo "a=$a") echo "a=$a
命令替換 發現了$(cmd)結構,便將$(cmd)中的cmd執行一次,獲得其標準輸出,再將此輸出放到原來命令。
用於初始化數組 如:array=(a b c d)
a=$((4+5)) echo "a=$a"
a=5 ((a++)) echo "a=$a"
if ((1+1>1));then echo "1+1>1" fi
用於字符串比較 須要注意,用於字符串比較,運算符只能是 ==和!=,須要注意,運算符號2邊必須有空格,否則結果不正確!!!好比:
if [ "2" == "2" ]; then # "2" 的2邊都有空格,不能省略 echo "2==2" else echo "2!=2" fi
用於整數比較 須要注意,整數比較,只能用-eq,-gt這種形式,不能直接使用大於(>)小於(<)符號。只能用於整形。
if [ 2 -eq 2 ]; then echo "2==2" else echo "2!=2" fi
符號表
符號 | 運算 |
---|---|
-eq | 等於 |
-ne | 不等於 |
-gt | 大於 |
-ge | 大於等於 |
-lt | 小於 |
-le | 小於等於 |
多個邏輯組合 -a 表示and 與運算 -o 表示or 或運算
if [ "2" == "2" -a "1" == "1" ]; then #注意,在這裏,不能是[ "2" == "2" ] -a [ "1" == "1" ] 會報錯 echo "ok" fi
[[是 bash 程序語言的關鍵字。並非一個命令,[[ ]] 結構比[ ]結構更加通用。
if [[ "123" == 12* ]]; then #右邊是正則不須要引號 echo "ok" fi
if [[ 2.1 > 1.1 ]]; then echo "ok" fi
if [[ 1.1 > 1.1 ]] || [[ 1.1 == 1.1 ]]; then echo "ok" fi
touch new_{1..5}.txt #建立new_1.txt new_2.txt new_3.txt new_4.txt new_5.txt 5個文件
語法格式爲:
for a in "item1" "item2" "item3" do echo $a done
輸出當前目錄下 .sh結尾的文件
for a in `ls ./` do if [[ $a == *.sh ]] then echo $a fi done
語法
while condition do command done
咱們要輸出1-10000
int=1; while(($int<=10000)) do echo $int ((int++)) done
語法
until condition do command done
用法相似,這裏再也不贅述。 循環中 continue命令與break做用和其餘語言中相似。
case和其餘語言switch類型,多分支,選擇一個匹配。匹配發現取值符合某一模式後,其間全部命令開始執行直至 ;;,有點類型Java的break。若是無一匹配模式,使用星號 * 捕獲該值,再執行後面的命令。
echo '輸入 1 到 4 之間的數字:' echo '你輸入的數字爲:' read aNum case $aNum in 1) echo '你選擇了 1' ;; 2) echo '你選擇了 2' ;; 3) echo '你選擇了 3' ;; 4) echo '你選擇了 4' ;; *) echo '你沒有輸入 1 到 4 之間的數字' ;; esac
shell也能夠用戶定義函數,而後在shell腳本中能夠隨便調用。 注意:全部函數在使用前必須定義。這意味着必須將函數放在腳本開始部分,直至shell解釋器首次發現它時,纔可使用。調用函數僅使用其函數名便可。 語法格式以下:
[function] funname() { cmd.... [return int] }
一個最簡單的函數
Line(){ echo "--------分割線--------" } echo "123" Line echo "456"
在Shell中,調用函數時能夠向其傳遞參數。 在函數體內部,經過 $n 的形式來獲取參數的值,例如,$1表示第一個參數,$2表示第二個參數... 調用的時候 ,函數名,參數直接用空格分割開。 帶參數的函數示例:
out(){ echo "1-->$1" echo "2-->$2" } out 1 2 #調用的以後
還有一些其餘的特殊符號須要注意
符號 | 做用 |
---|---|
$# | 傳遞到腳本的參數個數 |
$* | 以一個單字符串顯示全部向腳本傳遞的參數 |
$$ | 腳本運行的當前進程ID號 |
$! | 後臺運行的最後一個進程的ID號 |
$@ | 與$*相同,可是使用時加引號,並在引號中返回每一個參數。 |
$? | 顯示最後命令的退出狀態。0表示沒有錯誤,其餘任何值代表有錯誤。 |
因此咱們能夠寫一個代碼參數,返回值的函數
out(){ echo "所有參數$*" for item in $* do echo "$item" done return $# #這類返回參數個數,返回值必須是整數 } out this is perfect echo "函數返回值:$?"
咱們能夠在執行 Shell 腳本時,向腳本傳遞參數,腳本內獲取參數的格式爲:$n。n 表明一個數字,1 爲執行腳本的第一個參數,2 爲執行腳本的第二個參數,以此類推…… 除了參數可使用特殊符號,也可使用上文中函數所使用的特殊符號,這裏再也不贅述
echo "執行的文件名:$0"; echo "所有參數:$*" echo "參數個數:$#" echo "第一個參數爲:$1"; echo "第二個參數爲:$2"; echo "第三個參數爲:$3";
shell腳本,他自己的功能並不強大,強大的是他能夠調用其餘程序,而在Linux下,系統自帶的就有很是多的強大工具能夠調用。
後臺執行一個腳本只須要在後面加上&符號便可,咱們先用以前學習的,寫一個腳本,1s輸出一個數字
#!/bin/bash int=1 while : do echo $int ((int++)) sleep 1s #睡眠一秒 done
咱們執行sh d.sh & 咱們發現,的確會後臺輸出,可是會輸出到當前控制檯,咱們能夠用以前學的重定向,把輸出重定向到文件
sh d.sh > out.log 2>&1 &
這樣就把輸出和錯誤從新定向到out.log文件了 可是,咱們發現,關閉終端之後,文件就不輸出了。 當咱們端口鏈接遠程主機的session或者關閉當前終端的時候, 會產生一個SIGHUP信號 ,致使程序退出,咱們可使用nuhup來忽略這個信號 ,達到真正的後臺。
nuhup sh d.sh > out.log 2>&1 &
這樣啓動程序,就能夠打到真正後臺運行了。 那麼問題來了,咱們驗證程序在後臺運行呢?要怎嚒結束後臺程序呢?請繼續看。
在本文中,咱們已經屢次用到cat,他的做用就是讀取文件輸出到標準輸出上,也就是咱們的終端。 語法是:
cat [option] file
咱們也可使用:cat -n file ,來輸出行號。
相似上面的例子,咱們要驗證程序是否是在後臺,每一秒輸出一個數字到文件,使用cat讀取,須要不斷的屢次查看,一次cat只能輸出一次。 tail很是適合查看這種日誌類文件,他的做用是讀取文件末尾幾行輸出到標準輸出上。 tail out.log 默認顯示10行,可使用參數-n指定行數 tail -20 out.log 顯示文件末尾20行 tail -f out.log 持續監控文件out.log,若是有變化,他會試試的顯示在咱們的屏幕上面。
ps,查詢進程 這個命令參數比較多,列舉幾個比較經常使用的
參數 | 做用 |
---|---|
a | 顯示終端上的全部進程,包括其餘用戶的進程。 |
u | 顯示面向用戶的格式信息。 |
x | 顯示沒有控制終端的進程。 |
通常查詢,使用 ps aux就能夠了,查詢出來比較多,能夠篩選一下。 這裏咱們使用 ps u 就能夠查詢出咱們剛纔開啓的後臺進程了。
咱們看到咱們剛纔啓動的程序PID爲7523, 使用kill命令就能夠殺死他了
kill命令比較簡單,就是根據PID結束一個程序,好比咱們已經查詢到,咱們開的後臺進行是7523,要結束他可使用: kill 7523 以上是經常使用用法,其實kill是給程序發送一個信號,上面的程序給會程序發送一個SIGTERM信號,程序收到這個信號,完成資源的釋放,就退出了。 可是也有程序不聽話,收到信號就是不退出,這個時候,就要強制他退出,使用9號命令(SIGKILL),強制殺死他。 簡單的說 kill PID 是告訴程序,你應該退出了,請本身退出。 kill -9 PID ,是直接告訴程序,你被終結了,這個命令信號,不能被抓取或者忽略。
推薦
推薦一個庫,經過API把消息推送到我的微信上,SDK接入:https://github.com/zjiecode/wxpusher-client。
關注公衆號開發者服務(wxpusher),萬一有須要的時候呢?
http://c.biancheng.net/shell/ https://baike.baidu.com/item/shell/99702?fr=aladdin https://blog.csdn.net/lixinze779/article/details/81012318 http://www.javashuo.com/article/p-chvewjpu-bc.html https://blog.csdn.net/felix_f/article/details/12433171 http://www.runoob.com/linux/linux-shell-printf.html http://www.runoob.com/linux/linux-shell-process-control.html https://www.jb51.net/article/123081.htm http://www.runoob.com/linux/linux-shell-io-redirections.html https://www.cnblogs.com/mfryf/p/3336804.html http://www.javashuo.com/article/p-tnhezbqz-bh.html