简单的log和err_log

shell函数如下:

1
2
3
4
5
6
7
8
9
10
11
function log()
{
timer=`date "+%Y-%m-%d %H:%M:%S"`
echo "$timer -- $1"
}
function err_log()
{
log "[Error]: $1"
}

用法:

1
2
log "hello world."
err_log "hello error."

输出:

1
2
2021-09-20 11:47:49 -- hello world.
2021-09-20 11:47:49 -- [Error]: hello error.


err_log要彩色输出

shell函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
function log()
{
timer=`date "+%Y-%m-%d %H:%M:%S"`
echo -e "$timer -- $1"
}
function err_log()
{
RED='\033[0;31m'
NC='\033[0m' # No Color
log "${RED}[Error]: $1 ${NC}"
}

用法:

1
2
log "hello world."
err_log "hello color error."

输出:
shell_log_with_color.jpg

附带 ANSI escape codes

1
2
3
4
5
6
7
8
Black 0;30 Dark Gray 1;30
Red 0;31 Light Red 1;31
Green 0;32 Light Green 1;32
Brown/Orange 0;33 Yellow 1;33
Blue 0;34 Light Blue 1;34
Purple 0;35 Light Purple 1;35
Cyan 0;36 Light Cyan 1;36
Light Gray 0;37 White 1;37

参考自: How to change the output color of echo in Linux


if else用法

用法:

1
2
3
4
5
6
7
8
9
if [ expression1 ]; then
statement1
statement2
elif [ expression2 ]; then
statement3
statement4
else
statement5
fi


switch case

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac

其中;;相当于是break,*)相当于是default分支

一个解析参数的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while [ $# -gt 0 ]
do
case "$1" in
-a)
options="$options $1" # 没有参数只有-a
;;
-f)
options="$options $1" # -f file_name形式
argfile="$1"
shift
;;
*)
files="$files $1"
;;
esac
shift
done

参考: 35.22. Handling Arguments with while and shift


创建临时文件

使用mktemp来创建临时文件,再使用trap在脚本运行结束后,删除该文件

mktemp用法: tmp_file=$(mktemp -p $TARGET_PATH tmp_file.XXXXXXXX) || exit 1
其中XXXXXXXX会自动变为一个随机字串。mktemp命令返回临时文件的路径。
例如:

1
2
3
4
$ tmp_file=$(mktemp -p /tmp tmp_file.XXXXXXXX) || exit 1
$ echo $tmp_file
/tmp/tmp_file.psCxkVau
$

再使用trap在shell脚本退出时,删除这个临时文件

1
trap 'rm -rf "$tmp_file"' INT TERM EXIT

合起来的使用方法:

1
2
tmp_file=$(mktemp -p /tmp tmp_file.XXXXXXXX) || exit 1
trap 'rm -rf "$tmp_file"' INT TERM EXIT

信号量详解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
$

常用的几个信号量:

信号名称 信号数 描述
SIGHUP 1 本信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一session内的各个作业,这时它们与控制终端不再关联。登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
SIGINT 2 程序终止(interrupt)信号,在用户键入 Ctrl+C 时发出。
SIGQUIT 3 和SIGINT类似,但由QUIT字符(通常是Ctrl /)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。
SIGFPE 8 在发生致命的算术运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术错误。
SIGKILL 9 用来立即结束程序的运行。本信号不能被阻塞,处理和忽略。
SIGALRM 14 时钟定时信号,计算的是实际的时间或时钟时间。alarm 函数使用该信号。
SIGTERM 15 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出;kill 命令缺省产生这个信号。

参考:


创建文件夹锁

文件夹锁,防止脚本重复运行

1
2
3
4
5
6
7
lockdir="${TARGET_PATH}/${LOCK_DIR}.lock"
if mkdir "$lockdir"; then
trap 'rm -rf "$lockdir"' INT TERM EXIT
else
echo "Exit by mutex control"
exit 1
fi


flock文件锁&后台运行程序

普通的flock文件锁机制在crontab中的运用

1
* * * * * /usr/bin/flock -xn /tmp/crontab_task.lock -c "/bin/bash -l -c '/usr/local/bin/myshell.sh >> /tmp/xxxx.log 2>&1'"

如果/usr/local/bin/myshell.sh中执行了后台运行的程序,比如nohup backgrounp.sh &,此时只要这个后台进程不结束,整个外层的/tmp/crontab_task.lock都不会被释放。

此时如果还是需要重复执行/usr/local/bin/myshell.sh的脚本,那么就不合适在外层使用flock来锁文件,可以考虑在脚本内部使用上面的文件夹锁的机制来实现。


ps检查某个进程是否存在

以ffmpeg为例。命令如下:

1
2
3
4
5
6
exist=`ps aux | grep '/usr/local/bin/[f]fmpeg'`
if [ "$exist" == "" ]; then
echo "没有ffmpeg命令在运行"
else
echo "有ffmpeg命令在运行"
fi

其中在ps命令中过滤grep本身的方法有两种


ps查找后杀进程

命令:

1
ps -ef | grep your_process_name | grep -v grep | awk '{print $2}' | xargs kill


shell切割字符串到array中

命令:

1
IFS=', ' read -r -a array <<< "$string"

IFS需要和read在一行中,这样保证只作用于当前的read命令。

获取单独的元素

1
echo "${array[0]}"

循环遍历数组中的元素:

1
2
3
4
for element in "${array[@]}"
do
echo "$element"
done

同时获取索引和值

1
2
3
4
for index in "${!array[@]}"
do
echo "$index ${array[index]}"
done

获取数组中元素的数量:

1
echo "${#array[@]}"

参考: How to split a string into an array in Bash?

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ string="字段1,字段2 字段3,字段4"
$ IFS=', ' read -r -a array <<< "$string"
$ echo "${array[0]}"
字段1
$ echo "${array[1]}"
字段2
$ echo "${array[2]}"
字段3
$ echo "${array[3]}"
字段4
$ echo "${array[4]}"
$


如何将多分隔符的字串切割到array中

假设多分隔符字串为”|||”, 命令如下:

1
mapfile -t array < <( echo "${line//|||/$'\n'}" )

先将|||更新为\n, 然后再map成array

参考: How to split string by string (multi-character) separator into an array?


逐行读取文件内容

命令:

1
2
3
while IFS= read -r line; do
echo "line is ${line}"
done < $file_path

参考: Read a file line by line assigning the value to a variable [duplicate]


将命令输出切割到数组中

Bash版本>=4:

1
mapfile -t my_array < <( my_command )

Bash版本小于4:

1
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )

命令详解: Reading output of a command into an array in Bash

留言