Lyndra's Blog

Shell 脚本模板

2025-02-28
编程开发系统维护 LinuxArchlinuxShell
9分钟
1758字
温馨提示:本文最后更新于 2025-03-11 ,部分信息可能因时间推移而不再适用,欢迎反馈。

Shell 脚本模板

在日常的开发工作中,我们经常需要编写一些脚本,来完成一些自动化工作,同时还能保存一些环境使用的配置。可以在一段时间后,仍然能保证快速地回忆和使用。

但每一个脚本有可能需要传递命令行参数,如果为每一个脚本都撰写一套命令行解析,会比较麻烦,浪费时间。但不进行命令行参数解析,又会导致后续使用不便,一段时间后就不记得怎么使用了。

模板

先给出模板代码。这个脚本模板采用模块化设计,每个模块都有独立的函数,方便管理和调用。每一个功能由单独的选项来调用,方便记忆和使用。

可以在一个脚本中整合多个功能,利用统一的命令行参数处理框架,不需要为单个功能编写脚本单独处理参数。

Shell_script_template.sh
1
#!/usr/bin/env bash
2
#!/usr/bin/env bash
3
#==================================================================
4
# ██╗ ██╗ ██╗███╗ ██╗██████╗ ██████╗ █████╗
5
# ██║ ╚██╗ ██╔╝████╗ ██║██╔══██╗██╔══██╗██╔══██╗
6
# ██║ ╚████╔╝ ██╔██╗ ██║██║ ██║██████╔╝███████║
7
# ██║ ╚██╔╝ ██║╚██╗██║██║ ██║██╔══██╗██╔══██║
8
# ███████╗██║ ██║ ╚████║██████╔╝██║ ██║██║ ██║
9
# ╚══════╝╚═╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
10
#==================================================================
11
#
12
# 文件名称: 脚本模板.sh
13
# 文件描述: 一个通用的 Shell 脚本模板,提供命令行参数处理、日志输出等基础功能
14
#
15
# 作者: Lyndra <lyndra@hysling.top>
197 collapsed lines
16
# 版本: 1.0.0
17
# 创建日期: 2025-02-28
18
# 最后修改: 2025-02-28
19
#
20
# 使用许可: GPLv2 or other licenses
21
# Copyright (c) 2025 Lyndra
22
#
23
#==================================================================
24
25
set -eo pipefail
26
27
# 颜色配置
28
RED='\033[0;31m'
29
GREEN='\033[0;32m'
30
YELLOW='\033[1;33m'
31
NC='\033[0m'
32
33
# 命令配置,格式为:[命令名]="描述::处理函数::选项声明1::选项声明2::..."
34
declare -A COMMAND_MAP=(
35
["install"]="安装软件包::handle_install::-p,--package:指定包名(必须)"
36
["remove"]="移除软件包::handle_remove::-p,--package:指定包名(必须)"
37
["backup"]="执行备份::handle_backup::-s,--src:源目录(必须)::-d,--dest:目标目录(默认:/backups)"
38
)
39
40
# 错误处理函数
41
panic() {
42
echo -e "${RED}[错误]${NC} $1" >&2
43
exit 1
44
}
45
46
# 日志输出函数
47
log_info() {
48
echo -e "${GREEN}[信息]${NC} $1" >&2
49
}
50
51
log_warning() {
52
echo -e "${YELLOW}[警告]${NC} $1" >&2
53
}
54
55
log_success() {
56
echo -e "${GREEN}[成功]${NC} $1" >&2
57
}
58
59
##############################################
60
# 通用的帮助信息生成函数 #
61
##############################################
62
63
show_command_help() {
64
local cmd="$1"
65
local info="${COMMAND_MAP[$cmd]}"
66
67
# 1) 把所有 "::" 替换成一个不会在脚本中出现的分隔符,比如 '|'
68
info="${info//::/|}"
69
70
# 2) 这时才能安全地用 IFS='|' 来做分割
71
local desc handler options_str
72
IFS="|" read -r desc handler options_str <<< "$info"
73
74
echo -e "${YELLOW}命令: ${GREEN}$cmd${NC}"
75
echo -e "${YELLOW}描述: ${NC}$desc"
76
echo -e "${YELLOW}选项:${NC}"
77
78
# 3) 选项声明里可能有多段「::」分隔,继续替换并拆分
79
options_str="${options_str//::/|}"
80
IFS="|" read -ra opt_lines <<< "$options_str"
81
82
for opt_line in "${opt_lines[@]}"; do
83
[[ -z "$opt_line" ]] && continue
84
85
# opt_line 类似 "-p,--package:指定包名(必须)"
86
# 用 ":" 分割出(短+长选项)和描述
87
IFS=":" read -r flags desc <<< "$opt_line"
88
printf " ${GREEN}%-20s${NC} %s\n" "$flags" "$desc"
89
done
90
}
91
92
# 主帮助信息
93
show_help() {
94
echo -e "${YELLOW}使用方法:${NC}"
95
echo " $0 [命令] [选项]"
96
echo
97
echo -e "${YELLOW}可用命令:${NC}"
98
for cmd in "${!COMMAND_MAP[@]}"; do
99
local desc=${COMMAND_MAP[$cmd]%%::*}
100
printf " ${GREEN}%-15s${NC} %s\n" "$cmd" "$desc"
101
done
102
echo -e "\n使用 ${YELLOW}$0 命令 --help${NC} 查看具体命令帮助"
103
}
104
105
##############################################
106
# 命令处理函数 #
107
##############################################
108
109
handle_install() {
110
local package=""
111
while [[ $# -gt 0 ]]; do
112
case $1 in
113
-p|--package)
114
package="$2"
115
shift 2
116
;;
117
-h|--help)
118
show_command_help "install"
119
exit 0
120
;;
121
*)
122
panic "未知选项: $1"
123
;;
124
esac
125
done
126
[[ -z "$package" ]] && panic "必须指定包名"
127
log_success "正在安装 ${YELLOW}$package${NC}"
128
}
129
130
handle_remove() {
131
local package=""
132
while [[ $# -gt 0 ]]; do
133
case $1 in
134
-p|--package)
135
package="$2"
136
shift 2
137
;;
138
-h|--help)
139
show_command_help "remove"
140
exit 0
141
;;
142
*)
143
panic "未知选项: $1"
144
;;
145
esac
146
done
147
[[ -z "$package" ]] && panic "必须指定包名"
148
log_success "正在移除 ${YELLOW}$package${NC}"
149
}
150
151
handle_backup() {
152
local src="" dest="/backups"
153
while [[ $# -gt 0 ]]; do
154
case $1 in
155
-s|--src)
156
src="$2"
157
shift 2
158
;;
159
-d|--dest)
160
dest="$2"
161
shift 2
162
;;
163
-h|--help)
164
show_command_help "backup"
165
exit 0
166
;;
167
*)
168
panic "未知选项: $1"
169
;;
170
esac
171
done
172
[[ -z "$src" ]] && panic "必须指定源目录"
173
log_info "从 ${YELLOW}$src${NC} 备份到 ${YELLOW}$dest${NC}"
174
175
# 示范如何安全处理包含空格的文件名
176
log_info "搜索文件..."
177
local count=0
178
179
while IFS= read -r -d '' file; do
180
((count++))
181
log_info "处理文件: ${YELLOW}$file${NC}"
182
# 这里放实际的处理逻辑
183
done < <(find "$src" -type f -print0)
184
185
log_success "共处理了 ${YELLOW}$count${NC} 个文件"
186
}
187
188
##############################################
189
# 主逻辑 #
190
##############################################
191
192
main() {
193
[[ $# -eq 0 ]] || [[ "$1" == "--help" ]] && show_help && exit 0
194
195
local cmd="$1"
196
shift
197
198
[[ -n "${COMMAND_MAP[$cmd]}" ]] || panic "未知命令: $cmd"
199
200
# 同样地,这里也要避免直接 IFS="::" 分割
201
# 先用 '|' 替换 "::" ,再做一次分割即可
202
local info="${COMMAND_MAP[$cmd]}"
203
info="${info//::/|}"
204
205
local desc handler options_str
206
IFS="|" read -r desc handler options_str <<< "$info"
207
208
# 调用对应的处理函数
209
$handler "$@"
210
}
211
212
main "$@"

使用方式

在命令行中输入 ./Shell_script_template.sh 即可看到帮助信息。命令行参数分为命令、选项。

命令是脚本的功能,比如 installremovebackup 等。

选项是命令支持的参数,比如 -p,--package 表示包名。每一个选项可以传入多种不同类型的输入参数,由该选项自主解析。

例如:

Terminal window
1
./Shell_script_template.sh install -p package_name

基于模板开发脚本

基于这个脚本模板开发新脚本时,只需要在 COMMAND_MAP 中添加新的命令,并实现对应的处理函数即可。

COMMAND_MAP 的命令配置格式为:[命令名]="描述::处理函数::选项声明1::选项声明2::..."。其中 选项声明 的格式为:-短选项,--长选项:描述

参数的解析由每一个选项的处理函数来完成。可以处理多个参数,或者不处理参数。

处理多参数选项

对于需要接收多个参数的选项,可以通过数组来收集参数。下面是一个示例:

Terminal window
1
# 在 COMMAND_MAP 中添加新命令
2
declare -A COMMAND_MAP=(
3
# ... 其他命令 ...
4
["compile"]="编译源文件::handle_compile::-s,--src:源文件列表::-d,--debug:启用调试"
5
)
6
7
# 处理函数示例
8
handle_compile() {
9
local -a source_files=() # 定义数组保存源文件
10
local debug=false
11
12
while [[ $# -gt 0 ]]; do
13
case $1 in
14
-s|--src)
15
shift # 移除 --src 选项
36 collapsed lines
16
# 收集所有不以 - 开头的参数作为源文件
17
while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
18
source_files+=("$1")
19
shift
20
done
21
;;
22
-d|--debug)
23
debug=true
24
shift
25
;;
26
-h|--help)
27
show_command_help "compile"
28
exit 0
29
;;
30
*)
31
panic "未知选项: $1"
32
;;
33
esac
34
done
35
36
# 检查是否提供了源文件
37
[[ ${#source_files[@]} -eq 0 ]] && panic "必须指定至少一个源文件"
38
39
# 显示编译信息
40
log_info "准备编译以下文件:"
41
for src in "${source_files[@]}"; do
42
log_info "- ${YELLOW}$src${NC}"
43
done
44
45
if [[ $debug == true ]]; then
46
log_info "调试模式已启用"
47
fi
48
49
# 这里添加实际的编译逻辑
50
log_success "编译完成"
51
}

使用示例:

Terminal window
1
# 编译多个源文件
2
./build.sh compile --src file1.c file2.c file3.c
3
4
# 启用调试模式编译
5
./build.sh compile --src main.c utils.c --debug
6
7
# 查看帮助
8
./build.sh compile --help

关键点说明:

  1. 使用数组 local -a source_files=() 来存储多个参数
  2. 通过 while 循环收集连续的非选项参数
  3. 使用 ! $1 =~ ^- 判断参数是否以 - 开头来区分选项和参数
  4. 使用 ${#source_files[@]} 获取数组长度
  5. 使用 for 循环遍历数组中的所有参数

说明:

在 bash 中,符号 =~ 表示通过正则表达式匹配。$1 =~ ^- 表示 $1 参数通过正则表达式 ^- 匹配,并返回匹配结果。若 $1 参数以 - 开头,则返回 true,否则返回 false

本文标题:Shell 脚本模板
文章作者:Lyndra
发布时间:2025-02-28
总访问量
总访客数人次
Copyright 2025
站点地图