Linux Shell脚本笔记

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

持续更新中…

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值