shell腳本學習筆記一

一、前言

使用shell寫腳本也寫了很多次了,不過大部分都是要用到了谷歌用法然後來的,沒有比較全面的一次學習,這次趁着有些時間從頭開始學一次shell腳本,也新建了一個新的文集,作爲學習筆記使用,shell腳本大神請繞道哈~

二、基本的io重定向

標準io也就是標準的輸入輸出,這個可能是軟件設計原則裏邊最普遍最重要的概念了,shell提供了集中語法標記,用來改變默認的io端,就是輸入端和輸出端。在這裏還將說到一個強大的命令 -- tr

以 < 改變標準輸入: program < file 可將 program 的標準輸入修改爲 file,如:(shell_demo是路徑)

先查看我本地名交 wc_test 的文件內容:

➜  shell_demo vim  wc_test
 ls | wc -l
~                                                                               
~                                                                               
~         

可以看到內容是 ls ......

再使用tr命令,將wc_test定義爲標準輸入,並且將其中的內容的ls刪除並且打出刪除後的結果,命令如下:

➜  shell_demo tr -d 'ls' < wc_test
  | wc -

從輸出的結果可以看出,wc_test的內容被刪除了ls並且打印了出來。

使用 > 改變標準輸出: program > file 可將program的標準輸出修改爲file,在上面的命令上進行再次的修改:

➜  shell_demo tr -d 'ls' < wc_test > wc_test2

此處是將wc_test的內容被刪除了ls之後再將其輸入到wc_test2中,此處可以通過vim查看內容,並且要注意一點的是,在同目錄下本來是沒有wc_test2這個文件的,但是後來卻新建了一個,說明重定向符在目的文件不存在時會新建一個,並且如果目的文件存在了,則原有內容會被覆蓋掉。

那麼,在日常需求中,也會不覆蓋文件,而是附加內容的需求存在,那麼怎麼做呢?

shell提供了 >> 附加命令,program >> file 可將program的標準輸出附加到file的結尾處,注意的是,如果文件不存在則會新建一個,如果存在了,則不會覆蓋原有文件,而是將程序所產生的數據附加到文件末尾處,還是在原有的命令上進行修改:

➜  shell_demo tr -d 'ls' < wc_test >> wc_test2

使用vim查看wc_test2可以發現內容已經變成了這樣:

  | wc -
  | wc -
~        

標準輸入和輸出都講到了,接下來要說說如何將輸入和輸出連接在一起,也就是管道,shell 以 | 建立管道,即program1 | program2 ,可將program1的標準輸出修改爲program2的標準輸入,爲了看到效果,我先將wc_test裏邊的內容修改爲:

ls
9
8     
7
6
5   
4   
3
2
1
1
2
3
4

在採用tr命令將ls字符刪除,並且將刪除後的數據用通道的形式變成第二個程序的標準輸入,命令如下:

➜  shell_demo tr -d 'ls' < wc_test | sort > wc_test3

通過vim查看wc_test3的數據可以發現,數據是是wc_test中去掉ls後的內容並且是排好序的。

tr的作用異常強大,具備對標準輸入的字符壓縮、替換刪除的作用,具體的可以查看 http://man.linuxde.net/tr ,此處便不做詳解。

sort的作用是排序,可惜的是針對每行進行排序,具體的可以查看 http://man.linuxde.net/sort ,此處便不做詳解。

這裏有個知識點想提及,在ubuntu系統中經常會遇見的一個問題,那就是修改root密碼,如何做到修改密碼的時候不打印鍵盤輸入的字符,這裏先給出一份腳本,再來講解知識點,命令如下所示:

#!/bin/sh
printf "請輸入密碼:"
stty -echo
read pass < /dev/tty
printf "\n" 
printf "請再輸入密碼:"
read pass2 < /dev/tty  
stty echo
printf "\n"           

以上是我新建的一個名字叫 wc_test4.sh 的腳本文件,運行後可以看到類似修改root密碼的時候輸入字符終端卻不打印出字符的效果,和這種效果有關的一個知識點,那就是/dev/tty,當程序打開此文件時,unix會自動將它重定向到終端,而stty(set tty)命令就是用來控制終端的各種設置,比如 stty -echo 的作用就是設置終端不打印出字符,當然了使用結束後用stty echo 來恢復該功能。

三、新學的腳本命令

➜  ~ ls
examples.desktop  Snapshots  模板  圖片  下載  桌面
npm-debug.log     公共的     視頻  文檔  音樂

理解備註:這是查詢本地目錄的一個命令。

但是有時候會衍生另一種需求:查看文件夾下面的文件和文件夾個數(同一目錄下的),命令如下:

➜  ~ ls | wc -l
11

理解備註:在這裏利用的是wc字數統計程序,可以算出行數、字數與字符數,| (管道)符號可以在兩程序之間建立管道,ls的輸出,成了wc的輸入,wc所列出的結果就是目錄下文件和文件夾的個數。

有時候會遇見一種情況,需要將命令放進獨立的腳本文件裏,爲了方便以後直接使用,可以嘗試一下的方法:

➜  cat > wc_test

輸入以下命令:(可以替換爲你想放進獨立腳本的如何命令)

 ls | wc -l

這裏輸入後記得以 ctrl+d 結束輸入!

之後就是添加執行權限~

➜  chmod +x wc_test

運行輸出

➜  ./wc_test 

執行了這個腳本程序之後,會隨之誕生一個問題, 那就是在執行這個shell腳本的時候,內核是如何執行這個過程的?

當shell要求內核執行這個腳本時,內核講無法執行這件事,並回應“not executable format file”錯誤信息,意思就是說這個不是內核可執行的格式文件,shell收到此錯誤信息之後,就會認爲這不是編譯程序,那麼一定是shell腳本,接着就會啓動一個新的/bin/sh(標準的shell)副本來執行程序。

當系統只有一個shell時,退回到/bin/sh的機制很方便,但是現在的unix系統都會擁有好幾個shell,因此告知unix內核是哪個shell來執行所指定的shell腳本就有必要了,那麼怎麼做呢?方法是在腳本的第一行設置,採用#!來開頭,例如在我的go微服務項目(https://github.com/wiatingpub/MTBSystem)中的腳本:

#!/bin/sh


if [ $1 == "all" ]; then
    for srv in `ls src`; do
        if [[ ${srv:0-4} == "-srv" ]]; then
            echo "開始更新$srv"
            GOROOT=/data/services/go GOBIN=/data/goapp/mtbsystem/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart $srv:*
        fi
    done
else
    for srv in "$@"
    do
        if [[ "${srv:0-4}" != "-srv" ]]; then
            srv="${srv}-srv"
        fi
        echo "開始更新$srv"
        GOROOT=/data/services/go GOBIN=/data/goapp/mtbsystem/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart class-$srv:*
    done
fi

ps:今天一邊學習一邊思考人生,也扔了個漂流瓶記錄了下想法,希望未來能做個行動派,像我現在這樣的年紀,更多的是想的多,做的少~

有興趣的可以關注我的個人公衆號 ~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章