Linux Shell脚本
脚本解释器
# which bash
#!/usr/bin/bash
#!/usr/bin/env bash
#!/bin/bash
#!/bin/env bash
demo脚本
#!/usr/bin/bash
SCRIPT_DIR=$( cd $(dirname $0); pwd )
SCRIPT_NAME=$(basename $0)
SCRIPT_ABS_NAME="${SCRIPT_DIR}/${SCRIPT_NAME}"
function logger() {
echo "[$(date "+%Y%m%d %H:%M:%S.%3N")] [pid-$$] [${SCRIPT_NAME}:$(caller | awk '{printf "%s",$1}')] | $*"
}
function main() {
logger "script start: ${SCRIPT_ABS_NAME} pid=$$ ppid=$(ps -p $$ -oppid= | grep -oE '[0-9]+')"
logger "script end: ${SCRIPT_ABS_NAME}"
}
main
# 后台执行: bash xxxxx.sh &>>xxxxx.log &
# 后台执行: bash xxxxx.sh &>>"temp_$(date '+%Y%m%d_%H%M%S').log" &
$特殊含义
$? # 上一条命令的返回值(0表示正常,其他表示错误)
$# # 参数个数
$* # 所有参数列表
$@ # 所有参数列表
$0 # Shell本身的文件名
$1~$n # 添加到Shell的各参数值
$$ # Shell本身的pid
$! # Shell最后运行的后台Process的PID
if判断
# 字符串判断
# = 等于
# != 不等于
# -z 字符串长度为0(为空)(空格和tab也算空)(推荐字符串判空)
# -n 字符串长度不为0(非空)(不推荐使用)
# =~ 模式匹配(正则表达式)
# 文件判断
# -e 文件存在
# -d 文件为目录
# -f 文件为普通文件
# 判断上一条命令返回码
if [ $? -eq 0 ]; then
echo 'res is success'
else
echo 'res is fail'
fi
# 字符串非空判断
if [ ! -z ${xxxxx} ]; then
echo '字符串为空'
else
echo 'else执行'
fi
# 给文件最后一行添加空行
[ ! -z $(tail -1 xxxxx) ] && echo '' >> xxxxx
t_file='/home/xxxx'
if [ ! -e $t_file ]; then
echo "${t_file} 不存在"
elif [ -d $t_file ]; then
echo "${t_file} 是目录"
elif [ -f $t_file ]; then
echo "${t_file} 是文件"
else
echo "${t_file} 无法识别"
fi
echo $(realpath $t_file)
if数字判断
temp_num=5
if ((${temp_num} >= 5)); then
echo "if执行"
else
echo "else执行"
fi
# -gt 大于
# -ge 大于等于
# -lt 小于
# -le 小于等于
# -eq 等于
# -ne 不等于
[ xxx -gt 500 ]
字符串处理
# 字符串长度
str="Hello"
length=${#str}
echo $length # 输出:5
# 字符串截取
str="Hello World"
substring=${str:6:5}
echo $substring # 输出:World
# 去除前缀、去除后缀
str="path/to/file.txt"
prefix_removed=${str#path/}
echo $prefix_removed # 输出:to/file.txt
suffix_removed=${str%.txt}
echo $suffix_removed # 输出:path/to/file
多行文本处理
# 管道符和while命令
find '/root/xxx' | while read line; do
[ -d ${line} ] && echo "文件夹: ${line}" || ll ${line}
done
# for命令
for line in $(find / -name 'xxxx'); do
[ -d ${line} ] && echo "文件夹: ${line}" || ll ${line}
done
# 来自文件
while read line; do
echo "${line}"
done <zzz.txt
# 示例:给输出结果加上时间戳
ping 127.0.0.1 | while read line; do
echo "[$(date '+%Y-%m-%d %H:%M:%S.%3N')] ${line}"
done
xxxxx | while read line; do echo "[$(date '+%Y-%m-%d %H:%M:%S.%3N')] ${line}"; done
借助python处理命令执行结果
(多个命令结果)利用python解析json
cmd_res=$(echo '{"key1":"value1","key2":"value2"}')
python3 -c "
input = r'''${cmd_res}'''
import json
j = json.loads(input)
print(j)
print(j['key1'])
print(j['key2'])
"
(单条命令)通过管道符,用python的sys.stdin接收
ls -l | python3 -c '
import sys
for e in sys.stdin.readlines():
# print(e.rstrip())
print(e, end="")
'
(持续输出的命令)命令加时间戳(ping 127.0.0.1 | python3 xxxxx.py)
ping 127.0.0.1 | python3 -c '
import datetime
import sys
def add_timestamp():
f = sys.stdin
while True:
line = f.readline()
timestamp = str(datetime.datetime.now())[0:-3]
print(f"[{timestamp}] {line}", end="", flush=True)
if __name__ == "__main__":
try:
add_timestamp()
except:
pass
'
文件权限判断
# 文件权限数字形式判断
if [ $(stat -c %a xxxxx) != 500 ]; then
echo "if执行"
else
echo "else执行"
fi
# 文件属组判断
if [ $(stat -c %U:%G xxxxx) != 'user:group' ]; then
echo "if执行"
else
echo "else执行"
fi
for循环
for ((i = 0; i < 10; i++)); do
echo $i
sleep 1s
done
# 输出10~1
for i in {10..1..-1}; do
echo $i
done
while
sum=0
while ((${sum} < 10)); do
echo ${sum}
sum=$((sum + 1))
sleep 1
done
echo "end: ${sum}"
# 死循环
while true; do
echo 'xxxxx'
sleep 1
done
日志功能
function logger_info() {
shell_param="[$(date "+%Y%m%d %H:%M:%S")] INFO [$(caller | awk '{printf "%s:%s",$2,$1}')] | $*"
echo "${shell_param}"
}
function logger_error() {
echo "[$(date "+%Y%m%d %H:%M:%S")] ERROE [$(caller | awk '{printf "%s:%s",$2,$1}')] | $*"
}
logger_info "aaa"
logger_info "bbb"
logger_error "ccc"
function logger() {
echo "[$(date "+%Y%m%d %H:%M:%S")] [pid-$$] [${SCRIPT_NAME}:$(caller | awk '{printf "%s",$1}')] | $*"
}
命令重试实现
function retry() {
retry_count=0
max_retry_count=10
while [ ${retry_count} -lt ${max_retry_count} ]; do
# 重试命令 比如 cat xxxx
cat xxxx
if [ $? -eq 0 ]; then
break
fi
retry_count=$((retry_count + 1))
sleep 3
done
if [ ${retry_count} -eq ${max_retry_count} ]; then
echo "The maximum number of retries has been reached"
fi
}
retry
写文本到文件
cat
是一个常用的 Unix 命令,用于显示文件内容或将多个文件的内容合并输出。<< EOF
是一个 Here Document 的开始标记。EOF
是你可以选择的任何标识符,只要确保开始和结束使用相同的标识符。>> myfile.txt
是一个重定向操作,它将cat
命令的输出追加到myfile.txt
文件中。如果你使用单个>
而不是>>
,则会覆盖文件的内容,而不是追加。- 在
EOF
之前的所有内容(直到遇到另一个单独的EOF
)都会被cat
命令读取,并输出到myfile.txt
。 - 最后的
EOF
标志着 Here Document 的结束。
Here Document 是一个非常有用的技术,因为它允许你在脚本中直接输入多行文本,而无需使用多个 echo
命令或其他方法。
cat << EOF >> zzz.txt
111
222
333
EOF
#--------------------------
echo '123
456
789'>zzz.txt
export导出变量
在Shell脚本中,变量加不加export命令的区别主要在于变量的作用域和可见性。
作用域:
非导出变量 (不加export的变量): 这类变量只在定义它们的Shell进程及其子进程中可见。也就是说,如果你在一个Shell脚本中定义了一个变量但没有使用export,那么这个变量只在那个脚本及其调用的子脚本中有效。
导出变量 (加export的变量): 使用export命令定义的变量可以在当前Shell进程及其子进程、以及子进程的子进程等整个进程树中都是可见的。这意味着,如果你在一个脚本中定义并导出了一个变量,那么这个变量可以在那个脚本、由那个脚本调用的其他脚本,以及那些脚本的子脚本中都可以访问。
可见性:
非导出变量: 只在当前Shell进程和它的直接子进程中可见。
导出变量: 可以在整个进程树中访问,包括当前Shell进程、子进程、孙进程等。
随机数
echo $RANDOM
# 随机1,2,3
echo $(($RANDOM % 3 + 1))
# 随机1-10(含1和10)
echo $(($RANDOM % 10 + 1))
计算1-100的奇数和
sum=0
for i in {1..100..2}; do
let sum=$sum+$i
done
echo "1~100的奇数和为${sum}"
# let sum=$sum+$i
# sum=$(($sum+$i))
# sum=$[$sum+$i]
质数判断
read -p '请输入一个正整数:' num
[ $num -eq 1 ] && echo "${num}不是质数" && exit
[ $num -eq 2 ] && echo "${num}是质数" && exit
for i in $(seq 2 $(($num - 1))); do
[ $(($num % $i)) -eq 0 ] && echo "${num}不是质数" && exit
done
echo "${num}是质数" && exit
ping检查
ping -c5 '127.0.0.1' &>/dev/null
if [ $? -eq 0 ]; then
echo 'ping是互通的'
else
echo 'ping不通'
fi
ip格式化
ip addr | python3 -c '
import json
import re
import sys
import yaml
ipaddr = {}
network = ""
stdin = sys.stdin
lines = stdin.readlines()
for e in lines:
# print("----- " + e.strip())
if re.match("^[0-9]+: ", e):
network = re.findall("^[0-9]+: ([0-9a-z]+): <", e)[0]
ipaddr[network] = []
# print(network)
elif re.match(".*inet.*scope", e):
ip = re.findall("([0-9a-f:.]+)/[0-9]+", e)[0]
ipaddr[network].append(ip)
# print(ip)
print(json.dumps(ipaddr, indent=4))
for k, v in ipaddr.items():
print(k, ", ".join(v))
print("----------------------")
print(yaml.dump(ipaddr, default_flow_style=False))
'
表格化
import json
import re
import sys
import yaml
ipaddr = {}
network = ""
stdin = sys.stdin
lines = stdin.readlines()
for e in lines:
# print("----- " + e.strip())
if re.match("^[0-9]+: ", e):
network = re.findall("^[0-9]+: ([0-9a-z]+): <", e)[0]
ipaddr[network] = []
# print(network)
elif re.match(".*inet.*scope", e):
ip = re.findall("([0-9a-f:.]+)/[0-9]+", e)[0]
ipaddr[network].append(ip)
# print(ip)
print(json.dumps(ipaddr, indent=4))
for k, v in ipaddr.items():
print(k, ", ".join(v))
print("----------------------")
print(yaml.dump(ipaddr, default_flow_style=False))
def fill(s, l, p):
return s + p * (l - len(s))
def tabular(arr):
res = [""] * (len(arr) * 2 + 1)
max_column = [0] * len(arr[0])
# 每列最大值
for i, _ in enumerate(arr):
for j, _ in enumerate(arr[i]):
if len(arr[i][j]) > max_column[j]:
max_column[j] = len(arr[i][j])
# 每列前缀
for i, _ in enumerate(res):
if i % 2 == 0:
res[i] = "+"
else:
res[i] = "|"
# 一列一列写
for i in range(len(arr)):
for j in range(len(arr[i])):
index = 2 * i + 1
res[index] = res[index] + " " + fill(arr[i][j], max_column[j], " ") + " |"
res[index + 1] = res[index + 1] + "-" + fill("", max_column[j], "-") + "-+"
res[0] = res[-1]
return res
arr2 = []
for k, v in ipaddr.items():
arr1 = [k]
for e in v:
arr1.append(e)
arr2.append(arr1)
print(arr2)
table = tabular(arr2)
for e in table:
print(e)
进程内存VmRSS查看
# VmRSS表示进程当前占用的实际物理内存大小,也被称为Resident Set Size(常驻集大小)
PID=$(ps -ef | grep 'xxxxx' | grep -v grep | awk '{print $2}')
echo "----- PID=${PID}"
echo "$(ps -fp ${PID})"
for ((i = 0; i < 100; i++)); do
echo "$(date "+%F %T") ===> $(grep VmRSS /proc/${PID}/status)"
sleep 2s
done
# 内存占用查看
free -k | grep Mem | awk '{printf "%s/%s",$3,$2}'
进程cpu和mem统计
#!/bin/bash
SCRIPT_DIR=$( cd $(dirname $0); pwd )
SCRIPT_NAME=$(basename $0)
SCRIPT_ABS_NAME="${SCRIPT_DIR}/${SCRIPT_NAME}"
SCRIPT_LOG="${SCRIPT_DIR}/log_$(date '+%Y%m%d_%H%M%S').log"
function logger() {
echo "$*" >> ${SCRIPT_LOG}
}
# echo "script log path : ${SCRIPT_LOG}"
pid=5771
logger "$(ps -ef | grep -v grep | grep ${pid})"
logger "date,cpu,mem"
for ((i = 0; i < 30; i++)); do
line=$(ps -p ${pid} -o %cpu,%mem | grep -v CPU)
if [ -z "${line}" ]; then
logger 'Process does not exist'
break
fi
cpu=$(echo ${line} | grep -oE '[0-9\.]+' | sed -n '1p')
mem=$(echo ${line} | grep -oE '[0-9\.]+' | sed -n '2p')
logger "$(date '+%F %T'),${cpu},${mem}"
sleep 1
done
脚本demo(周期执行示例)
#!/usr/bin/bash
# 后台执行: bash xxxxx.sh &
# 执行次数
COUNT=360
# 执行间隔(秒)
PERIOD=10
SCRIPT_DIR=$( cd $(dirname $0); pwd )
SCRIPT_NAME=$(basename $0)
SCRIPT_ABS_NAME="${SCRIPT_DIR}/${SCRIPT_NAME}"
SCRIPT_LOG="${SCRIPT_DIR}/log_$(date '+%Y%m%d_%H%M%S').log"
# 日志打印
function logger() {
echo "[$(date "+%Y%m%d %H:%M:%S")] [pid-$$] [${SCRIPT_ABS_NAME}:$(caller | awk '{printf "%s",$1}')] | $*" >>${SCRIPT_LOG}
}
# 周期执行的函数
function run() {
logger $(date "+%F %T")
}
function main() {
logger "script start"
# echo "script log path : ${SCRIPT_LOG}"
logger "script log path : ${SCRIPT_LOG}"
time1=$(date '+%s%3N')
##### --------------------
for ((i = 0; i < ${COUNT}; i++)); do
# logger $i
run
sleep ${PERIOD}
done
##### --------------------
time2=$(date '+%s%3N')
logger "script end: cost $(echo $((time2 - time1))) ms"
}
main
作者博客首页:https://blog.csdn.net/qq_43111676?type=blog
持续更新中…