Sheel基礎2

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

echo

  1. 回聲,用於列印輸出工作
    1. -n: 可以不換行
    2. -e:對字元進行轉義,\t ===》 enable

printf

  1. printf “%d,%s\n” 10 “你好”
  2. 注意10 和 “你好” 兩個引數之間,沒有逗號

管道|

  1. 使用| 可以將多個命令拼接在一起

    cat /etc/passwd  | grep itcast | cut -d: -f4
    1000
  2. 原理:將前一個命令的標準輸出作為後一個命令的標準輸入

  3. 標準錯誤輸出是不會被管道傳遞的

    cp | grep information.  # 這個是不生效的
    cp  --help | grep existing --color  #這個是正確用法

  4. 並不是所有的命令都可以接受|的資料,比如說ls

      find ../../01-shell-basic/ | ls
  5. 需求:實現一個接收管道輸出的命令:讀取標準輸入,將標準輸入的資料轉成大寫,然後輸出:

    #include<stdio.h>
    #include<ctype.h>
    
    int main()
    {
        int ch=getchar();
        while(ch != EOF)
        {
            putchar(toupper(ch));
            ch = getchar();
        }
    
        return 0;
    }
  6. 經常與more和less使用

    1. more,只可以單向查閱
    2. less可以雙向查閱
    3. ctrl + f(前),ctrl+B(後)

tee

作用之一:

  1. 將標準輸出重新輸出到指定的檔案

  2. 公司伺服器列印log,希望能夠在螢幕上列印,同時儲存到檔案:

    ping www.baidu.com | tee ping.log
  3. 如果想動態檢視ping.log檔案內容

    1. vim ping.log, 然後輸入:e,可以隨時重新整理
    2. tail -f ping.log,動態重新整理
    3. alias tailf=tail -f

作用之二:

當修改了一個檔案很多內容,但是沒有修改許可權的時候,可以直接使用tee命令來啟用sudo 許可權,從而完成修改

重定向

  1. cmd > 1.txt , 將輸出寫入到1.txt,每一次都是重新覆蓋,等同於: cmd 1> 1.txt(常用)

  2. cmd > > 1.txt,將輸出追加到1.txt

  3. >, >> 兩者只能將標準輸出重定向到檔案,而標準錯誤輸出無法直接寫入

  4. 案例:

    $  cp > cp.txt
    cp: missing file operand
    Try 'cp --help' for more information.
    
    cat cp.txt 返回空
  5. 檔案描述符:

    1. 標準輸入檔案描述符:0
    2. 標準輸出檔案描述符:1
    3. 標準錯誤輸出檔案描述符:2
  6. 如果想將錯誤的資訊重定向到檔案,需要使用檔案描述符2

    cp 2> cperr.txt  #2> 表示對錯誤資訊重定向
  7. ==如何將正確輸出和錯誤輸出都寫入到一個檔案中?(最常用)==

    cp > /tmp/cperr1.txt 2>&1
  8. 寫一段c程式碼,裡面同時輸出:stdout, stderr

    #include<stdio.h>
    
    int main()
    {
        //寫一段c程式碼,裡面同時輸出:stdout, stderr
        fprintf(stdout, "this is stdout\n"); //fd 1
        fprintf(stderr, "this is stderr\n"); //fd 2
    
        return 0;
    }

    結果:

  1. 使用重定向進行輸入

    cmd < file

    cat < /etc/passwd
  2. 其他:

    1. cmd < &fd
    2. cmd > &fd
    3. cmd < &-

函式

  1. ==沒有引數列,沒有返回值,return返回的是狀態碼,使用$?檢視==

  2. 定義函式:

    function test1()
    {
        #這個return的值,表示這個函式之後之後返回的狀態碼,而不出返回值,
        #不能通過a=test()來獲取100值
        #可以通過echo $?來獲取
        return 100
    }
    
    #function去掉
    test()
    {
    
    }
    #去掉括號 
    function test
    {
    
    }
    
    #function()至少保留一個,否則無法識別成函式ss
  1. 案例:

    #!/bin/bash
    
    function test1()
    {
        a=200
        #a=100
            #這個return的值,表示這個函式之後之後返回的狀態碼,而不出返回值,
            #不能通過a=test()來獲取100值
            #可以通過echo $?來獲取
        if [ $a -eq 100 ]; then
            return 0
        else
            echo "hello test1"
            return 100
        fi
    }
    #function去掉
    test2()
    {
            echo "hello test2"
    }
    #去掉括號
    function test3
    {
            echo "hello test3"
            #如果函式沒有return值,那麼最後一條執行命令的狀態碼,就是這個函式的狀態碼
    }
    
    

#function和()至少保留一個,否則無法識別成函式ss

v1=test1

if [ $? -eq 0 ];then
echo “test1執行成功! a=100”
else
echo “test1執行之後,a!=100”
fi

#echo “test1 的返回狀態碼:” $?

#echo “v1 : “ “$v1”

test2
test3


   ![](http://qiniuyun.quancundexiwang.wang/1585709515511.png)



3. 小結:

   1. 執行狀態$?return的返回值,如果沒有返回值,則以最後一條命令的返回值為準

      ```sh
      test2()
      {
          echo "hello test2"
          cp  #echo $? 1
      }
      ```

   2. return修飾的狀態碼只能是數字

      ```sh
      function test3
      {
          echo "hello test3"

          #./functest.sh: line 26: return: beijing: numeric argument required
          return "beijing" #這是錯誤的用法,必須是數字
      }
      ```

4. 函式引數:

   1. 呼叫方法與命令一樣,也是$1, $2 ..

      ```sh
      test4()
      {
          echo "test4 函式引數為: $1, $2"
      }

      test4 "hello" "world"

      ```

5. 案例:遞迴遍歷指定目錄的檔案!

```sh
#!/bin/bash

#案例:遞迴遍歷指定目錄的檔案!
#目錄由使用者通過命令列傳入:

#./recursive.sh ..

recursive()
{
    #接收使用者輸入的目錄
    #currDir="$1"
    local currDir="$1"
    echo "====> 當前目錄為:" "$currDir"

    #遍歷當前目錄
    for i in `ls $currDir`
    do
        #這樣才是有效的路徑,後續才能正確判斷性質
        #item="$currDir+"/"+$i"  <<=== 這是錯誤的,  /usr/+/+test.txt
        item="$currDir/$i"
        #echo "item: " $item

        #如果i是檔案,則列印
        if [ -f "$item" ]; then
            echo "檔案找到: $item"
        elif [ -d "$item" ];then
        #如果i是資料夾,則遞迴遍歷
            echo "資料夾找到:" "$item"
            recursive $item
        else
            echo "未知檔案:" "$item"
        fi
    done
}

#判讀輸入引數的個數
if [ $# -ne 1 ];then
    echo "Usage: recursive <檔案目錄>"
    exit
fi

recursive $1

除錯

shell除錯引數:

  • -n 幫助我們快速檢查指令碼的語法,而不是逐條執行

    • 指令碼如果很大,中間執行過程很長,如果不能實現發現指令碼錯誤,會浪費時間

    •  bash -n ./recursive.sh
  • -x,執行指令碼的同時將程式碼執行過程列印到螢幕,便於追蹤

    • bash -x recursive.sh ==》 作用於整個指令碼

    • 可以在程式碼中,對指定的程式碼段進行除錯

      • set -x ==> 啟動除錯

      • xxxx

      • set +x ==> 停止除錯

  • -v,一邊執行指令碼,一邊列印錯誤輸出(很少用)

shift 8 ==> *, 快速定位當前游標所值單詞(向下尋找,n)

shift 3 ==> #, 快速定位當前游標所值單詞(向上尋找,n)

  1. 以S開頭的字串

    1. ^: 可以限定一個字元開頭

    2. 目標:ShelloShello world
      表示式:^Shello
  1. 以數字結尾的字串

    1. $:可以限定結尾

    2. 表示數字方式:

      1. [0123456789]
      2. [0-9]
    3. 目標:helloworld9
      表示式:helloworld[0-9]$
  1. 匹配空字串(沒有任何字元)

    1. ^$
  2. 字串只包含三個數字

    1. 三個數字:[0-9][0-9][0-9]
  3. 字串只有3到5個字母

    1. 字母表示:
      1. [a-zA-Z], 中間不要加空格
      2. 三個到五個字母:[a-zA-Z]{3,5}
    2. 重複次數{m, n}:
      1. 匹配前面修飾字元的次數,最少m次,最多n次
  4. 匹配不是a-z的任意字元

    1. ^ 在[]內部的時候,表示取反(注意,不是限定開頭)
    2. ^[^0-9] : 以非數字開頭的字元
    3. 內反,外頭
    4. [^a-z] <<<===
  5. 字串有0到1個數字或者字母或者下劃線

    1. 表示0-1的方法

      1. {0,1}
      2. ? : 表示0,1個
    2. [a-zA-Z0-9_]?

    3. 內容
      hello
      hello1
      hello*
      
      正則:
      hello[a-zA-Z0-9_]?
      
      匹配結果:
      hello
      hello1
      hello
  6. 字串有1個或多個空白符號(\t\n\r等)

    1. \s ==》 代表空格

    2. +:一個或多個

    3. 內容:hello      world
      
      正則:hello\s+world
      
      結果:hello      world
  7. 字串有0個或者若干個任意字元(除了\n)

    1. .: 點,代表任意字元,除了\n

    2. *:星號,重複前一個字元0-n次

    3. 匹配任意字元,任意次數:通常做法,是: .* ==> 相當於萬用字元裡面的 *

    4. 內容:hello     asdfaasdfasdfadfasdfasdf asdfasdfasdf   world
      正則:hello.*world
      結果:hello     asdfaasdfasdfadfasdfasdf asdfasdfasdf   world
    5. .{,} ==> 與 .*效果相同

  8. 小結:

    1. ? ===> 重複前面一個字元0-1
    2. + ===> 重複前面一個字元1-n
    3. * ===> 重複前面一個字元0-n
    4. . ===> 表示任意字元
    5. ^ ===> 限定以某個字元開頭
    6. [^a-z] ===> 在方括號內,表示取反(非a-z的任意字元)
    7. $ ===> 限定以某個字元結尾
    8. 數字 ====》 [0123456789] 或[0-9]
    9. 字母 ===> [abcd] [a-zA-Z]
  9. 匹配0或任意多組ABC,比如ABC,ABCABCABC

    1. 使用()進行分組,組成新的匹配單元
    2. (ABC)*
  10. 字串要麼是ABC,要麼是123

    1. (ABC|123)

    2. 內容:
      ABC
      ABCABCABCD 
      123456
      hellowlrd
      
      
  正則:(ABC|123```
  1. 字串只有一個點號

    1. ^\.$
  2. 匹配十進位制3位整數 :

    1. 0~999 , 5, 10 , 200, 999
    2. 分段匹配:
      1. 一位數:
        1. [0-9]
      2. 二位數:
        1. [1-9][0-9]
      3. 三位數:
        1. [1-9][0-9]{2}
      4. 整合:
        1. ^ ([0-9]|[1-9][0-9]{1,2})$
  3. 匹配0-255的整數, ip地址

    1. 分段
      1. 一位:[0-9]
      2. 二位:[1-9][0-9]
      3. 三位:
        • 100- 199
          • 1[0-9]{2}
        • 200-249
          • 2[0-4][0-9]
        • 250-255
          • 25[0-5]
      4. 整合:
        • ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
        • 192.168.1.1
  4. 匹配埠號

    1. 0~65536
  5. email

    [\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?

三種正規表示式:

  1. 基礎正則:(不使用) ==》 +?* 都當成普通字元來處理

  2. 擴充套件正則:(我們預設使用這個)==》 +?* 當成特殊字元

  3. Perl正則:

    1. +?*是特殊字元

    2. \d ==》 數字 [0-9]

    3. \w ==> 字元[a-zA-Z]

    4. 用法\d{3}\w 可以匹配:212c

sort(排序)

-f 忽略字元大小寫
-n 按照數值比較
-t 分割字元
-k 指定分割之後比較的欄位
-u 只顯示一次
-r 反向排序

使用:分割之後,擷取第三段,以數字的值進行比較,反向列印

 sort passwd -t: -k3 -n -r

uniq(刪除重複)

uniq.txt

xiaoliu 99 man 25
xiaowen 98 man 24
xiaoliu 99 man 25

去重方式,需要重複的兩行挨著才能去重,一般和sort配合使用

cat uniq.txt | sort | uniq

wc

-l: 統計檔案的行數 <==最常用

-c:統計字元

-w:統計單詞數量

 duke ~$  wc /etc/passwd -l    
32 /etc/passwd
 duke ~$  wc /etc/passwd -c
1694 /etc/passwd
 duke ~$  wc /etc/passwd -w
49 /etc/passwd

grep

grep [options]
主要引數:  grep --help可檢視
    -c:只輸出匹配行的計數。
    -i:不區分大小寫。
    -h:查詢多檔案時不顯示檔名。
    -l:查詢多檔案時只輸出包含匹配字元的檔名。
    -n:顯示匹配行及 行號。
    -s:不顯示不存在或無匹配文字的錯誤資訊。
    -v:顯示不包含匹配文字的所有行。
    --color=auto :可以將找到的關鍵詞部分加上顏色的顯示。
    -r: 表示遞迴查詢

-i 忽略大小寫,NOLOGIN一行也列印了

grep -i nologin  passwd  --color

使用擴充正則[0-9].*

grep -i "[0-9].*bin"  passwd  --color

如果想使用Perl正則,需要指定-P開關

 grep -i -P  "\d.*bin"  passwd  --color

使用-r進行遞迴查詢

grep -i -P  "\d.*bin"  *  --color  -r
cat /etc/passwd | grep root

find

語法:

find pathname -options [-print -exec -ok ...]
  1. 通過名字查詢
find ./shell  -name "*.txt"

  1. 通過許可權查詢
 find ./shell/ -perm 755
  1. 通過型別查詢
find ./shell -type f "*.txt"   #檔案型別
find ./shell/ -type d -name "*.txt"  #資料夾型別

-type 查詢某一型別的檔案,諸如:
b - 塊裝置檔案。
d - 目錄。
c - 字元裝置檔案。
p - 管道檔案。
l - 符號連結檔案。
f - 普通檔案。
  1. 指定查詢深度
 find / -maxdepth 2 -type d

  1. 不區分檔名字大小寫,使用-iname
 find ./shell -type f -iname "*.txt"

  1. 對find查詢到的檔案,進行處理,使用

    1. -exec: 直接執行

      find ./shell -type f -iname "*.txt" -exec rm {} \;
      {} 替換為傳入的資料
      \; 表示命令結束
    2. -ok:逐個確認

      find ./shell -type f -iname "*.txt" -ok rm {} \;
  2. 按照日期查詢

    -atime : 訪問時間
    -mtime: 修改時間, 內容修改
    -ctime: 不是建立時間,而是狀態修改時間,許可權,所有者
     find ./shell/ -mtime +1   
     -1: -n表示在n天之內修改的
     +1: +n表示在n天以前修改的

引數列表:

-name 按照檔名查詢檔案。
-perm 按照檔案許可權來查詢檔案。

-size n:[c] 查詢檔案長度為n塊的檔案,帶有c時表示檔案長度以位元組計。
-depth 在查詢檔案時,首先查詢當前目錄中的檔案,然後再在其子目錄中查詢。


-prune 使用這一選項可以使find命令不在當前指定的目錄中查詢,如果同時使用-depth選項,那麼-prune將被
find命令忽略。
-user 按照檔案屬主來查詢檔案。
-group 按照檔案所屬的組來查詢檔案。
-mtime -n +n 按照檔案的更改時間來查詢檔案,-n表示檔案更改時間距現在n天以內,+n表示檔案更改時間距現在
n天以前。find命令還有-atime和-ctime 選項,但它們都和-m time選項。
-nogroup 查詢無有效所屬組的檔案,即該檔案所屬的組在/etc/groups中不存在。
-nouser 查詢無有效屬主的檔案,即該檔案的屬主在/etc/passwd中不存在。
-newer file1 ! file2 查詢更改時間比檔案file1新但比檔案file2舊的檔案。
-fstype 查詢位於某一型別檔案系統中的檔案,這些檔案系統型別通常可以在配置檔案/etc/fstab中找到,該配
置檔案中包含了本系統中有關檔案系統的資訊。
-mount 在查詢檔案時不跨越檔案系統mount點。
-follow 如果find命令遇到符號連結檔案,就跟蹤至連結所指向的檔案。
pathname: find命令所查詢的目錄路徑。例如用.來表示當前目錄,用/來表示系統根目錄,遞迴查詢。
-print: find命令將匹配的檔案輸出到標準輸出。
-exec: find命令對匹配的檔案執行該引數所給出的shell命令。相應命令的形式為'command' {} \;,注意{}內部無空格,和\;之間含有一個空格分隔符。
-ok: 和-exec的作用相同,只不過以一種更為安全的模式來執行該引數所給出的shell命令,在執行每一個命令之前,都會給出提示,讓使用者來確定是否執行。只有使用者明確輸入y才會執行後邊的語句

head、tail、scp、xargs、sed、awk

本作品採用《CC 協議》,轉載必須註明作者和本文連結
CunWang@Ch