前言
最近就干了兩件有意思的事,昨天分享了一個,今天我們來分享另一個——shell腳本統計數據。
相比於python,shell是另一種特別常用,特別實用,也特別基礎的腳本語言,基本上所有的linux系統都支持shell,更准確地說,shell讓系統運行更方便,是shell讓linux更強大,所以學習一些基本的shell語法,可以簡化linux的運維工作,提升我們的工作效率。
其實,win平台也是有它的shell的,也就是bat或者cmd腳本,通過這些腳本,我們同樣也可以做很多事,等下我們也分享一些win平台的常用腳本,讓各位小伙伴見識下腳本語言的魅力。
好了,廢話少說,下面我們開始編寫數據統計腳本。
shell腳本統計數據
在開始之前,我們先看下shell的相關內容。shell通俗來講就是我們通常說的CMD(commond,也叫命令行),通過shell我們可以直接與操作系統內核進行溝通。
至於為什么叫shell,也很好理解,shell的中文名稱是殼,而kernel是操作系統的核心,核心是需要和外側進行隔離的,通過什么來隔離呢,就是通過殼(shell),而且shell還要充當核心與外界溝通的信使。
當然shell本身也有好多種:
/bin/sh:已經被/bin/bash所取代/bin/bash:Linux預設的shell/bin/tcsh:整合C shell,提供更多的功能/bin/csh:已經被/bin/tcsh所取代/bin/zsh:自動補全功能很強大,檢查拼寫功能很強大,因為oh-my-zsh名聲大噪,而且可定制性比bash更高
如果是日常使用的話,推薦實用zsh,但是如果是寫腳本的話,建議用bash,畢竟它是linux的預裝shell
簡單介紹
下面是最簡單的shell腳本:
#!/bin/bash
echo "hello shell"
結合我們上面shell的相關知識,我們可以推測出第一行是標記shell類別的,也就是用哪種shell來解釋腳本;從第二行開始就是腳本的內容了。
運行這個腳本之后會在telminal控制台打印輸出hello shell字符串:

shell腳本基本要點
-
linux系統的shell腳本文件后綴是.sh,也就是shell的簡寫。 -
shell腳本的注釋方式是# 注釋內容,linux平台絕大多數的配置文件都是采用這種方式進行注釋的:#!/bin/bash # 我是腳本注釋 eacho "hello shell" -
shell腳本有兩種運行方式第一種是:
# 腳本名稱hello.sh
sh hello.sh
# 或者
bash hello.sh
第二種方式是:
./hello.sh
雖然這兩種方式都可以運行,但是第二種方式需要給腳本增加運行權限,具體命令如下:
chmod u+x hello.sh
u表示所屬用戶,+x表示增加運行權限,所以執行上面的這行命令,這個腳本就有了運行權限,然后再通過./hello.sh運行即可。
關於sh腳本的基礎知識我們就先分享這么多,想系統學習的小伙伴可以去看下《鳥叔的Linux私房菜》,里面關於shell script講的還是比較系統的,而且很全面,這本書也算是linux的中文聖經了。
編寫統計腳本
下面這段腳本執行的操作很簡單,首先是查詢了db庫配置庫,並將結果寫入到connect_info文件中,拿到查詢結果之后,循環遍歷,然后再次組裝sql查詢我們要統計的數據,並將數據寫入count_data,之后我們就可以通過awk、sort、head等命令對結果進行解析、排序,最終拿到我們想要的結果。
#!/bin/bash
# mysql數據庫配置信息
# 數據庫端口
port=3306
# 數據庫用戶名
user="root"
# 數據庫密碼
pawd="root"
# 數據庫地址
host="192.168.1.105"
# 庫名
db_name="exam_system"
# 查詢數據庫連接信息
select_sql="select id, db_server, db_source_name from db_config"
# 統計sql
count_sql="select count(*) from question_option_info where question_id in (select id from question_info where type = 5 ) and CHAR_LENGTH(title) >= 50"
# 命令行連接mysql,並執行sql,這里我將查詢結果輸出到connect_info文件中
mysql -h "${host}" -P "${post}" -u"${user}" -p"${pawd}" -D${db_name} -N -s -e "${select_sql}" >connect_info
# 循環遍歷
while read line
do
# 獲取連接信息中的數據
config_id=`echo "$line"|cut -f1`
db_server=`echo "$line"|cut -f2`
db_name=`echo "$line"|cut -f3`
#mysql -h "${db_server}" -P "${port}" -u"${user}" -p"${pawd}" -D${db_name} -N -s -e "${count_sql}">>count_data
result=`mysql -h "${host}" -P "${port}" -u"${user}" -p"${pawd}" -D${db_name} -N -s -e "${count_sql}"`
echo "${config_id} ${result}" >> count_data
done < connect_info
這里我們簡單解釋下這段腳本,4~16行我們分別定義了7個變量,sh腳本定義變量的方式和python有點像,都不需要指定數據類型;
mysql命令參數
18行我們執行了一個mysql的命令,這行命令的作用是連接mysql並執行sql語句,-e表示執行並退出,后面直接跟sql語句,會直接打印sql,執行完成后會打印sql結果:

我以前真不知道還有這種操作方式,不是這次統計數據,應該不會去了解。
-s表示以制表符分割顯示結果,不加這個參數的話,結果會包括|,也就是我們通常看到的結果:

不加-s查詢結果如下:

加了-s顯示結果如下:

-N參數的作用是隱藏字段名稱,也就是title(加上這個參數可以保證我們查詢結果只有數據):

sh腳本要點
在sh腳本中,將運行結果輸出到文件中有兩種方式,一種是>,另一種是>>,兩者的區別是,前者會覆寫文件中的內容,而后者是在文件中追加(如果文件中已經存在內容,則會在原有內容后面追加新的內容),這種操縱在sh中很普遍,比如:
# 將 ls 結果輸出到文件中
ls /home/syske > file_list
# 將當前時間輸出到文件中,每次追加
date >> date_list
<與>剛好相反,是將文件的內容讀取出來,比如:
# 讀取 date_list
cat < date_list
另外除了直接將命令結果輸出,我們還可以用變量接收運行結果:
result=`mysql -h "${host}" -P "${port}" -u"${user}" -p"${pawd}" -D${db_name} -N -s -e "${count_sql}"`
比如上面這行sh代碼就是用result接收mysql的運行結果,需要注意的是命令必須通過( ` )進行包裝,否則命令無法運行。這種方式有個好處,就是可以對結果進行處理,比如拼接變量:
echo "${config_id} ${result}"
運行結果處理
拿到運行結果之后,我們可以通過awk、sort等命令進行處理,以拿到最終數據。比如我們的查詢結果如下:
Sun Nov 7 16:59:54 CST 2021
Sun Nov 7 16:59:57 CST 2021
Sun Nov 7 16:59:58 CST 2021
如果是通過awk處理,則可以直接幫我們將數據分割成:sun、Nov、7、16:59:54、CST、2021這樣的格式,而且很簡單:
awk '{print $1, $2, $3, $4, $5, $6}' date_list
這里的$1表示分割的第一部分,也就是Sun,其他以此類推;$0表示原始數據

排序的話,可以用sort命令進行操作:
awk '{print $1, $2, $3, $4, $5, $6}' date_list | sort -rk 4
排序結果如下:

其中-r表示降序排序,默認是正序排序,-k表示按指定序列排序,4表示按第四列排序,也就是按時間排序。更多其他排序可以參考sort文檔。
最后再補充點akw的常用操作命令:
- 求和
cat data|awk '{sum+=$1} END {print "Sum = ", sum}'
- 求平均
cat data|awk '{sum+=$1} END {print "Average = ", sum/NR}'
- 求最大值
cat data|awk 'BEGIN {max = 0} {if ($1>max) max=$1 fi} END {print "Max=", max}'
- 求最小值(min的初始值設置一個超大數即可)
awk 'BEGIN {min = 1999999} {if ($1<min) min=$1 fi} END {print "Min=", min}'
擴展知識
這里再分享win環境下的幾個常用命令:
- 關機操作:
# 休眠操作
shutdown /h
# 重啟
shutdown /r
# 定時關閉(兩小時后關機,單位秒)
shutdown /s /t 3600
- 顯示指定文件夾下的所有內容,包括文件和子文件夾:
dir /b/s ./
- 顯示指定文件夾樹形結構:
tree ./

另外,我們前面說了,win平台也支持>和>>,這樣我們就可以把上面命令的結果輸出到文件中:
dir /b/s ./ > file_list.log
tree ./ >> struts.log
-
批量修改文件后綴(文件名)
# 修改文件擴展名 ren *.gif *.jpg # 或者(ren和rename等同) rename *.gif *.jpg # 修改文件名 ren *.gif *-syske.gif -
批量刪除文件
# 我經常通過這樣的方式刪除重復文件
del *(1).jpg
結語
shell腳本統計數據的內容我們就分享這么多,核心知識點包括shell的基本語法、linux的常用命令等,總體的內容難點並不大,如果實際工作中遇到了類似的需求,各位小伙伴可以參考。
最后,還想多說兩句。在我的理解中,我覺得一切工具都可以變成生產力工具,最關鍵的還是要學會活學活用,養成一個善於動腦的小可愛,這樣才能避免低效重復工作,有時候同樣的工具,關鍵在於你如何用,你是否能夠發現它的價值點,現實中的栗子有很多,比如回形針的一百種用法。
當然,實際工作中也會遇到很多類似的問題,比如你現在需要修改一批線上數據(400多條),數據的條件和更新內容各不相同,你能想到那些處理方式?通過編輯器批量修改?還是手工一條一條修改?還是借助其他工具,python、excel?上周五我就完成了這樣的工作,最終通過excel生成update sql,通過文本編輯器批量插入和替換拼接緩存key,最后完成相關數據修改。我說這個例子是希望各位小伙伴在學習和實踐的時候,要學會發散思維,不要被當前的語言、環境、方法所局限,要學會解決問題,學會知識的橫向應用,橫向思考。好了,今天廢話有點多,各位小伙伴,晚安吧!
