Shell Programming Techs I
记录阅读 MAGMA 源码时,遇到的一些高级shell编程技巧
Set
设置环境变量
set -a # 激活 Shell 的 allexport 模式。当 allexport 模式启用时,所有定义的变量(包括后续加载的变量)都会自动导出为环境变量。
source "$1" # 加载配置文件,将其中定义的变量和命令导入当前 Shell 环境。
set +a # 关闭 allexport 模式。
设置shell变量
MAGMA=${MAGMA:-"$(cd "$(dirname "${BASH_SOURCE[0]}")/../../" >/dev/null 2>&1 && pwd)"}
${MAGMA:-value}
: 表示如果环境变量 MAGMA
已经被定义,则保留其原值。如果 MAGMA
未被定义,则设置为后面指定的值。
$(...)
: 用于命令替换,表示运行括号中的命令并将其输出结果赋值给变量。
dirname "${BASH_SOURCE[0]}"
: 返回当前脚本文件所在的目录。${BASH_SOURCE[0]}
是当前脚本文件的路径。
>/dev/null 2>&1
: 将标准输出重定向到 /dev/null
,抑制命令的标准输出和错误输出。2>
表示重定向标准错误。&1
表示将标准错误的输出流合并到标准输出流中。避免命令的输出污染终端显示。仅判断命令是否执行成功(通过 $?
查看退出状态),不关心输出内容。
相应的有,&> /dev/null
抑制输出,避免干扰。
Operation
获取CPU列表
WORKERS_ALL=($(lscpu -b -p | sed '/^#/d' | sort -u -t, -k ${WORKER_MODE}g | cut -d, -f1))
lscpu -b -p
: 列出 CPU 的简洁信息,以逗号分隔。
sed '/^#/d'
: 删除以 #
开头的注释行。
sort -u -t, -k ${WORKER_MODE}g
: 按第 WORKER_MODE
列对数据进行排序并去重。
cut -d, -f1
: 提取第一个字段(CPU 核心编号)。
(...)
: 将命令的输出分割为多个元素(基于空白符)。
确保绝对路径
WORKDIR="$(realpath "$WORKDIR")"
双引号用于避免路径中可能包含的空格或特殊字符导致问题。
$()
用于捕获子命令的输出。
安全删除文件
shopt -s nullglob # shopt 是 Bash 的一个内建命令,用于启用或禁用 shell 选项。nullglob 是一个特定的 shell 选项,其作用是控制文件名通配符(如 *)在没有匹配文件时的行为。启用时,文件名通配符在没有匹配文件时返回空字符串。启用 nullglob 确保目录 $LOCKDIR 中没有匹配文件时,通配符 * 不会被当作字符串 *,而是返回空。
rm -f "$LOCKDIR"/* # 如果 $LOCKDIR 是空目录,启用了 nullglob 的情况下,* 返回空,rm 不会执行删除操作,也不会报错。
shopt -u nullglob # 关闭 nullglob 后恢复 Bash 的默认行为,即文件名通配符在没有匹配文件时不返回空,而是原样保留。
排序操作
cids=($(sort -n < <(basename -a "${campaigns[@]}")))
sort -n
: 按数字排序这些基名。
< <(...)
: 将子命令的输出重定向为标准输入流。
导出函数
export -f get_next_cid # 将函数导出为当前 shell 的子进程可用(例如在 bash 脚本或其他子 shell 中调用)。
清理临时文件
trap 'rm -f "$LOCKDIR/$mux"' EXIT
trap
: 设置退出时的清理动作。捕获 EXIT
信号,当脚本或函数退出时(无论正常还是异常退出),都会执行指定命令。
移除参数
shift # 使用 shift 移除第一个参数,将后续参数(命令及其参数)从 $2..N 调整为 $1..N。
创建子shell
(
flock -xF 200 &> /dev/null
"${@}"
) 200>"$LOCKDIR/$mux"
使用 ()
启动一个子 shell,确保锁的范围仅限于当前子 shell。
flock
是一个用于管理文件锁的工具。-x
: 获取排他锁(独占锁),确保同一时间只有一个进程持有该锁。-F
: 强制刷新文件,如果文件描述符无法获取锁时立即返回(不会阻塞)。
200>"$LOCKDIR/$mux"
将文件描述符 200
定向到 $LOCKDIR/$mux
,该文件用于作为锁标识。
"${@}"
运行给定命令,@
是传递给 mutex
函数的命令及其参数。
失败处理
|| if [ $? -eq $errno_lock ]; then
continue
fi
$?
表示前一个命令的退出状态码。
文件操作:防止覆盖已有文件
set -o noclobber # 确保文件创建是原子的(即如果文件已存在则创建失败)。
cut
: 一个用于从文本中按字段或字符位置提取数据的工具
cut -d',' -f2- <<< $WORKERSET
# example
cut -d',' -f2- <<< "a,b,c"
echo "a,b,c" | cut -d',' -f2- # equivalent command
# output: a b c
-d','
: 指定逗号 ,
为字段分隔符。
-f2-
: 提取从第 2 个字段开始的所有字段。
后台任务处理
for job in `jobs -p`; do
if ! wait $job; then
continue
fi
done
jobs -p
列出当前 Shell 中的所有后台任务的进程 ID(PID)。
wait $job
等待后台任务完成。如果任务成功完成,wait
返回 0;否则返回非零状态码。
遍历目录下所有文件
find "$LOCKDIR" -type f | while read lock; do
if inotifywait -qq -e delete_self "$lock" &> /dev/null; then
continue
fi
done
find "$LOCKDIR" -type f
找出 $LOCKDIR
所有文件
while read lock
逐个遍历文件设为 lock
变量获取
set -- "DEFAULT" "${@:2}"
set -- "${@:2}" # 删除第一个参数(包括 DEFAULT),使用剩余参数。
"DEFAULT"
替换第一个参数,生成新的参数列表。"${@:2}"
代表原参数的第 2 个及之后的部分。
Array
数组切片
WORKER_POOL="${WORKERS_ALL[@]:0:WORKERS}"
[@]
表示操作数组的所有元素。如果只用 ${#WORKERS_ALL}
,则表示字符串长度,而不是数组元素数量。${#array[@]}
语法只能用于明确声明为数组的变量。
[@]:start:length
是 Bash 中的数组切片语法,用于提取数组的一部分。start
表示起始索引(从 0
开始),length
表示要提取的元素个数。
"${...}"
将数组切片的结果(即前 WORKERS
个元素)合并成一个字符串,元素之间用空格分隔。
使用 read
将字符串解析为数组
IFS=',' # 设置字段分隔符
read -a workers <<< "$AFFINITY"
read -a
: 读取输入并将其存储为一个数组。
<<<
是 Here String 的语法,用于将一个字符串作为命令的标准输入。
数组定义与解引用
name="${name}[@]" # 将变量名作为数组处理
value="${!name}" # 间接引用变量 name 的值
Functions
将传入的参数用指定分隔符连接成一个字符串
function join_by { local IFS="$1"; shift; echo "$*"; } # 定义分隔符为"$1",移除第一个参数,打印每个参数
pattern=$(join_by _ "${@}") # ${@} 包含传入的所有参数