数组函数

# io & net ``` [root@master39 cklog]# cat /proc/40599/io rchar: 698368493 wchar: 2247160034 syscr: 84640265 syscw: 84413964 read_bytes: 6230016 write_bytes: 245821440 cancelled_write_bytes: 0 [root@master39 cklog]# cat /proc/40599/net/dev Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 eth0: 3059187128 42551433 0 0 0 0 0 0 4885111559 43330864 0 0 0 0 0 0 磁盘IO统计指标解释 rchar:表示自系统启动以来,从文件系统等读取的字节数(不论实际是否发生硬盘IO操作)。 wchar:表示自系统启动以来,写入到文件系统等的字节数(不论实际是否发生硬盘IO操作)。 syscr:表示自系统启动以来,系统调用read()的次数。 syscw:表示自系统启动以来,系统调用write()的次数。 read_bytes:表示实际从磁盘读取的数据量,这个数字会受到磁盘缓存的影响较小。 write_bytes:表示实际写入磁盘的数据量。 cancelled_write_bytes:由于某些原因未完成的写操作字节数,比如写操作被取消或者合并了。 网络统计指标解释 这部分数据描述了每个网络接口的接收和发送的详细统计信息: Inter-| Receive | Transmit:表格的顶部标题,分别代表接口、接收和发送。 face:指网络接口的名字。 bytes:接收或发送的总字节数。 packets:接收或发送的数据包总数。 errs:在接收或发送数据包时发生的错误总数。 drop:被丢弃的数据包总数,通常因为网络拥塞或数据包损坏。 fifo:FIFO缓冲区错误的次数。 frame:物理或数据链路层的帧错误。 compressed:压缩的数据包数量。 multicast:接收的多播数据包数量。 colls:发送时发生的碰撞次数。 carrier:载波错误的次数。 示例输出解释 lo:本地环回接口,通常用于本机内部通信,数据均为0表示没有数据被发送或接收。 eth0: Receive: bytes: 3040163319:接收了3040,163,319字节。 packets: 42285490:接收了42,285,490个数据包。 Transmit: bytes: 4854597976:发送了4854,597,976字节。 packets: 43059433:发送了43,059,433个数据包。 ``` # awk 如何修改这条命令可以计算这四个值的和 ```bash ./topic.sh group |awk '{print $6}' LAG 7501 2299 2309 7505 ./topic.sh group | awk '{sum+=$6} END {print sum}' # 其中 sum 变量用于存储计算结果,{sum+=$6} 表示将每行的第六个字段加到 sum 中。END 语句块在处理完所有行后执行,其中 {print sum} 用于输出最终的计算结果。 ``` # captor启停 ```bash [root@FullLink-Captor-Node-01 lixian_captor]# cat start.sh #!/bin/bash export LD_LIBRARY_PATH=$(cd $(dirname $0);pwd) nohup ./lixian > ./logs/captor.log 2>&1 & [root@FullLink-Captor-Node-01 lixian_captor]# cat stop.sh #!/bin/bash ps -ef |grep ./lixian |grep -v grep|awk '{print $2}'|xargs kill -9 mv logs/captor.log logs/captor.log.`date +%F` ``` # 颜色 ``` #!/bin/bash # ANSI color codes COLOR_RESET='\033[0m' COLOR_GREEN='\033[0;32m' COLOR_RED='\033[0;31m' COLOR_YELLOW='\033[0;33m' # Function to print logs # Usage: println <level> <message> [<date_format>] [<line_number>] function println(){ local level="$1" local message="$2" local date_format="${3:-+%Y-%m-%d_%H:%M:%S}" # Default date format if not provided local line_number="${4:-$BASH_LINENO}" # Use $BASH_LINENO to get the line number of the caller case $level in info) echo -e "${COLOR_GREEN} [`date "$date_format"`] INFO [Line $line_number]: $message ${COLOR_RESET}" ;; err | error) echo -e "${COLOR_RED} [`date "$date_format"`] ERROR [Line $line_number]: $message ${COLOR_RESET}" ;; warn) echo -e "${COLOR_YELLOW} [`date "$date_format"`] WARNING [Line $line_number]: $message ${COLOR_RESET}" ;; *) echo "Invalid log level. Usage: println <level> <message> [<date_format>] [<line_number>]" ;; esac } # Example usage println info "This is an info message" println error "This is an error message" println warn "This is a warning message" ``` # 回收站 ``` #!/usr/bin/env bash # 关键系统中 # 避免误删先move到/tmp/回收站 目录,后面确定删除无影响后再清空回收站 if [ -z $1 ];then echo "Usage: $0 [dir | file]..." && exit 1 fi dir="/tmp/recovery/`date +%F_%H`" if [ ! -d $dir ];then mkdir -p $dir fi mv $* $dir ``` # 重试机制 ``` #!/bin/bash # 定义函数 function retry { local retries=$1 # shift命令用于向左移动位置参数的值。当执行shift命令时,位置参数$2的值将赋给$1,位置参数$3的值将赋给$2,以此类推。 # 简而言之,shift命令将删除位置参数$1并将所有其他位置参数向左移动一个位置。 shift for ((i=0; i<retries; i++)); do if "$@"; then echo "命令成功执行" return 0 else echo "命令执行失败,正在重试" sleep 1 # 休眠一秒再重试 fi done echo "命令执行失败,已达到最大重试次数" return 1 } # 使用函数 retry 5 echo "Hello, World!" # 重试5次,执行命令 "echo Hello, World!" ``` # shell 计算程序运行时间 ``` starttime=$(date -d "`date +"%Y-%m-%d %H:%M:%S"`" +%s) sleep 3 endtime=$(date -d "`date +"%Y-%m-%d %H:%M:%S"`" +%s) runtime=$(($endtime-$starttime)) echo "程序运行时间:$runtime" #echo "耗时 $(($(($endtime-$starttime))/60)) 分钟!" ``` # Linux Shell中捕获CTRL+C ``` #!/bin/bash trap 'onCtrlC' INT function onCtrlC () { echo 'Ctrl+C is captured' } while true; do echo 'I am working!' sleep 1 done ``` 执行上述脚本,按下Ctrl+C按键将会触发onCtrlC函数 使用Python作为文件服务器 ```shell #!/usr/bin/env bash port=$1 trap 'onCtrlC' INT function onCtrlC () { echo 'Ctrl+C is captured' exit 0 } if [[ $port == "" ]];then echo "exec : $0 port" exit 1 else #如果没有onCtrlC函数,在这一步接收到ctrl+c后不会关闭程序而是会跳到python3 -m http.server $port python -m SimpleHTTPServer $port if [[ $? != 0 ]];then python3 -m http.server $port if [[ $? != 0 ]];then echo "please install python2 or python3 !" exit 1 fi fi fi ``` ``` #!/bin/bash #临时存放命令结果文件,程序结束时释放 memfile=/opt/cache_file && trap "rm -rf $memfile" EXIT QUIT INT DEBUG=true #为true时打印命令结果方便调试,可取消 #程序主函数入口 function main(){ try_except "tshark -v" "yum -y install wireshark" } function debug(){ if [[ $DEBUG == "true" ]];then echo "[DEBUG] *************exec [ $1 ] *************" while read -r line do echo " $line" done < $memfile echo "**********************************************" fi } function println() { case $1 in green) echo -e "\033[32m [INFO] `date +%H:%M:%S` $2\e[0m" ;; red) echo -e "\033[31m [ERROR] `date +%H:%M:%S` $2\e[0m" ;; orange) echo -e "\033[33m [WARN] `date +%H:%M:%S` $2\e[0m" ;; *) echo "Example: Usage [red] [orange] [green]" # esac } function progress() { mark='' for ((ratio=0;${ratio}<=100;ratio+=2)) do printf "progress:[%-50s]%d%%\r" "${mark}" "${ratio}" mark="#${mark}" sleep $(($1/50)) done echo } progress 120 #错误处理机制 Usage:try_except [command1] [command2] => command1执行失败时尝试执行command2,若command2失败则退出 function try_except(){ $1 > $memfile 2>&1 if [[ $? == 0 ]];then println green "exec [ $1 ] status: succeed!" debug "$1" else println red "exec: [ $1 ] failed, err: [ `cat $memfile` ]! " if [[ $2 != "" ]];then println orange "try exec [ $2 ]:" $2 > $memfile 2>&1 if [[ $? == 0 ]];then println green "exec [ $2 ] status: succeed!" debug "$2" else println red "exec: [ $2 ] failed, err: [ `cat $memfile` ], exit!" exit 1 fi fi fi } main ``` ## shell读取文档中的命令并逐行执行 ``` 1、如果我使用read line逐行读取打印 while read line || [ -n "$line" ] ; do echo $line ; done < filename 2、如果我使用read line逐行读取并执行(不使用eval的话,一些特殊的符号会报错) while read -r line || [ -n "$line" ] ; do eval $line ; done < filename 注: 1)while read line 读取不到最后一行的话,加上 || [ -n "$line"] 2)读取命令打印的时候,可能一条占了多行,需加参数-r,读取完整的一条 ``` # 数组 ## Map定义: 在使用map时,需要先声明,否则结果可能与预期不同,array可以不声明 ### 方式1: ``` declare -A myMap myMap["my03"]="03" ``` ### 方式2: ``` declare -A myMap=(["my01"]="01" ["my02"]="02") myMap["my03"]="03" myMap["my04"]="04" ``` ## Map初始化: 与array类似,可以使用括号直接初始化,也可以通过添加的方式来初始化数据,与array不同的是,括号直接初始化时使用的为一个键值对,添加元素时,下标可以不是整数 ``` myMap["my03"]="03" myMap["my04"]="04" ``` 输出Map所有的key、value、长度: ``` # 1)输出所有的key #若未使用declare声明map,则此处将输出0,与预期输出不符,此处输出语句格式比arry多了一个! echo ${!myMap[@]} #2)输出所有value #与array输出格式相同 echo ${myMap[@]} #3)输出map长度 #与array输出格式相同 echo ${#myMap[@]} ``` Map遍历: ``` #1)遍历,根据key找到对应的value for key in ${!myMap[*]};do echo $key echo ${myMap[$key]} done #2)遍历所有的key for key in ${!myMap[@]};do echo $key echo ${myMap[$key]} done #3)遍历所有的value for val in ${myMap[@]};do echo $val done ``` # 函数 ``` #!/bin/bash function edit_npds(){ ImagesList=`docker images|grep $1 |awk '{print $1":"$2}'` echo "==============获取到以下 [ $1 ] 服务的镜像!==============" num=0 declare -A ImageMap for imagename in $ImagesList; do num=$(($num+1)) echo "["$num"] => ["$imagename"]" ImageMap[$num]=$imagename done echo "************************************************************************" read -p "请选择要替换的镜像编号:" id if [[ $id > ${#ImageMap[@]} ]];then echo "选项超出范围!" && exit 1 fi echo "kubectl set image -n npds sts/$1 $1=${ImageMap[$id]}" kubectl set image -n npds sts/$1 $1=${ImageMap[$id]} if [[ $? != 0 ]];then echo "[ $1 ]镜像替换失败,请手动检查!" && exit 1 else echo "[ $1 ]镜像替换成功!" fi } echo "################################################# 0. all npds service 1. manager 2. client 3. pair 4. exit #################################################" read -p "请选择要更换镜像的服务:" system case $system in 0) edit_npds npds-manager edit_npds npds-client edit_npds npds-pair ;; 1) edit_npds npds-manager ;; 2) edit_npds npds-client ;; 3) edit_npds npds-pair ;; 4) echo "I'm fine, thank you !" exit 0 ;; *) echo "这么几个选项都能输错???" ;; esac ``` # 多进程 shell中实现多进程实际上就是将多个任务放到后台中执行而已,但是现在需要控制多进程并发的数量该如何实现呢?别急,我们一步一步来实现这个目标,首先从最原始的串行执行开始: ``` #!/bin/bash start=`date +%s` for i in $(seq 1 5); do echo test sleep 2 done end=`date +%s` time=$(($end - $start)) echo "time: $time" ``` 执行结果: ``` # sh test1.sh test test test test test time: 10 ``` 这是最原始的一种处理方式,串行执行,无法有效利用计算机的资源,并且效率低下。如果可以一次执行多个任务的,把它放到后台即可,我们做如下改进: ``` #!/bin/bash start=`date +%s` for i in $(seq 1 5); do { echo test sleep 2 }& done wait end=`date +%s` time=$(($end - $start)) echo "time: $time" ``` 首先看下执行结果: ``` # sh test2.sh test test test test test time: 2 ``` 说下改动,首先我把for循环中的代码用{}包为一个块,然后增加&符号使其后台运行,之后增加wait指令,该指令会等待所有后台进程结束,如果不加wait,程序直接往下走,最终打出的time将会是0。现在程序已经由之前的10秒缩短为2秒,似乎效果不错,不过试想这样一个场景,有1000个这样的任务,如果还是以这种方式执行,机器负载是扛不住的,我们必须想一种办法来控制进程的并发数,那就是管道和文件描述符。 首先介绍下管道(pipe): - 无名管道 它经常出现在我们的命令中,例如cat /etc/passwd | awk -F: ‘{print $1}’,其中"|"就是管道,它将前一个命令的结果输出到后一个进程中,作为两个进程的数据通道,不过他是无名的。 - 有名管道 使用mkfifo命令创建的管道即为有名管道,例如,mkfifo pipefile, pipefile即为有名管道。 管道有一个显著的特点,如果管道里没有数据,那么去取管道数据时,程序会阻塞住,直到管道内进入数据,然后读取才会终止这个操作,反之,管道在执行写入操作时,如果没有读取操作,同样会阻塞. 下面开始介绍如何使用管道文件和文件描述符来控制进程并发: ``` mkfifo tm1 exec 5<>tm1 rm -f tm1 ``` 首先创建一个管道文件tm1,然后使用exec命令将该文件的输入输出绑定到5号文件描述符,而后删除该管道文件,这里删除的只是该文件的Inode,实际文件已由内核open函数打开,这里删除并不会影响程序执行,当程序执行完后,文件描述符会被系统自动回收。 ``` for ((i=1;i<=10;i++)); do echo >&5 done ``` 通过一个for循环向该文件描述符写入10个空行,这个10其实就是我们定义的后台进程数量,这里需要注意的是,管道文件的读取是以行为单位的。 ``` for ((j=1;j<=100;j++)); do read -u5 { echo test$j sleep 2 echo >&5 }& done wait ``` 这里假定我后台有100个任务,而我们在后台保证只有10个进程在同时运行 read -u5 的作用是,读取5号文件描述符中的一行,就是读取一个空行 在减少文件描述符的一个空行之后,在后台执行一次任务,而任务在执行完成以后,会向文件描述符中再写入一个空行,这是为什么呢,因为如果我们写入空行的话,当后台放入了10个任务之后,由于没有可读取的空行,read -u5就会被阻塞住! ``` exec 5>&- exec 5<&- ``` 我们生成做绑定时 可以用 exec 5<>tm1 来实现,但关闭时必须分开来写> 读的绑定,< 标识写的绑定 <> 则标识 对文件描述符5的所有操作等同于对管道文件tm1的操作。 完整代码如下: ``` #!/bin/bash mkfifo tm1 exec 5<>tm1 rm -f tm1 for ((i=1;i<=10;i++)); do echo >&5 done for ((j=1;j<=100;j++)); do read -u5 { echo test$j sleep 2 echo >&5 }& done wait exec 5>&- exec 5<&- ``` 这样,就实现了进程的并发控制