- shell是使用者與作業系統互動工具,橋樑
- 它本身是一個應用程式,可以使用echo $$來檢視它的pid
- shell貝殼的意思,像一個貝殼一樣將作業系統包裹起來,供使用者互動使用
在開發過程中,需求:統計一個日誌中錯誤日誌的條數:
- c,python,java,開發效率很低,需要2小時
- shell,幾個命令就可以搞定了
運維上的需求,打包,編譯,預處理,批量的、重複性的操作,我們可以快速的使用shell指令碼進行完成。
shell開發人員是必須掌握的技能!
主流的shell工具:
sh,直接執行sh命令,可以進入到sh的shell終端,也可以與作業系統進行互動
csh
bash 《== 最重要的,使用兩種方式檢視當前使用者終端的預設shell:
- echo $SHELL
- vi /etc/passwd
小技巧:當我們緊急處理一個任務的時候,可以使用ctrl+z將當前的vi放到後臺,再使用fg進行喚醒(避免反覆退出vim)
whoami, 檢視當前登陸使用者
echo $$,可以檢視當前pid
同時編輯兩個vim檔案的技巧
- 開啟1.txt
- 輸入 : tabe 2.txt
- 使用gt在兩個檔案中快速切換
命令:
cd, ==> 內建命令(buildin)
chmod ==》 外部命令
如何判斷一個命令的型別:
- type 加上命令
- which 加上命令, 如果沒有找到這個命令的位置,則說明他是內建命令
區別:
內建命令是不需要啟動子程式的,而是在當前的程式下去呼叫一個普通的函式,產生的效果直接作用在當前終端(cd , source, . ), 回想一下source ~/.bashrc
小技巧:將常用目錄設定成變數,定義在.bashrc中,cd $c39,可以快速切換
外部命令:bash會先fork一個子程式,內部呼叫exec,執行程式,執行完畢後退會到bash中
shell
shell命令:cd 、ls
shell指令碼(shell script):把shell命令按照一定的邏輯進行程式設計,完成既定步驟比較多的任務。
#!/bin/bash #注意,這不是註釋
echo "hello world"
cd ..
ls
touch c39.txt
一個shell指令碼必須指定解析器,明確下面的命令使用哪個解析器:bash, sh
#! ==> 開頭是固定的
shell使用#號作為註釋
shell沒有主函式,也不需要編譯,它是解釋性語言,而不是編譯性語言(c,c++)
shell指令碼的執行順序就是從上到下
如果一個命令在解析器之上,是不會被解析的,但是不會報錯
常見執行方式:
- chmod +x hello.sh , 使用 ./hello.sh執行
- 明確指定解析器,/bin/bash hello.sh , 也可以,而且不需要hello.sh有執行許可權
- (cd ..;ls),使用括號的方式,也可以啟動子程式執行,而不是影響當前程式
執行指令碼的邏輯:
- bash在執行shell指令碼時,啟動一個子程式,進行exec處理,退出
1. 變數相關:
#!/bin/bash
#定義變數不需要指定型別,int float
#定義變數時,等號兩邊不要有空格,否則會報錯
name="Lily"
#使用$進行遍歷的引用
echo $name
echo name
#如果在變數解析的時候,涉及到邊界劃分問題,可以使用${}方式,來進行劃分
#helloa
a="hello"
aa="world"
echo $a
echo ${a}a
結果:
2.單引號-雙引號-反引號
- 單引號:所見即所得,不會解析裡面的變數
- 雙引號:所見非所得,裡面可以包含變數,會先解析變數,在賦值
- 反引號:會事先對命令進行解析,得到的結構再進行賦值
案例:
#!/bin/bash
name=lily
person="$name is a good girl"
person1='$name is a good girl'
echo "person: $person , person1: $person1"
touch a.txt b.txt
v1="a.txt b.txt"
ls $v1
echo "-----------"
ls "$v1" #<<=== 經驗老道都這樣寫!
d0=date
d1=`date` #《== tab鍵上面的
d2=$(date) #與``效果相同
echo "d0: $d0"
echo "d1: $d1"
echo "d2: $d2"
curPath=$(cd `dirname $0`;pwd)
#curPath=$(cd dirname $0;pwd) #此時會報錯,too many arguments
echo "curPath: $curPath"
結果:
此時,程式將 “a.txt\ b.txt”當成一個檔案去查詢了,所以報錯
注意點:所有使用變數的地方,儘量使用雙引號將變數包裹起來 “$var”, 而不用直接使用 $var,尤其是在if,for裡面,如果變數不加雙引號,會經常出bug
3. 變數分類
shell內變數:
- 分為全域性變數,區域性變數local修飾的,不能在程式間傳遞
環境變數,最出名的環境變數:PATH
- 加上了export之後,就會變成環境變數
- 環境變數可以在當前程式已及子程式之間都可以使用
- 子程式的環境變數無法傳遞給父程式, 只能單向傳遞
#!/bin/bash
export school=浙江大學
echo "address" $address #事先在bash終端中設定了 export addrss="Beijing"
echo 111111 : $name
#全域性的內變數
name=Lily
echo 222222 : $name
function testfunc()
{
#區域性內變數,只能在自己的作用域中使用
local age=20
echo 333333 : $age
}
testfunc
echo 444444 : age :$age , name : $name
/bin/bash 4.1.子程式.sh
4.1.子程式.sh:
#!/bin/bash
echo "school :" $school
echo "name :" $name
結果:
address 黑龍江
111111 :
222222 : Lily
333333 : 20
444444 : age : , name : Lily
school : 浙江大學
name :
可以使用env來檢視環境變數(export修飾的)
可以使用set命令來檢視所有變數(包括環境變數和普通變數)
可以使用unset命令來刪除變數的值(置空)
4.萬用字元
- * ==> 最常用的,代表0或多個任意字元
- ?==> 匹配任意1個字元
- [] ==》方框中的任意數字出現1次
touch {1..9}.txt
touch {a..h}.sh
mv 1.txt 111.txt
mv c.sh cccc.sh
ls *.txt
ls ?.txt
ls [123ght].txt
5. 轉義字元
特殊字元變成普通字元
echo \$SHELL
把普通字元變成特殊字元
\r\n ==> 換行回車
一行文字過多時,可以使用\進行換行
1. test
用於檢測檔案型別,數值(字串,數字)
test -e file1, 此時測試檔案是否存在,使用echo $? 來列印執行結果,0:代表真,非0,:代表假
使用[ -e file1 ] 等同於test
案例:
#!/bin/bash echo "====> 1. 檔案測試!" f1=test.sh test -e "$f1"; echo $? #0 test -f "$f1"; echo $? #0 test -d "$f1"; echo $? #1 test -d a; echo $? #0 [ -d a ]; echo $? #0 echo "====> 2. 數字測試!" v1=100 v2=200 [ $v1 -eq 100 ];echo $? #0 #使用[ 時,一定要留出空格,否則報錯 #[$v1 -eq 100];echo $? #0 [ $v1 -eq 100 ];echo $? #0 [ $v1 -ne $v2 ];echo $? #0 [ $v1 -gt $v2 ];echo $? #1 [ $v1 -lt $v2 ];echo $? #0 [ $v1 -ge $v2 ];echo $? #1 [ $v1 -le $v2 ];echo $? #0 #下面這種>寫法,語法執行時沒有報錯,但是處理的結果是錯的,需要慎重 [ $v1 > $v2 ];echo $? #0 echo "====> 3. 字串測試!" name="duke !" [ -z "$name" ];echo $? #1 當name為空時,返回true [ ! -z "$name" ];echo $? #0 [ -n "$name" ];echo $? #0 當name非空時,返回true name1="" [ -z $name1 ];echo $? #[ -n "$name" ];echo $? [ -n "$name1" ];echo $? #該案例中,如果不加引號,判斷是錯誤的 name2="lily !" #在shell的條件判斷中,=不是賦值,而是等同於== [ "$name" = "$name2" ]; echo $? #1 #[ "duke !" = "lily !" ] #[ duke ! = lily ! ] #不加的效果 [ "$name" != "$name2" ]; echo $? #0
echo “===》 與或非”
[ -f test.sh -a -z “$name2” ] ;echo $? #1
[ -f test.sh -o -z “$name2” ] ;echo $? #0
[ ! -z “$name2” ] ;echo $? #0
如果不加雙引號報錯如下:
![](http://qiniuyun.quancundexiwang.wang/1585637746234.png)
公司專案工程示意:
```sh
src internal external test build.sh
./build.sh debug
./build.sh release
2. if test
- 基本語法:
#!/bin/bash
f1="if.sh"
if [ -d "$f1" ];then
echo "$f1 is a file"
elif [ -x "$f1" ]; then
echo "$f1 有執行許可權!"
else
echo "$f1 is not a file"
fi
if true;then
echo "永遠為真"
else
: #代表空操作
fi
if :;then
echo "永遠為真"
else
: #代表空操作
fi
- if 與test配合使用,後面如果then放在下一行,則分號可以省略
- if必須使用fi進行結尾
- 冒號,代表永遠為真
- 案例:讓使用者輸入YES,NO進行分支處理
#!/bin/bash
#echo "請問最近在找工作嗎?(Y|N)"
#read YES_OR_NO
#-p 是輸入前的提示
read -p "請問最近在找工作嗎?(Y|N)" YES_OR_NO
if [ "$YES_OR_NO" = "Y" ];then
echo "是的"
elif [ "$YES_OR_NO" = "N" ];then
echo "不是"
else
echo "請正確輸入,Y|N"
fi
- -a 與 &&的區別(-o和||同理)
3. case
c語言的switch:
switch (表示式)
{
case 值1:
xxx
break;
case 值2:
xxx
break;
default:
xxxx;
}
shell:
case 表示式 in
v1|v2)
xxx
;;
v3|v4)
xxx
;;
*)
xxx
esac
案例:
#!/bin/bash
read -p "滅霸同學,你集齊拯救世界的寶石沒?" YES_OR_NO
case "$YES_OR_NO" in
y|Y|yes|YES) #表示裡面的多個值都是滿足當前分支條件的
echo "是的,Mr杜!"
;; #v這兩個分號必須存在
n|N|no|NO)
echo "抱歉先生!"
;;
*)
echo "輸入無效!"
esac
4. for、while
#!/bin/bash
values="aaa bbb ccc ddd biu biu biu"
for i in $values
do
echo $i
done
#for i in `ls /`
#do
# echo $i
#done
#1 + 2+ ... + 100 = 5050
sum=0
for i in {1..100}; do
#sum=$sum+$i #這是錯的,效果:1+2+3+4...
#sum=$(($sum + $i)) #正確的
#sum=$((sum + i)) #正確的
sum=$[$sum+$i] #正確的
sum=$[sum+i] #正確的
done
echo sum : $sum
#shell的陣列
nums=(10 20 30 40)
echo ${nums[0]}
echo ${nums[1]}
echo ${nums[2]}
echo ${nums[3]}
echo "for遍歷陣列...."
#for i in $nums #這種方式是錯誤的
for i in ${nums[*]}
do
echo $i
done
i=10
while [ $i -gt 0 ]
do
echo "$i"
i=$[i-1]
done
if [ $? -eq 0];then
echo "上一句命令執行成功"
fi
while true
do
sleep 1
echo "hello"
done
結果:
5. 特殊字元
$? : 一個程式執行完畢的返回值,用於判斷程式是否執行成功,從而控制後續邏輯
$$: 返回當前程式的pid
$#: 獲取當前指令碼傳入引數的個數
$@:獲取當前指令碼傳入引數的具體數值 “aa” “bb” “cc”
$*:與$@類似,但是=》 “aa bb cc”
$0:當前指令碼檔案的名字
$1 ~ $9:依次儲存每一個輸入引數的變數值
shift:將使用者輸入的引數逐個剔除,從而使得程式控制$1即可。
#!/bin/bash
echo "接收引數的數量:" $# #argc
echo "接收引數的內容:" $@ #argv[0]之後的
echo "程式的名字:" $0 #argv[0]
echo "遍歷引數如下:"
for i in $@ #$@使用者輸入引數的集合
do
echo $i, 當前引數總個數為: $#
done
#這些依次儲存使用者輸入的引數,但是最多到$9
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11
echo "使用shift命令之後....."
for i in $@
do
echo $1, 當前引數總個數為: $#
shift #將當前的引數最左側的剔除掉,後一個變成了第一個
done
結果:
本作品採用《CC 協議》,轉載必須註明作者和本文連結