shell腳本初學與linux命令學習
shell腳本編寫
由於工作原因,目前遇到一個問題,要求對程序所在服務器進行實時監控,主要監控程序的CPU利用率,程序的內存佔用率。剛開始考慮用代碼去寫,考慮了下,還得java,再結合linux命令,寫起來有點麻煩,於是想到了直接用shell編寫一個定時腳本。
需求
**文件編碼使用utf-8格式編碼。文件產生頻率爲每5分鐘產生一個文件,新產生的文件覆蓋上一次產生的文件。文件格式爲xml文件,格式樣例如下: **
編寫生成各字段要求的值
#!/bin/bash
#author weixg
#xml文件名
fileName="zhywgl_ywgl_waywgl.xml"
#系統程序名(進程名)
procName="FlowServer"
#版本號
procVersion="1.0"
#最近啓用時間
procStartTime=`ps -eo lstart,command| grep -v 'grep'| grep -w $procName | awk '{print($1" "$2" "$3" "$4" "$5);}'`
#程序接收流量速率
inputRate="-1"
#程序輸出流量速率
outputRate="-1"
#程序接收日誌量大小
inputLog="-1"
#程序輸出日誌量大小
outputLog="-1"
#統計週期,以分鐘爲單位
duration="5"
#提取本服務器的ip地址信息
IP=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"`
#服務器用戶默認root
psUser=root
#查看進程是否存在
pid=`ps -ef | grep -w $procName | grep -v 'grep' | awk '{print $2;}'`
if [ "$pid" != "" ]; then
ProcAlive=1
else
ProcAlive=0
fi
#cpu使用率
ProcCpu=`ps aux |grep -w $procName | grep -v 'grep'| awk '{print($3);}'`
#程序內存使用率
ProcMem=`ps aux |grep -w $procName | grep -v 'grep'| awk '{print($4);}'`
#時間戳
Timestamp=`date +%s`
echo "fileName is $fileName"
echo "procName is $procName"
echo "procVersion is $procVersion"
echo "inputRate is $inputRate"
echo "outputRate is $outputRate"
echo "inputLog is $inputLog"
echo "outputLog is $outputLog"
echo "duration is $duration"
echo "pid is $pid"
echo "ProcAlive is $ProcAlive"
echo "ProcCpu is $ProcCpu"
echo "ProcMem is $ProcMem"
echo "IP is $IP"
echo "procStartTime is $procStartTime"
echo "Timestamp is $Timestamp"
提取本服務器的ip地址信息
IP=/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"
分析下這個命令:
/sbin/ifconfig -a 查詢本機ip
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.0.3 netmask 255.255.240.0 broadcast 172.16.15.255
inet6 fe80::5054:ff:fe68:1c8a prefixlen 64 scopeid 0x20<link>
ether 52:54:00:68:1c:8a txqueuelen 1000 (Ethernet)
RX packets 13295352 bytes 3943242853 (3.6 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 12367502 bytes 2298562223 (2.1 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 2898759 bytes 338158163 (322.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2898759 bytes 338158163 (322.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
/sbin/ifconfig -a|grep inet 過濾篩選出inet的值
inet 172.16.0.3 netmask 255.255.240.0 broadcast 172.16.15.255
inet6 fe80::5054:ff:fe68:1c8a prefixlen 64 scopeid 0x20<link>
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1從上一步過濾去掉(反匹配)值爲127.0.0.1的信息
inet 172.16.0.3 netmask 255.255.240.0 broadcast 172.16.15.255
inet6 fe80::5054:ff:fe68:1c8a prefixlen 64 scopeid 0x20<link>
inet6 ::1 prefixlen 128 scopeid 0x10<host>
/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 同上一步的作用一樣
inet 172.16.0.3 netmask 255.255.240.0 broadcast 172.16.15.255
/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk ‘{print $2}’ 輸出第二位的值
172.16.0.3
/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk ‘{print $2}’|tr -d “addr:” tr -d 的作用是刪除addr字符串,由於沒有,所以從上一步還是返回的ip值
172.16.0.3
程序存活1</ ProcAlive >
#查看進程是否存在
pid=`ps -ef | grep -w $procName | grep -v 'grep' | awk '{print $2;}'`
if [ "$pid" != "" ]; then
ProcAlive=1
else
ProcAlive=0
fi
$procName是變量名
ps -ef | grep -w $procName
查詢出進程
ps -ef | grep FlowServer
root 5596 15476 0 17:10 pts/1 00:00:00 grep --color=auto FlowServer
root 31207 1 0 Nov13 ? 00:07:05 java -jar /home/java/FlowServer-1.0.jar
過濾 -v (反匹配) 有 ‘grep’的
ps -ef | grep FlowServer | grep -v 'grep’
root 31207 1 0 Nov13 ? 00:07:05 java -jar /home/java/FlowServer-1.0.jar
ps -ef | grep -w FlowServer | grep -v ‘grep’ | awk '{print $2;} 輸出字符串第二位
31207
補充
算術比較運算符 詳情查看[:https://blog.csdn.net/ithomer/article/details/6836382](https://blog.csdn.net/ithomer/article/details/6836382)
-eq 等於 [ 3 -eq $mynum ]
-ne 不等於 [ 3 -ne $mynum ]
-lt 小於 [ 3 -lt $mynum ]
-le 小於或等於 [ 3 -le $mynum ]
-gt 大於 [ 3 -gt $mynum ]
-ge 大於或等於 [ 3 -ge $mynum ]
————————————————
程序cpu使用率0.43
#cpu使用率
ProcCpu=ps aux |grep -w $procName | grep -v 'grep'| awk '{print($3);}'
程序內存使用率0.5
#程序內存使用率
ProcMem=ps aux |grep -w $procName | grep -v 'grep'| awk '{print($4);}'
解析:
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 125492 3696 ? Ss Nov04 3:14 /usr/lib/systemd/systemd -
root 2 0.0 0.0 0 0 ? S Nov04 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S Nov04 0:04 [ksoftirqd/0]
root 31207 0.0 15.1 3591284 587332 ? Sl Nov13 7:05 java -jar /home/java/FlowS
ps aux |grep -w $procName 過濾進程名的信息
root 8478 0.0 0.0 112708 1004 pts/1 S+ 17:25 0:00 grep --color=auto -w FlowServer
root 31207 0.0 15.1 3591284 587332 ? Sl Nov13 7:05 java -jar /home/java/FlowServer-1.0.jar
ps aux |grep -w $procName | grep -v ‘grep’ 過濾取出含有‘grep’的數據
root 31207 0.0 15.1 3591284 587332 ? Sl Nov13 7:05 java -jar /home/java/FlowServer-1.0.jar
ps aux |grep -w $procName | grep -v ‘grep’| awk ‘{print($4);}’ 輸出第四位的數值
15.1
時間戳
Timestamp=date +%s
當前時間 格式爲時間戳
時間命令:date
向date命令傳遞參數適用‘+‘(加號),在傳遞的參數中
%Y表示年
%m表示月
%d表示天
%H表示小時(表示的時間是00-23)
%M表示分鐘
%S表示秒
%s(表示unix時間戳的秒數)
1.1例如 date +%Y-%m-%d
這個在我的機器上面的結果是:
date命令的輸出結果是對當前時間的以傳遞的參數進行格式化
1.2例如date +’%Y-%m-%d %H:%M:%S’
1.3獲取當前時間的unix時間戳
date +%s
1.4使用date命令獲取一個特定時間的unix時間戳
最近啓用時間
procStartTime=ps -eo lstart,command| grep -v 'grep'| grep -w $procName | awk '{print($1" "$2" "$3" "$4" "$5);}'
生成xml腳本
此編碼邏輯是從網上其他大神那裏找到的
#! /bin/bash
#author:weixg
#data:20191120
#filename:create_xml.sh
#從外部傳入的第一個參數作爲xml的文件名
outfile=/var/log/wg/zhywgl_ywgl_waywgl.xml
#xml中的縮進位
tabs=0
# ++++++++++++++++++++++++++++
# 組裝一個節點,輸出到文件
# 說一說傳參數時的這幾個區別:假如有下面這個腳本執行的命令
# /path/to/scriptname opt1 opt2 opt3 opt4
# $0: 的值是默認是腳本的名字,從$1-$4 開始就是參數的值
# $# :代表後接的參數『個數』
# $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每個變量是獨立的(用雙引號括起來);
# $* :代表『 "$1c$2c$3c$4" 』,其中 c 爲分隔字節,默認爲空白鍵, 所以本例中代表『 "$1 $2 $3 $4" 』之意。
# 在shell中我們可以也可以使用${}包含變量名,來調用變量
# ++++++++++++++++++++++++++++
put_head2(){
echo '<?'${1}'?>' > $outfile
}
put(){
echo '<'${*}'>' >> $outfile
}
# 這裏也是輸出一個xml的節點,只是比上面的節點有更多的設置
# ${@:2} 的意思:它的值就是由第二個參數開始到最後一個參數,爲什麼要這樣?有時可能你的第二個參數中有空格,shell接受參數是以空格計算的
put_tag(){
echo '<'$1'>'${@:2}'</'$1'>' >> $outfile
}
# 同樣是一個輸出節點函數,但是添加了CDATA,防止特殊字符造成xml解析失敗
put_tag_cdata() {
echo '<'$1'><![CDATA['${@:2}']]></'$1'>' >> $outfile
}
put_head(){
put '?'${1}'?'
}
# 這是一個縮進的算法,自行理解
out_tabs(){
tmp=0
tabsstr=""
while [ $tmp -lt $((tabs)) ]
do
tabsstr=${tabsstr}'\t'
tmp=$((tmp+1))
done
echo -e -n $tabsstr >> $outfile
}
tag_start(){
out_tabs
put $1
tabs=$((tabs+1))
}
tag() {
out_tabs
if [ "$1" == 0 ]
then
put_tag $2 $(echo ${@:3})
elif [ "$1" == 1 ]
then
put_tag_cdata $2 $(echo ${@:3})
fi
}
tag_end(){
tabs=$((tabs-1))
out_tabs
put '/'${1}
}
調用方式如下
此處調用是在上面各字段生成的腳本中調用
source '/home/java/create_xml.sh'
put_head2 'xml version='1.0' encoding="GBK"'
tag_start 'Program'
put_tag 'ProcName' "$procName"
put_tag 'ProcVersion' "$procVersion"
put_tag 'ProcAlive' "$ProcAlive"
put_tag 'ProcStartTime' "$procStartTime"
put_tag 'IP' "$IP"
put_tag 'ProcCpu' "$ProcCpu"
put_tag 'ProcMem' "$ProcMem"
put_tag 'InputRate' "$inputRate"
put_tag 'OutputRate' "$outputRate"
put_tag 'InputLog' "$inputLog"
put_tag 'OutputLog' "$outputLog"
put_tag 'Duration' "$duration"
put_tag 'Timestamp' "$Timestamp"
tag_end 'Program'
echo `date +'%Y-%m-%d %H:%M:%S'`+"執行成功!">>/home/java/shLogs.txt
配置定時器
vi /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
*/5 * * * * root /home/java/wangguan.sh 2> /home/java/log 2>&1
*/5 * * * * root /home/java/wangguan.sh 2> /home/java/log 2>&1
解析該命令:
*/5 * * * * :每五分鐘執行一次該腳本
root :爲用戶
/home/java/wangguan.sh :要執行的腳本
2 :表示標準錯誤,stderr。
> : 代表覆蓋形式寫入
/home/java/log :日誌地址
&1 :文件描述符 1,而1標識標準輸出,stdout。
2>&1: 將標準錯誤重定向到標準輸出
碰到的異常
在執行定時任務的時候,日誌中報出 Permission denied 沒有權限
上網查詢:
linux中,執行sh顯示Permission denied,是由於沒有權限的原因,爲了獲得權限,執行如下命令:
chmod 777 run.sh
其中“run.sh”是我要執行的文件,“chmod 777”是授權語句,“chmod 777 run.sh”的意思是對"run.sh"文件權限進行設置。
在修改定時文件後,要重啓定時服務
service crond start
報異常:Redirecting to /bin/systemctl start crond.service
出現Redirecting to /bin/systemctl start crond.service,
即service crond start 需要替換爲systemctl方式