数组函数
# 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<&-
```
这样,就实现了进程的并发控制