shell介紹

村望老弟發表於2021-11-24

  1. shell是使用者與作業系統互動工具,橋樑
  2. 它本身是一個應用程式,可以使用echo $$來檢視它的pid
  3. shell貝殼的意思,像一個貝殼一樣將作業系統包裹起來,供使用者互動使用

在開發過程中,需求:統計一個日誌中錯誤日誌的條數:

  1. c,python,java,開發效率很低,需要2小時
  2. shell,幾個命令就可以搞定了

運維上的需求,打包,編譯,預處理,批量的、重複性的操作,我們可以快速的使用shell指令碼進行完成。

shell開發人員是必須掌握的技能!

主流的shell工具:

  1. sh,直接執行sh命令,可以進入到sh的shell終端,也可以與作業系統進行互動

  2. csh

  3. bash 《== 最重要的,使用兩種方式檢視當前使用者終端的預設shell:

    1. echo $SHELL
    2. vi /etc/passwd
  4. 小技巧:當我們緊急處理一個任務的時候,可以使用ctrl+z將當前的vi放到後臺,再使用fg進行喚醒(避免反覆退出vim)

  5. whoami, 檢視當前登陸使用者

  6. echo $$,可以檢視當前pid

  7. 同時編輯兩個vim檔案的技巧

    1. 開啟1.txt
    2. 輸入 : tabe 2.txt
    3. 使用gt在兩個檔案中快速切換

命令:

cd, ==> 內建命令(buildin)

chmod ==》 外部命令

如何判斷一個命令的型別:

  1. type 加上命令
  2. which 加上命令, 如果沒有找到這個命令的位置,則說明他是內建命令

區別:

  1. 內建命令是不需要啟動子程式的,而是在當前的程式下去呼叫一個普通的函式,產生的效果直接作用在當前終端(cd , source, . ), 回想一下source ~/.bashrc

    小技巧:將常用目錄設定成變數,定義在.bashrc中,cd $c39,可以快速切換

  2. 外部命令:bash會先fork一個子程式,內部呼叫exec,執行程式,執行完畢後退會到bash中

shell

shell命令:cd 、ls

shell指令碼(shell script):把shell命令按照一定的邏輯進行程式設計,完成既定步驟比較多的任務。

#!/bin/bash     #注意,這不是註釋

echo "hello world"

cd ..
ls
touch c39.txt
  1. 一個shell指令碼必須指定解析器,明確下面的命令使用哪個解析器:bash, sh

  2. #! ==> 開頭是固定的

  3. shell使用#號作為註釋

  4. shell沒有主函式,也不需要編譯,它是解釋性語言,而不是編譯性語言(c,c++)

  5. shell指令碼的執行順序就是從上到下

  6. 如果一個命令在解析器之上,是不會被解析的,但是不會報錯

  7. 常見執行方式:

    1. chmod +x hello.sh , 使用 ./hello.sh執行
    2. 明確指定解析器,/bin/bash hello.sh , 也可以,而且不需要hello.sh有執行許可權
    3. (cd ..;ls),使用括號的方式,也可以啟動子程式執行,而不是影響當前程式
  8. 執行指令碼的邏輯:

    1. 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.單引號-雙引號-反引號

  1. 單引號:所見即所得,不會解析裡面的變數
  2. 雙引號:所見非所得,裡面可以包含變數,會先解析變數,在賦值
  3. 反引號:會事先對命令進行解析,得到的結構再進行賦值

案例:

#!/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內變數:

  1. 分為全域性變數,區域性變數local修飾的,不能在程式間傳遞

環境變數,最出名的環境變數:PATH

  1. 加上了export之後,就會變成環境變數
  2. 環境變數可以在當前程式已及子程式之間都可以使用
  3. 子程式的環境變數無法傳遞給父程式, 只能單向傳遞
#!/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 :

1585625598155

可以使用env來檢視環境變數(export修飾的)

可以使用set命令來檢視所有變數(包括環境變數和普通變數)

可以使用unset命令來刪除變數的值(置空)

4.萬用字元

  1. * ==> 最常用的,代表0或多個任意字元
  2. ?==> 匹配任意1個字元
  3. [] ==》方框中的任意數字出現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. 轉義字元

  1. 特殊字元變成普通字元

    echo \$SHELL

  2. 把普通字元變成特殊字元

    \r\n ==> 換行回車

  3. 一行文字過多時,可以使用\進行換行

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

  1. 基本語法:
#!/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
  1. if 與test配合使用,後面如果then放在下一行,則分號可以省略
  2. if必須使用fi進行結尾
  3. 冒號,代表永遠為真
  1. 案例:讓使用者輸入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
  1. -a 與 &&的區別(-o和||同理)

1585640601506

3. case

c語言的switch:

switch (表示式)
{
    case1:
        xxx
        break;
    case2:
        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 協議》,轉載必須註明作者和本文連結
CunWang@Ch

相關文章