一、前言
使用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:今天一邊學習一邊思考人生,也扔了個漂流瓶記錄了下想法,希望未來能做個行動派,像我現在這樣的年紀,更多的是想的多,做的少~
有興趣的可以關注我的個人公衆號 ~