尝试阅读理解一份linux shell脚本
以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「englyf」 https://mp.weixin.qq.com/s/YkJsqlqsviWtHHArONv3Rw
从头一二去阅读语法和命令说明,对于脚本小白来说比较枯燥,难以坚持,所以这里选择对一份完整的shell脚本代码来逐行逐段解读,希望可以一渡小白,帮助我们快速进入脚本的大门_。
司机要开车了:
#!/bin/sh
用注释的形式说明文件打开类型,此处意指本脚本需要用 /bin/sh 打开。
#V1.0 Added hardware type detection
#V1.1 xxxx-xx-xx 重构脚本
#V1.2 xxxx-xx-xx 将日志信息输出到U盘
#V1.3 xxxx-xx-xx 读取存储的检测结果值
#V1.4 xxxx-xx-xx 添加校验程序版本,U盘下有对应版本信息文件才校验
上面几行表示注释内容。单行注释,以
#
开始
UDiskMountDir=$(df | grep /mnt/sd | awk 'END{print $NF}')
声明变量 UDiskMountDir 并赋值。
$()
括号内填入命令并执行,最后返回输出到变量 UDiskMountDir。
df
命令显示liinux系统上的文件系统磁盘使用情况统计。后边还可以带选项,执行更复杂的输出。命令后边加 '|' 表示将此命令的输出作为后边紧接着的命令的输入。
grep
命令用于对文本按行搜索然后输出该行。英文全称 global search regular expression(RE) and print out the line。
grep /mnt/sd
表示搜索输入信息里包含
/mnt/sd
的一行,并输出该行。
awk命令用于对文本处理,END表示执行最后的运算或者打印最终的输出结果,这里用于打印输出, NF 表示列数,print NF 表示打印最后一个字段,各个字段默认以空格划分,可用选项 -F指定使用什么字符串来划分。
LogFile=${UDiskMountDir}/Debug.log
声明变量 LogFile 并以右边的内容赋值。将变量 UDiskMountDir 所代表的路径名与后边的字符串结合为新的文件名。一般命令中用到的文件名,要不是带相对路径的文件名,如
./diretion/filename
,就是带绝对路径的文件名,如
/root/diretion/filename
。
${UDiskMountDir}
表示引用变量 UDiskMountDir 的值。
AllCheckPassed=true
声明变量 AllCheckPassed 并赋值为true。
AddError()
AllCheckPassed=false
echo "-200" > /tmp/VerCheckRes
}
声明函数 AddError,输入参数无需声明。
变量 AllCheckPassed 赋值为true。
echo
命令用于输出字符串。
echo "-200" > /tmp/VerCheckRes
将 -200 输出到文件 /tmp/VerCheckRes 并覆盖原有内容。如果将符号
>
换成
>>
则是将内容追加到最后位置。
AddRecord()
if [ ! -d "/userdata/recordsDir/" ];then
mkdir -p /userdata/recordsDir/
echo "$*" > /userdata/recordsDir/test
AddDebugLog2UDisk $*
}
声明函数 AddRecord。
if
表示 if 语句的开始。
if[]
方括号内填判断条件,如为 true 则执行 then 后的语句,否则跳出 if 语句。
fi
表示 if 语句的结束。和 C 语言类似,if 语句也可以有
else
甚至
else if
(shell 内应该写成
elif
)。
if/elif
和
then
如果不在同一行则可以省略符号
;
。
if [ ! -d "/userdata/recordsDir/" ]
判断目录 /userdata/recordsDir/ 是否存在,并对结果值取反(
!
)。
mkdir
命令用于构建目录。带选项
-p
表示构建路径下所有的目录。
mkdir -p /userdata/recordsDir/
创建目录 /userdata/recordsDir/, /userdata/ 如果不存在也会被构建。
echo "
sync
命令用于数据同步。Linux 系统中写入硬盘的数据往往会被先存放于 buffer 中,这样是为了效率起见,但是如果系统突然断电,那么数据就会丢失,这时可以调用 sync 将 buffer 中的数据同步到硬盘。
AddDebugLog2UDisk $*
调用函数 AddDebugLog2UDisk 并传入当前函数(AddRecord)的所有参数。
AddDebugLog2UDisk()
echo "$*" >>"$LogFile"
}
声明函数 AddDebugLog2UDisk。
echo "\
sync
同步数据到硬盘。
#生成下/tmp/App/version.txt版本信息
注释内容
rm -f "$LogFile"
rm
命令用于移除文件。选项
-f
表示强制。
rm -f "$LogFile"
强制移除变量 LogFile 代表的文件,变量 LogFile 存储的是文件名,包括路径。
echo "0" > /tmp/VerCheckRes
输出字符串 '0' 到文件 /tmp/VerCheckRes
sh /data/bin/run_normal.sh
在当前的环境中使用另一个shell来执行脚本文件 /data/bin/run_normal.sh,当前环境中变量的值可以在新脚本(/data/bin/run_normal.sh)中被使用(非引用,如果使用 source则变为引用),新脚本文件属性可以无执行权限。
sh
后边可以带选项
-n
、
-x
等,
-n
用于进行shell脚本的语法检查,
-x
用于实现shell脚本的逐句跟踪调试并打印该行命令和状态等。
sleep 2
阻塞当前进程,睡眠 2 秒,和
sleep 2s
同样效果。
sleep 2h
表示睡眠 2 小时。
sh /tmp/App/kill.sh
执行脚本文件 /tmp/App/kill.sh
killall MachineCheckNode
杀掉进程 MachineCheckNode,MachineCheckNode 为进程名。
lastSNScanResult=$(cat /userdata/recordsDir/scan)
读取文件 /userdata/recordsDir/scan 内容并赋值给变量 lastSNScanResult。
cat
命令用于连接文件并输出内容到标准输出。
AddDebugLog2UDisk "last sn scan result:$lastSNScanResult"
调用函数 AddDebugLog2UDisk,并传递字符串 last sn scan result: lastSNScanResult, lastSNScanResult 获取变量 lastSNScanResult 的值然后和前面的字符串结合成新的字符串。
#检查SN和markData
if [ ! -f /data/bin/sysData/sn ]; then
AddRecord "sn missing"
AddError
snContent=$(cat /data/bin/sysData/sn)
AddRecord "sn is:$snContent"
snLen=$(cat /data/bin/sysData/sn | awk '{print length($0)}')
if [ "$snLen" != "16" ] ; then
AddRecord "sn length error $snLen"
AddError
fi
if [ ! -f /data/bin/sysData/sn ]
判断文件 /data/bin/sysData/sn 是否是常规文件而且存在,'!' 表示对结果值取反。
如果文件 /data/bin/sysData/sn 找不到或者不是常规文件,则调用后边的语句块,直到 else 为止。调用函数 AddRecord(传入字符串 "sn missing") 和 AddError(无参数传入)。
否则,获取文件内容并赋值给变量 snContent,调用函数 AddRecord(输入 sn is: snContent", 字符串其中会插入变量 snContent 的值);声明变量 snLen,赋值为命令 cat /data/bin/sysData/sn | awk '{print length(
(cat /data/bin/sysData/sn | awk '{print length( 0)}') 读取文件 /data/bin/sysData/sn 的内容,通过管道('|')输入到后一命令语句,awk 计算输入内容的第一字段的字符长度并输出。
if [ ! -f /data/bin/sysData/markData ]; then
AddRecord "markData missing"
AddError
fi
判断文件 /data/bin/sysData/markData 是否不存在,不存在则调用后边的语句(以 fi 为止)。
#检验版本信息
while read -r line || [ -n "${line}" ]; do
[ "$line" = "" ] && continue
item_filename=$(echo "$line" | awk -F":" '{print $1}')
item_filemd5=$(echo "$line" | awk -F":" '{print $2}')
if [ ! -e "$item_filename" ]; then
AddRecord "$item_filename missing"
AddError
acture_md5=$(md5sum "$item_filename" | awk '{print $1}')
if [ "$item_filemd5" = "$acture_md5" ]; then
continue
AddRecord "$item_filename" "md5 diff! file:" "$item_filemd5" "acture:" "$acture_md5"
AddError
done </tmp/MachineDecDir/check_md5list
while read xxx; do
...
done </filename
循环读取文件名 filename 所指向的文件的内容,每读取一行存入 xxx 变量。
read xxx
为 while 语句的判断语句,判断语句和
do
如果不在同一行,则
;
可省略,如:
while read xxx
do
...
done </filename
read 后边的
-r
表示读取内容过程中对特殊字符有效,如
/
(输入未结束需要换行继续输入的特殊符号),'/n' 等等。
[ -n "${line}" ]
判断 line 的内容不为空。而前边的 '||' 表示前一语句
read -r line
返回 false 才执行这个语句。
[ "$line" = "" ] && continue
表示如果 line 的内容为空则执行 continue;continue 命令用于跳过当前循环内容回去执行 while 判断语句。
item_filename= (echo "$``$1}') 声明变量 item_filename 并以后边语句执行结果赋值。awk -F":" '{print
if [ ! -e "$item_filename" ]
用于判断变量 item_filename 代表的文件名所指的文件是否不存在。
!
表示取反。if 判断的内容为 true 则执行 then 后边的语句,直到 fi 为止,否则跳过。
md5sum "$item_filename"
表示计算变量 item_filename 代表的文件名所指的文件的MD5值。
| awk '{print $1}'
将前一语句的输出通过管道连接到后边的这个语句的输入,默认按照空格划分内容并输出第1个字段内容。
if [ "
#找到 U 盘路径
UDiskMountDir=$(df | grep /mnt/sd | awk 'END{print $NF}')
#校验mcu程序版本,U盘下有mcuversion.txt才校验
UDiskMountDirfile="${UDiskMountDir}/SpecialDir/mcuversion.txt"
CurMcuVersionFile="/tmp/mcuversion.txt"
if [ -f "$UDiskMountDirfile" ]; then
TargetMcuVersion=$(cat "$UDiskMountDirfile")
if [ -f "$CurMcuVersionFile" ]; then
CurMcuVersion="$(cat "$CurMcuVersionFile")"
if [ "$TargetMcuVersion" != "$CurMcuVersion" ]; then
AddRecord "mcuversion diff cur:$CurMcuVersion target:$TargetMcuVersion"
AddError
AddRecord "$CurMcuVersionFile not found"
AddError
fi
UDiskMountDirfile="${UDiskMountDir}/SpecialDir/mcuversion.txt"
声明变量 UDiskMountDirfile 并赋值为其后的内容,其后是将变量 UDiskMountDir 所代表的路径名与后边的字符串结合为新的文件名。
if 语句可以有多重嵌套,如上。
[ "
#写入检查结果
if [ $AllCheckPassed = "true" ]; then
AddRecord "Pass"