Saki's 研究记录

Shell 脚本编写的六大技巧

字数统计: 1.1k阅读时长: 4 min
2024/11/06

原文链接《6 Techniques I Use to Create a Great User Experience for Shell Scripts》

根据 Shell 脚本难读难写,本文给出 6 个小技巧,可以提升用户体验。

1. 全面的错误处理和输入验证

为了提供更好的用户体验,脚本中实现了全面的错误处理和输入验证机制,确保用户能看到清晰的错误提示。示例代码如下:

1
2
3
4
5
6
if [ -z "$1" ] 
then
echo "Usage: evaluate.sh <fork name> (<fork name 2> ...)"
echo " for each fork, there must be a 'calculate_average_<fork name>.sh' script and an optional 'prepare_<fork name>.sh'."
exit 1
fi

这种方法可以帮助用户快速识别和解决问题,节省时间并减少挫折感。

2. 清晰的彩色输出

为了让脚本的输出更易读和用户友好,使用 ANSI 颜色代码来突出显示重要信息、警告和错误。示例代码如下:

1
2
3
BOLD_RED='\033[1;31m'
RESET='\033[0m'
echo -e "${BOLD_RED}ERROR${RESET}: ./calculate_average_$fork.sh does not exist." >&2

这种视觉区分帮助用户快速理解每条消息的性质。

3. 详细的进度报告

为了提高脚本执行过程的透明度,可以设计一个函数,让它会在执行命令前先打印出命令内容。示例代码如下:

1
2
3
4
function print_and_execute() {
echo "+ $@" >&2
"$@"
}

这与 Bash 内置的 set -x 跟踪输出格式相匹配,但给予脚本作者对打印内容更精细的控制。
这种透明度不仅能让用户了解执行情况,还能在出现问题时帮助调试。

4. 灵活的错误处理策略

为了平衡脚本的健壮性和容错性,可以采用了 set -eset +e 选项的组合策略:脚本核心代码出错时立即停止,而单个 fork 的失败则不会影响整体执行。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 脚本开始时
set -eo pipefail

# 运行每个 fork 的测试和基准测试之前
for fork in "$@"; do
set +e # 我们不希望因为某个 fork 的 prepare.sh、test.sh 或 hyperfine 失败而导致脚本提前退出

# 运行准备脚本(简化版)
print_and_execute source "./prepare_$fork.sh"

# 运行测试套件(简化版)
print_and_execute $TIMEOUT ./test.sh $fork

# ... (其他针对特定 fork 的操作)
done
set -e # fork 相关操作完成后重新启用错误即退出

这种方法让脚本作者能够精细控制哪些错误会导致脚本退出,哪些可以用其他方式处理。

5. 跨平台适配

考虑到用户可能在不同的操作系统上运行这个脚本,可以添加检测操作系统并相应调整脚本行为的逻辑。示例代码如下:

1
2
3
4
5
6
7
8
9
if [ "$(uname -s)" == "Linux" ]; then 
TIMEOUT="timeout -v $RUN_TIME_LIMIT"
else # 假设是 MacOS
if [ -x "$(command -v gtimeout)" ]; then
TIMEOUT="gtimeout -v $RUN_TIME_LIMIT"
else
echo -e "${BOLD_YELLOW}WARNING${RESET} gtimeout not available, install with `brew install coreutils` or benchmark runs may take indefinitely long."
fi
fi

这确保了在不同环境下的一致体验。例如,许多 #1BRC(One Billion Row Challenge) 参与者在 MacOS 上开发,而评估机器运行的是 Linux

6. 时间戳输出文件

为了支持多次基准测试运行而不覆盖之前的结果,可以实现了一个带时间戳的文件输出系统。这允许用户多次运行脚本并保留所有结果的历史记录。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
filetimestamp=$(date +"%Y%m%d%H%M%S")

# ... (在每个 fork 的循环中)
HYPERFINE_OPTS="--warmup 0 --runs $RUNS --export-json $fork-$filetimestamp-timing.json --output ./$fork-$filetimestamp.out"

# ... (基准测试之后)
echo "Raw results saved to file(s):"
for fork in "$@"; do
if [ -f "$fork-$filetimestamp-timing.json" ]; then
cat $fork-$filetimestamp-timing.json >> $fork-$filetimestamp.out
rm $fork-$filetimestamp-timing.json
fi

if [ -f "$fork-$filetimestamp.out" ]; then
echo " $fork-$filetimestamp.out"
fi
done

--

通过实现这些技巧,目标是创建一个用户友好、信息丰富且健壮的 shell 脚本,为用户运行和分析基准测试提供出色的体验。希望这些想法能启发你提升自己的 shell 脚本的用户体验!

完整的基准测试评估脚本可以在 #1BRC 仓库中找到:evaluate.sh

以上。

CATALOG
  1. 1. 原文链接《6 Techniques I Use to Create a Great User Experience for Shell Scripts》
  2. 2. 1. 全面的错误处理和输入验证
  3. 3. 2. 清晰的彩色输出
  4. 4. 3. 详细的进度报告
  5. 5. 4. 灵活的错误处理策略
  6. 6. 5. 跨平台适配
  7. 7. 6. 时间戳输出文件