Shell输入输出(一):echo / printf 输出,格式控制与颜色设置
------ 当你的脚本需要"说清楚"发生了什么
"日志不是给机器看的,是给人看的。而人,需要结构、重点和上下文。"------ 一个被满屏无格式日志逼出强迫症的架构师
凌晨两点,告警响起:电科金仓 KES 备份失败。
你 SSH 登录,运行诊断脚本,屏幕上刷出:
start
check dir
dir ok
connect db
connected
dump start
error
exit 1
没有时间戳,没有关键路径,没有错误详情。
你只能猜:"error" 是磁盘满?权限错?还是网络断?
那一刻我意识到:输出的质量,决定了排障的速度。
今天,我们就把 Shell 的输出这件事讲透------
不是"能不能打出来",而是"怎么让人一眼看懂"。
一、echo vs printf:简单输出 vs 精确控制
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
echo |
简单直接 | 行为不一致(不同系统 -e 支持不同) |
快速调试、简单提示 |
printf |
格式精确、POSIX 标准、可移植 | 语法稍复杂 | 正式脚本、结构化输出 |
最小对比:
bash
# echo:够用,但不可靠
echo "备份完成: $file"
# printf:可控,且一致
printf "备份完成: %s\n" "$file"
在跨平台或生产脚本中,优先用 printf。
二、格式控制:让输出有结构
运维中最常见的需求:对齐、列宽、数字精度。
示例:KES 实例状态表
你想输出类似这样的表格:
实例名 状态 连接数
core running 128
report stopped 0
archive running 5
用 printf 轻松实现:
bash
#!/usr/bin/env bash
printf "%-12s %-10s %s\n" "实例名" "状态" "连接数"
printf "%-12s %-10s %s\n" "----" "----" "----"
for role in core report archive; do
status=$(sys_ctl -D "/opt/Kingbase/Server/data_$role" status 2>/dev/null | grep -q running && echo "running" || echo "stopped")
conn_count=$(ksql -d myapp_"$role" -t -A -c "SELECT count(*) FROM sys_stat_activity;" 2>/dev/null || echo "0")
printf "%-12s %-10s %s\n" "$role" "$status" "$conn_count"
done
关键说明:
%s:字符串%-12s:左对齐,宽度12%5d:右对齐整数,宽度5\n:换行(printf不自动加)
这种输出可直接用于监控看板或邮件报告。
三、颜色设置:突出关键信息
人眼对颜色敏感远高于文字。合理使用颜色,能瞬间传递状态。
基础 ANSI 颜色码:
bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
安全用法(避免污染后续输出):
bash
printf "${RED}错误:%s${NC}\n" "无法连接数据库"
printf "${GREEN}[+] %s${NC}\n" "备份成功"
printf "${YELLOW}警告:%s${NC}\n" "磁盘使用率 > 80%"
✅ 必须以
${NC}结尾,否则终端会持续着色。
封装成函数,提升可维护性:
bash
log_error() { printf "${RED}[-] %s${NC}\\n" "$*"; }
log_info() { printf "${GREEN}[+] %s${NC}\\n" "$*"; }
log_warn() { printf "${YELLOW}[!] %s${NC}\\n" "$*"; }
# 使用
log_info "启动 KES 实例"
if ! sys_ctl start; then
log_error "启动失败,请检查日志"
exit 1
fi
这样,整个脚本的输出风格统一,且易于全局调整。
四、真实场景:KES 健康检查报告
bash
#!/usr/bin/env bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
check_instance() {
local role="$1"
local data_dir="/opt/Kingbase/Server/data_$role"
if [[ ! -d "$data_dir" ]]; then
printf "${RED}[%-10s] 数据目录缺失${NC}\n" "$role"
return 1
fi
if sys_ctl -D "$data_dir" status >/dev/null 2>&1; then
local conn=$(ksql -d "db_$role" -t -A -c "SELECT count(*) FROM sys_stat_activity;" 2>/dev/null || echo "N/A")
printf "${GREEN}[%-10s] 运行中 (连接: %s)${NC}\n" "$role" "$conn"
else
printf "${YELLOW}[%-10s] 已停止${NC}\n" "$role"
fi
}
echo "=== KES 实例健康状态 ==="
check_instance "core"
check_instance "report"
check_instance "archive"
输出效果:
=== KES 实例健康状态 ===
[core ] 运行中 (连接: 128)
[report ] 已停止
[archive ] 运行中 (连接: 5)
绿色表示正常,黄色表示需关注,红色表示故障------一目了然。
五、注意事项
-
不要在非终端环境强制着色
如果脚本输出被重定向到文件,颜色码会变成乱码。可加判断:
bashif [ -t 1 ]; then # 终端输出,启用颜色 RED='\033[0;31m' NC='\033[0m' else RED='' NC='' fi -
echo -e不可移植在 BSD(如 macOS)或某些嵌入式 shell 中,
echo -e可能不支持转义。printf是 POSIX 标准,更可靠。 -
避免过度使用颜色
颜色是信号,不是装饰。只用于状态(成功/警告/错误),不要全文彩色。
结语:输出是脚本的"用户界面"
在自动化运维中,你的脚本可能每天被调用上百次,但人只在出问题时看它 。
这时候,清晰、结构化、带重点的输出,就是缩短 MTTR(平均修复时间)的关键。
printf 和颜色不是炫技,而是对使用者的尊重。
今日实践 :
写一个函数
print_status(role, status, metric),根据 status 为 "ok"/"warn"/"err" 输出不同颜色的行,
并用
printf对齐字段。做完这个,你的脚本就拥有了"表达力"。
注:文中涉及的 KES(Kingbase Enterprise Server)是由电科金仓开发的企业级关系型数据库,其运维脚本常需生成结构化状态输出。技术背景可参考 产品页面。