Notify Wifi Status And Query Statistics Using Service On Debian 10
【摘要】
敘述在 Debian 10 系統用 systemd 的 Service 顯示 Wifi 狀態,
以及記錄與計算收發量的方法。
【目錄】
【前言】
【抽取 syslog 的內容】
【監視記錄檔】
【連結的 .service 檔】
【處理 Wifi 狀態的指令】
【記錄 Wifi 傳輸量】
【查詢 Wifi 傳輸量】
【查詢 Wifi 傳輸量的累積圖】
【後語】
【前言】
一、現在的智慧型手機大多有顯示或控制上網使用量的程式,因為不是每一個用戶都是申請上網吃到飽的付費方案。試用了手機本身的和幾個另外安裝的應用程式,發現較簡單的程式只顯示每個月的量,較詳細的可以顯示年、月、週、日的量。但似乎沒有一個可以查詢自訂時段的量,譬如某日上午八時到下午五點的傳輸量。所以就想出了本文的方法,特將結果記錄於此。
二、本例的手機沒有 rooted,無法存取手機的系統資料,所以沒有辦法在手機端動手腳。但因本例目前大多用電腦發送 Wifi 讓手機上網,所以就變通地在電腦端想辦法。雖然電腦 Wifi 的收發量都比手機的上網量要大一些,但因差距不大,做個參考,還是可以的。當手機使用 SIM 卡上網時,心裡才有個底,會用掉多少 $$。
三、本文的內容大致分為:
- 抽出 syslog 有關 Wifi 狀態的記錄
- 用 systemd 的服務(service)監視上述的記錄檔,當狀態改變時,顯示通知
- 在 Wifi 的啟用期間,定時記錄傳輸量;Wifi 中斷,即顯示通知並停止記錄
- 查詢某時段的 Wifi 收發量
四、本文新增的檔案較多,先將檔名和目的依序摘要於此,較易掌握整篇文章:
- net_status.conf:將含有 regex 的內容抽出到 .net_status_wl 檔
- notify-wl-status.path:監視 .net_status_wl 檔是否有修改;若有,呼叫 notify-wl-status.service
- notify-wl-status.service:執行 notify-wl-up 檔中的指令
- notify-wl-up:在 Wifi 開啟時,顯示狀態為 up,並執行 log-wl 的指令開始記錄傳輸量
- log-wl:記錄傳輸量;若 Wifi 中止,顯示狀態為 down,並停止記錄
- query-wl:查詢傳輸量
- query:查詢傳輸量的累積圖
也因為檔案多,所以新增了一個「專用目錄」 /home/USER/.local/bin/notify_updown/,儘量將相關檔案都放在裡面,以方便檢視與管理。
五、本文將 wlpNsNbN 之類的無線網路界面名稱用界面名稱
取代,各位要記得改成自己系統的名稱。執行 ip a
應該就可以查到了。此外,使用者名稱用USER
代替,也要記得改成自己的登入名。
【抽取 syslog 的內容】
一、Debian GNU/Linux 10 系統是將網路狀態記錄在 /sys/class/net/界面名稱/operstate 檔案中,內容不是 up 就是 down。原本是直接用服務(service)監視此等檔案即可,但因是系統在控制 /sys/ 目錄中的內容,所以無法用 systemd 存取,權限不足。因此改用變通的辦法,監視可以存取又有記錄網路狀態的檔案。
二、Debian 系統的記錄檔大多是放在 /var/log/ 目錄,其中有記錄網路連線狀態的檔案至少有:kern.log、messages、syslog。
剛開啟 Wifi 時,這三個檔案都會有以下記錄:
- IPv6: ADDRCONF(NETDEV_UP): 界面名稱: link is not ready
- IPv6: ADDRCONF(NETDEV_CHANGE): 界面名稱: link becomes ready
中止 Wifi 時,會有以下記錄:
- IPv6: ADDRCONF(NETDEV_UP): 界面名稱: link is not ready
所以,理論上監視三檔之一即可。但問題是這三個檔案也會記錄其他事件,直接監視它們會有一大堆的回應,所以要把上述 Wifi 狀態的記錄抽出並存到另一檔,才來監視。
三、Debian 系統的 syslog 設定檔是 /etc/rsyslog.conf。其中有一行是:
- $IncludeConfig /etc/rsyslog.d/*.conf
這是要納入 /etc/rsyslog.d/ 目錄中所有副檔名是 .conf 的設定,因此可以在此目錄新增自己的設定。本例用 net_status.conf,內容為:
- :msg, regex, " IPv6: .* 界面名稱: link " /tmp/.net_status_wl
說明:
- 冒號:用 property 篩選訊息,這個冒號一定要在行首。
msg
:訊息中的 MSG 部分。regex
:用 regular expression 來篩選訊息。"..."
:regex 的內容。這裡其實只要有「 IPv6: 」就可以了;但為了更具獨特性,所以加了界面名稱。各位要記得改成自己系統的值,不然就用「 IPv6: 」也可以。- 最後一項:篩選出的訊息所要儲存的路徑,自訂。記錄檔放在 /tmp/ 目錄中是因為關機時會刪掉,再要記錄時才建立新檔,也就是有自動清除的功能。
- 詳細說明請見
man 5 rsyslog.conf
。
註:本例是將 net_status.conf 放在自家專用目錄中,再新增連結到 /etc/rsyslog.d/ 目錄。這樣要修改比較方便,不需有 root 權限。
四、新增 net_status.conf 檔後,要使之生效請執行:
- sudo /etc/init.d/rsyslog restart
或
- sudo systemctl restart rsyslog.service
等 Wifi 狀態有所改變時,執行 sudo cat /tmp/.net_status_wl
看看有沒有新增訊息。本網誌曾發表過電腦和手機互用網路的方法,其中有開關 Wifi 的步驟,願意的人可以參考。
【監視記錄檔】
一、systemd 的服務有很多種檔案類型(Type),譬如:.service、.socket、.path、.target、.mount、.automount、.timer、.device、.scope、.swap、.slice 等。每一個都是一個 unit。因為本文的一個目的是要即時通知 Wifi 狀態的改變,所以必需持續監視,選擇的服務類型就一定是 .path。
Path 類型是指定一或多個路徑,讓 systemd 監視。當任一路徑有「指定的變化」時,啟動其指定的服務檔中所設定的程式。
二、所謂「指定的變化」可以用的選項有:
PathExists=
指定的檔案或目錄是否存在PathExistsGlob=
指定的檔案或目錄是否存在,設定值可以用 regular expression 格式PathChanged=
指定的檔案或目錄是否改變,這是看該路徑是否被關閉PathModified=
指定的檔案或目錄是否被修改,這是看該路徑是否有寫入的動作DirectoryNotEmpty=
指定的目錄是否至少含有一個檔案或目錄
本例選用 PathModified=
,而所用的檔名是 notify-wl-status.path。
三、notify-wl-status.path 的內容是:
- [Unit]
- Description=Notify Wifi Status
- [Path]
- PathModified=/tmp/.net_status_wl
- Unit=notify-wl-status.service
- [Install]
- WantedBy=multi-user.target
說明:
- 檔案要放在 /etc/systemd/system/ 目錄中;也可以放在其他目錄,再新增連結到前述目錄。
[Unit]
一節是設定該檔的一般資訊。Description
是該 unit 的名稱,自訂。[Path]
一節設定要監視的路徑(此處用PathModified=
,說明見上)和要連結的服務(用Unit=
選項設定)。Unit=
設定值不能是 .path 的 unit;官方文件建議用相同的檔名,只有副檔名不同,本例即依此原則命名。[Install]
一節是設定安裝該檔的資訊。WantedBy=
是列舉對應的 units。也就是使用systemctl enable
本檔時,要建立連結的地方。- .path 檔不能有
[Service]
一節,若有,會被忽略。 - 詳細說明請見
man 5 systemd.path
(談[Path]
)和man 5 systemd.unit
(談[Unit]
和[Install]
)。
註:本例是將此檔放在自家專用目錄中,再新增連結到 /etc/systemd/system/ 目錄。這樣要修改比較方便,不需有 root 權限。
四、檢查語法是否正確:
- systemd-analyze verify /etc/systemd/system/notify-wl-status.path
【連結的 .service 檔】
一、此類 .service 檔設定由 systemd 控管的程序(process)的資訊。notify-wl-status.service 檔的內容很簡單,只有必要的項目:
- [Unit]
- Description=Notify Wifi Status
- [Service]
- ExecStart=/usr/bin/bash /home/USER/.local/bin/notify_updown/notify-wl-up
說明:
- 檔名雖然可以自己決定,但最好和 .path 檔一樣,只差在副檔名用 .service。
- 檔案放在 /etc/systemd/system/ 目錄中;也可以放在其他目錄,再新增連結到前述目錄。
[Unit]
一節是設定該檔的一般資訊。Description
是該 unit 的名稱,自訂。[Service]
一節設定該服務特有的設定。ExecStart=
設定啟動該服務時要執行的指令,可含引數,也可以是一指令檔。若是檔案,可以放在任何目錄,但需用全路徑。- 詳細說明請見
man 5 systemd.service
(談[Service]
)。
註:本例是將本檔放在專用目錄中,再新增連結到 /etc/systemd/system/ 目錄。這樣要修改比較方便,不需有 root 權限。
二、檢查語法是否正確:
- systemd-analyze verify /etc/systemd/system/notify-wl-status.service
三、將上述 .path 和 .service 檔放到 /etc/systemd/system/ 目錄或修改內容後,用此指令重新載入:
- sudo systemctl daemon-reload
若沒有任何訊息,表示正確,便可啟動:
- sudo systemctl start notify-wl-status.path
notify-wl-status.service 檔不需啟動,它是被 .path 檔呼叫的。
看 .path 的狀態用:
- systemctl status notify-wl-status.path
若顯示綠字的 active (waiting),表示應該沒有問題,便可用下面指令安裝:
- sudo systemctl enable notify-wl-status.path
這會在 /etc/systemd/system/multi-user.target.wants/ 目錄新增 notify-wl-status.path 連結檔,連結的目標即是 /etc/systemd/system/notify-wl-status.path。如此一來,系統重新開機時,即會啟動 notify-wl-status.path,繼續監視 /tmp/.net_status_wl。
【處理 Wifi 狀態的指令】
上面設定啟動的程式是一指令檔 notify-wl-up,其內容如下:
#!/usr/bin/bash if [ "$(cat /sys/class/net/界面名稱/operstate)" == "up" ] #目前的 Wifi 狀態為 up 才執行 then env DISPLAY=:0 env XAUTHORITY=/home/USER/.Xauthority zenity --notification --text=" Wifi up" --window-icon=/home/USER/.local/bin/notify_updown/wifi-up.png --timeout=2 #顯示狀態 bash /home/USER/.local/bin/notify_updown/log-wl #開始記錄傳輸量 fi exit 0
說明:
- 一開始即篩選出 Wifi 為 up 的狀況,先顯示狀態,然後用 log-wl 記錄傳輸量。
- 此檔只處理 Wifi up 的狀況,而 Wifi down 是在下一檔 log-wl 才處理。
【記錄 Wifi 傳輸量】
log-wl 檔的指令是每隔一段自訂時間就讀取系統的 Wifi 傳輸值並記錄到指定的檔案。
系統的 Wifi 傳輸值是在 /sys/class/net/界面名稱/statistics/ 目錄中;rx_bytes 是目前接收量,tx_bytes 是目前傳送量,單位都是位元組(bytes)。這兩個檔案都各只有一個最新的值,而且系統關機就刪除,所以必需把它們記錄到其他的檔案,才能在日後查詢。
log-wl 檔內容如下:
#!/usr/bin/bash #記錄 Wifi 的傳輸量 Interval=60 #記錄的間隔,單位:秒;最小值是 60 秒,需為 60 的倍數 #輸出檔若不存在,新增之;更改權限 if [ ! -e /tmp/.wl-rx_bytes ]; then echo "1970-1-1 0:0 0 0" > /tmp/.wl-rx_bytes; fi if [ ! -e /tmp/.wl-tx_bytes ]; then echo "1970-1-1 0:0 0 0" > /tmp/.wl-tx_bytes; fi sudo chown USER /tmp/.wl-rx_bytes /tmp/.wl-tx_bytes sudo chmod g+w /tmp/.wl-rx_bytes /tmp/.wl-tx_bytes while true #持續記錄直到 Wifi 中止 do Wait=$(( ${Interval} - $(date +%S) )) #到指定的間隔尙需等待的時間 for (( i=${Wait}; i>0; i-- )) { #等待期間每秒檢查一次 Wifi 是否停止 sleep 1 if [ "$(cat /sys/class/net/界面名稱/operstate)" == "down" ] #若 Wifi 停止,即顯示狀態並中止程式 then env DISPLAY=:0 env XAUTHORITY=/home/USER/.Xauthority zenity --notification --text=" Wifi down" --window-icon=/home/USER/.local/bin/notify_updown/wifi-down.png --timeout=2 exit fi } Time=$(date +"%Y-%m-%d %H:%M ")$(( $(date +%s) / 10 * 10 )) #目前的時間,格式:年-月-日 時:分 epoch秒 #%s是「epoch秒」,即某時刻到 1970-01-01 00:00:00 UTC 的秒數 #調整記錄的時間到整分,即秒數為 0,差距最多只有 1 秒 RX=$(cat /sys/class/net/界面名稱/statistics/rx_bytes) #系統記錄的目前接收量,單位:bytes(位元組) if [ ${RX} -ne $(tail -n1 /tmp/.wl-rx_bytes | awk '{ print $4 }') ] #只有當目前值和前一次的記錄值不同才記錄 then echo "${Time} ${RX}" >> /tmp/.wl-rx_bytes #記錄內容是:年-月-日 時:分 epoch秒 接收量 fi TX=$(cat /sys/class/net/界面名稱/statistics/tx_bytes) #系統記錄的目前傳送量,單位:bytes(位元組) if [ ${TX} -ne $(tail -n1 /tmp/.wl-tx_bytes | awk '{ print $4 }') ] #只有當目前值和前一次的記錄值不同才記錄 then echo "${Time} ${TX}" >> /tmp/.wl-tx_bytes #記錄內容是:年-月-日 時:分 epoch秒 傳送量 fi done exit 0
說明:
- 記錄檔的格式是:
年-月-日 時:分 epoch秒 傳輸量
- 記錄的內容有「年-月-日 時:分」,這是給人看的;「epoch秒」是查詢傳輸量時計算間隔用的;二者是相同的時間。
- 間隔請修改成自己的偏好值,每十分鐘、每小時都可以,單位是秒;最小值是 60 秒,需為 60 的倍數。
- 輸出檔若不存在或內容格式不對,後續的指令會有問題,所以不但要先建立,還要加入符合格式的內容,此處用 epoch=0 的時間。
- 調整記錄的時間到整分是為了查詢方便,因為查詢只輸入到分鐘,不含秒。
- 在等待到整分的期間也有可能中止 Wifi,所以必需經常檢查,才能立刻通知並中斷記錄,此處每秒檢查一次。
- 只有當值不同時才記錄是為了節省空間,不然待機時也一直記錄相同的值,記錄檔會很大。
- 記錄檔放在 /tmp/ 目錄中是因為關機時會刪掉,再要記錄時才建立新檔,也就是有自動清除的功能。如果不清除的話,在查詢傳輸量時,會有前值比後值還要大的問題,讓傳輸量變成負的。
【查詢 Wifi 傳輸量】
一、說明:
- 本例用 zenity 的對話框詢問要查詢的起訖時間。但為了讓查詢者知道可以查詢的時間範圍,要先找出容許的起點和終點。若查詢的時間超出了容許範圍,就中止程式。
- 由於記錄的資料是表格的型式,而且計算與結果的顯示會有小數,所以用 awk 較適合。
- 系統記錄的傳輸量是用位元組(B)做單位,以下的指令會在適當的時機將單位改成較易閱讀的 KB 或 MB。
- 其他的說明請見檔內註解。
- 將下述的指令存成檔案後,改成可執行檔,直接執行此檔會比較方便。
二、query-wl 檔的內容如下:
#!/usr/bin/bash #計算 Wifi 的傳輸量 FileRx=/tmp/.wl-rx_bytes FileTx=/tmp/.wl-tx_bytes #記錄傳輸量的路徑;由 log-wl 檔設定 #若記錄檔第一行是自增的 1970-1-1...,即刪除之 if [ "$(head -n1 ${FileRx})" == "1970-1-1 0:0 0 0" ] then sed '1d' ${FileRx} > temp && mv temp ${FileRx} fi if [ "$(head -n1 ${FileTx})" == "1970-1-1 0:0 0 0" ] then sed '1d' ${FileTx} > temp && mv temp ${FileTx} fi MinEpochRx=$(head -n1 ${FileRx} | awk '{ print $3 }') MinEpochTx=$(head -n1 ${FileTx} | awk '{ print $3 }') #上面兩檔的第一行記錄著開始記錄的時間,將之取出,才能知道容許查詢的範圍 #因為有進出兩個值,為了查詢時不要去查比最小值還要小的,所以選二者中較晚的時間做極限 if [ ${MinEpochRx} -lt ${MinEpochTx} ] then MinEpoch=${MinEpochTx} else MinEpoch=${MinEpochRx} fi MinTime=$(date --date=@${MinEpoch} +"%Y-%m-%d %H:%M") #改為人能看得懂的格式 MaxEpochRx=$(tail -n1 ${FileRx} | awk '{ print $3 }') MaxEpochTx=$(tail -n1 ${FileTx} | awk '{ print $3 }') #上面兩檔的最後一行記錄著最晚的記錄時間,將之取出,才能知道容許查詢的範圍 #因為有進出兩個值,為了查詢時不要去查比最大值還要大的,所以選二者中較早的時間做極限 if [ ${MaxEpochRx} -lt ${MaxEpochTx} ] then MaxEpoch=${MaxEpochRx} else MaxEpoch=${MaxEpochTx} fi MaxTime=$(date --date=@${MaxEpoch} +"%Y-%m-%d %H:%M") #改為人能看得懂的格式 TimeBegin=$(zenity --forms --title="請輸入計量開始時間" --text=" 計量開始時間,有效範圍:${MinTime} ~ ${MaxTime} " --add-calendar="年、月、日" --add-entry="時:分 (24小時制,如 2:5)" --forms-date-format="%Y-%m-%d" --separator=" ") #詢問查詢的起點;因為要將輸入的值存起來,所以整個 zenity 指令用小括號包起來 #在說明文字中顯示有效的起訖時間 if [ $? -ne 0 ]; then exit; fi #若選 [Cancel],即中止程式 TimeBegin=$(echo ${TimeBegin} | sed "s/:/ /" | awk '{ printf "%s %02d:%02d", $1, $2, $3 }') #將輸入值改成標準格式 EpochBegin=$(date --date="${TimeBegin}" +%s) #轉換成 epoch秒,才好計算間隔 TimeEnd=$(zenity --forms --title="請輸入計量結束時間" --text=" 計量結束時間,有效範圍:${MinTime} ~ ${MaxTime} " --add-calendar="年、月、日" --add-entry="時:分 (24小時制,如 2:5)" --forms-date-format="%Y-%m-%d" --separator=" ") if [ $? -ne 0 ]; then exit; fi #若選 [Cancel],即中止程式 TimeEnd=$(echo ${TimeEnd} | sed "s/:/ /" | awk '{ printf "%s %02d:%02d", $1, $2, $3 }') EpochEnd=$(date --date="${TimeEnd}" +%s) #詢問查詢的終點,並轉換成 epoch秒 #檢查輸入的範圍是否在記錄的範圍內,若超出,計算會有問題,所以立刻中止程式 if [ ${EpochEnd} -lt ${MinEpoch} -o ${EpochEnd} -gt ${MaxEpoch} ] then echo "輸入值超出範圍" exit fi #計算並顯示傳輸量 #因為要用 zenity 顯示結果,所以把整個 awk 指令的輸出設成 zenity 要顯示的內容 zenity --info --no-wrap --text=\ "$(awk -v epoBegin="${EpochBegin}" -v epoEnd="${EpochEnd}" 'BEGIN { Flag=0; Unit="KB" } { #Flag 是用來標識是否在查詢範圍內 if ((Flag == 0) && ($3 >= epoBegin)) { #起點 EBegin=EEnd=$3 #範圍時間的起點 bytesBegin=bytesEnd=$4 #範圍內傳輸量的起始值 Flag=1 #進入查詢範圍,便將標記打開 sub("^.*/", "" ,FILENAME) #輸入檔的全路徑只取檔名 next #略過以下指令,直接讀入並處理下一筆資料 } if ((Flag == 1) && ($3 <= epoEnd)) { #範圍內持續更新終點值 EEnd=$3 bytesEnd=$4 if ($3 != epoEnd) { next } #若剛好是查詢的終點,可能已達檔末,再呼叫下一行,會有錯,所以先剔除這種狀況 } if ((Flag == 1) && ($3 >= epoEnd)) { #終點 Kbytes=(bytesEnd - bytesBegin) / 1024 #計算範圍內的傳輸量並將單位由 B 改成 KB switch (FILENAME) { #將不同檔案的值存到不同的變數 case ".wl-rx_bytes": #接收 rx=Kbytes break case ".wl-tx_bytes": #傳送 tx=Kbytes break default: break } Flag=0 #在處理下一檔前要將標記改成非在範圍內 nextfile } } END { #前面是取得所需的數據,END 段輸出結果 if (rx+tx > 2000) { rx /= 1024; tx /= 1024; Unit="MB" } #若用 MB 做單位較適合的話,就改成較易看懂的單位 printf "接收量/傳送量/總和: %.2f / %.2f / %.2f %s \n\n", rx, tx, rx+tx, Unit printf "間隔: %d 分鐘\n\n", (EEnd-EBegin)/60 printf "速率: %.2f / %.2f / %.2f %s/min\n", 60*rx/(EEnd-EBegin), 60*tx/(EEnd-EBegin), 60*(rx+tx)/(EEnd-EBegin), Unit #傳輸率是傳輸量除以真實間隔 #真實間隔不一定是查詢的範圍 }' ${FileRx} ${FileTx})" #接收和傳送都用相同指令處理,所以接連地讀入 exit 0
【查詢 Wifi 傳輸量的累積圖】
一、說明:
- 前法是計算所選範圍的總值,此法是顯示每隔一指定間隔的傳輸量與其累積圖,優點是看得到隨時間的變化。
- 此程式碼可查詢有線或無線、接收或傳送的結果,但需先依前面所述,完成傳輸量的記錄才行。有線網路只要修改界面名稱等相關資訊即可,因內容大同小異,故不再贅述。
- 使用方法:開啟虛擬終端機,將視窗最大化,執行:
- bash query {en|wl} {rx|tx} interval
en
:ethernet/有線, wl
:wireless/Wifi,二選一,在前rx
:接收, tx
:傳送,二選一,在後interval
: 間隔秒數,正整數;建議至少 60,如:600(十分鐘)、1800(半小時)、3600(一小時)- 輸出的內容是每隔一指定的時間,顯示日期、時間、傳輸量(以 MB 為單位)、累積圖。
- 其他的說明請見檔內註解。
二、query 檔內容如下:
#!/usr/bin/bash #查詢網路的傳輸量;有線或無線,接收或傳送皆可 #檢查語法是否正確 case ${1} in "en" | "wl" ) ;; * ) echo " 使用方法:bash query {en|wl} {rx|tx} interval" echo " en:ethernet/有線, wl:wireless/Wifi,二選一,在前" echo " rx:接收, tx:傳送,二選一,在後" echo " interval: 間隔秒數,正整數;建議至少 60" exit 1;; esac #檢查語法是否正確 case ${2} in "rx" | "tx" ) ;; * ) echo " 使用方法:bash query {en|wl} {rx|tx} interval" echo " en:ethernet/有線, wl:wireless/Wifi,二選一,在前" echo " rx:接收, tx:傳送,二選一,在後" echo " interval: 間隔秒數,正整數;建議至少 60" exit 1;; esac #檢查間隔是否為正整數,至少十秒 if [ ${3} -lt 10 ] #若非整數,會有錯誤訊息 then echo "間隔秒數有問題" exit 1 fi File=/tmp/.${1}-${2}_bytes #記錄傳輸量的路徑;由 log-en/log-wl 檔設定 Interval=${3} #計算的間隔;單位:秒 if [ "$(head -n1 ${File})" == "1970-1-1 0:0 0 0" ] #若記錄檔第一行是自增的 1970-1-1...,即刪除之 then sed '1d' ${File} > temp && mv temp ${File} fi MinTime=$(head -n1 ${File} | awk '{ print $1, $2 }') MinEpoch=$(head -n1 ${File} | awk '{ print $3 }') #上檔的第一行記錄著開始記錄的時間,將之取出,才能知道容許查詢的範圍 MaxTime=$(tail -n1 ${File} | awk '{ print $1, $2 }') MaxEpoch=$(tail -n1 ${File} | awk '{ print $3 }') #上檔的最後一行記錄著最晚的記錄時間,將之取出,才能知道容許查詢的範圍 TimeBegin=$(zenity --forms --title="請輸入計量開始時間" --text=" 計量開始時間,有效範圍:${MinTime} ~ ${MaxTime} " --add-calendar=" 年、月、日" --add-entry="時:分 (24小時制,如 2:5)" --forms-date-format="%Y-%m-%d" --separator=" ") #詢問查詢的起點;因為要將輸入的值存起來,所以整個 zenity 指令用小括號包起來 #在說明文字中顯示有效的起訖時間 if [ $? -ne 0 ]; then exit 2; fi #若選 [Cancel],即中止程式 TimeBegin=$(echo ${TimeBegin} | sed "s/:/ /" | awk '{ printf "%s %02d:%02d", $1, $2, $3 }') #將輸入值改成標準格式 EpochBegin=$(date --date="${TimeBegin}" +%s) #轉換成 epoch秒,才好計算間隔 TimeEnd=$(zenity --forms --title="請輸入計量結束時間" --text=" 計量結束時間,有效範圍:${MinTime} ~ ${MaxTime} " --add-calendar=" 年、月、日" --add-entry="時:分 (24小時制,如 2:5)" --forms-date-format="%Y-%m-%d" --separator=" ") if [ $? -ne 0 ]; then exit 2; fi #若選 [Cancel],即中止程式 TimeEnd=$(echo ${TimeEnd} | sed "s/:/ /" | awk '{ printf "%s %02d:%02d", $1, $2, $3 }') EpochEnd=$(date --date="${TimeEnd}" +%s) #詢問查詢的終點,並轉換成 epoch秒 #檢查輸入的範圍是否在記錄的範圍內,若超出,計算會有問題,所以立刻中止程式 if [ ${EpochEnd} -lt ${MinEpoch} -o ${EpochEnd} -gt ${MaxEpoch} ] then echo "輸入值超出範圍" exit 1 fi BytesEnd=$(cat ${File} | grep ${EpochEnd} | awk '{ print $4 }') #查詢範圍之最大傳輸量;用來調整累積圖不要超過一行,暫定 100 個 marks #計算並顯示傳輸量 awk -v epoBegin=${EpochBegin} -v epoEnd=${EpochEnd} -v interv=${Interval} -v BEnd=${BytesEnd} 'BEGIN { Flag=0 #標識是否在查詢範圍內;0 否;1 是 Counter=1 #用來計算間隔數 Factor=BEnd/1024/1024/100 #調整累積圖不要超過 100 個 marks,以免跨行;虛擬終端機視窗要最大化 printf(" 年-月-日 時:分 傳輸MB 累積圖\n") #欄名 } { Mbytes=$4/1024/1024 #該行所列之傳輸量換算為 MB if ((Flag == 0) && ($3 >= epoBegin)) { #起點 EBegin=$3 printf("%s %s %5d %s\n", $1, $2, Mbytes, mark(Mbytes, Factor)) Flag=1 #進入查詢範圍,便將標記打開 next #略過以下指令,直接讀入並處理下一筆資料 } if ((Flag == 1) && ($3 < epoEnd)) { #範圍內持續輸出 Value=EBegin+Counter*interv #該間隔的時間 if ($3 >= Value) { #達到間隔的時間才輸出 printf("%s %s %5d %s\n", $1, $2, Mbytes, mark(Mbytes, Factor)) Counter+=(1+int(($3-Value)/interv)) #下一個間隔;若中間缺數據,要補上所缺的間隔數 } else { if ($3 != epoEnd) { next } #未達間隔時間,便處理下一行 #但若剛好是查詢的終點,可能已達檔末,再呼叫下一行,會有錯,所以先排除這種狀況 } } if ((Flag == 1) && ($3 >= epoEnd)) { #終點 printf("%s %s %5d %s\n", $1, $2, Mbytes, mark(Mbytes, Factor)) exit 0 } } #製作累積圖的一條,用星號做 marks function mark(mbytes, factor, str, i) { if (factor < 1) { factor=1 } #只縮小,不放大 for (i=1; i<=mbytes/factor; i++) { str=str "*" } return str }' ${File} #return value: 0 normal, 1 error, 2 quit
【後語】
一、如果開關 Wifi 並沒有看到通知,應該是因為未經由 .path unit 的關係。
二、若有問題,用 sudo cat /var/log/syslog
或 sudo journalctl -xe
看看是哪裡錯了。
三、本例是在自己組合的 Debian GNU/Linux 10 系統撰寫與測試的,不同的系統,目錄、檔名可能會不一樣,請自行修改。
沒有留言:
張貼留言