☰
Shell输入输出详解:read、echo、printf、颜色输出
2026/10/3 2:37:28 网站建设 项目流程

Shell 脚本跟用户打交道,靠的就是输入输出这几件事:read从键盘读变量,echo、printf往外打印。看着都是最基础的命令,真用起来一堆细节——read怎么隐藏密码、echo为什么要加-e、printf怎么对齐列、日志里怎么打出一行红字。这一篇把它们逐个拆开。

一、read:从标准输入读变量

语法格式

read[选项]变量名...

不带变量名时,读进来的内容默认存进REPLY,这是 bash 的约定,不是 bug。

常用参数

参数说明
-p 提示串把提示打到标准错误,不换行,紧跟在输入位置等用户敲
-s静默模式,输入不回显,输密码用
-t 秒数超时返回非零,脚本里用来做"默认继续"
-n 字符数读满 N 个字符就返回,不用按回车
-a 数组名把输入按 IFS 拆成数组
-d 分隔符用指定字符代替换行作为结束符

示例与预期输出

最基础的交互:

read-p"请输入你的名字: "nameecho"你好,$name"

预期输出(用户敲Ben后回车):

请输入你的名字: Ben 你好, Ben

静默读密码:

read-s-p"请输入密码: "pwechoecho"长度是${#pw}"

敲密码时屏幕一个字符都不显示,最后一行打长度。

超时给默认值:

read-t5-p"5 秒内按 Y 继续,其它任意键退出: "ansans=${ans:-N}["$ans"="Y"]&&echo"继续"||echo"退出"

5 秒不敲,ans是空串,${ans:-N}落回默认值 N。

一行拆进数组:

read-aparts<<<"apple banana cherry"echo"第一个:${parts[0]}, 共${#parts[@]}个"

预期输出:

第一个: apple, 共 3 个

⚠️ 常见坑

  1. -p后面的提示串含空格或中文,必须加引号,否则 shell 会把空格后的部分当成另一个参数。
  2. 从管道或文件喂read时,循环里的read会把后面所有输入一次性吃掉(while read那一篇专门讲)。
  3. read -s在没有 tty 的环境(cron、管道)里不生效,因为它要改终端属性。

一行一次读多个变量也很常见,按 IFS 拆:

read-p"输入姓名和年龄: "name ageecho"$name今年$age岁"

预期输出(输入Ben 25):

输入姓名和年龄: Ben 25 Ben 今年 25 岁

输入段数比变量多,最后一个变量吃掉剩余全部。比如只读name age两个变量,敲Ben 25 Beijing,age会拿到25 Beijing。

二、echo:最常用的打印

常用参数

参数说明
-n行尾不加换行
-e开启转义解释,识别\n\t\\
-E关闭转义(bash 默认行为)

示例与预期输出

echo"hello"echo-n"不换行"echo" 接着上面"

预期输出:

hello 不换行 接着上面

用-e打制表和换行:

echo-e"姓名\tBen\n年龄\t25"
姓名 Ben 年龄 25

常见转义符:

写法含义
\n换行
\t制表符
\\反斜杠本身
\a响铃(有些终端会"嘀"一声)
\033/\eESC 字符,颜色码的开头

注意:echo不带-e时,\n原样打印成两个字符。这是新手最常遇到的"为什么我的 \n 不换行"。

⚠️ 注意

echo在 bash 和 dash 下对-e的默认处理不一样。脚本要跨发行版跑(比如 Debian 默认sh是 dash),格式化输出交给printf,echo只用来打一行简单文本。另外echo $var不加引号,变量里带空格会被 IFS 拆开,还会把*.log这种通配符展开,这个坑在下面 printf 一节一起说。

三、printf:格式化输出,跨 shell 一致

printf模仿 C 语言的同名函数,格式串后面跟参数。它不自动加换行,这是和echo最大的区别。

常用格式符

格式符含义
%s字符串
%d十进制整数
%f浮点数
%x十六进制
%%百分号本身

宽度和对齐写在%和字母之间:%10s右对齐占 10 位,%-10s左对齐,%.2f保留两位小数。

示例与预期输出

printf"%-10s %6s\n"姓名 年龄printf"%-10s %6d\n"Ben25printf"%-10s %6d\n"Alice30

预期输出:

姓名 年龄 Ben 25 Alice 30

%-10s左对齐占 10 个字符宽,%6d右对齐占 6 位,两列自动对齐。这就是printf比echo强的地方——做表格、对齐数字,echo做不到。

保留两位小数:

printf"Pi = %.2f\n"3.14159
Pi = 3.14

循环里用printf打对齐的表:

whileread-ruser uid;doprintf"%-15s %6d\n""$user""$uid"done<<'EOF' root 0 zhangsan 1000 lisi 1001 EOF

预期输出:

root 0 zhangsan 1000 lisi 1001

%-15s左对齐占 15 位,短名字自动补空格,长名字顶出来也不影响下一列。这种表用echo拼不出来。

⚠️ 常见错误

  1. 忘记加\n,所有输出挤在一行。
  2. 格式符数量多于参数,剩下的%s填空串、%d填 0;参数多于格式符,格式串会从头再用一遍。写循环打印时这个"从头再用"经常导致意外重复。

四、颜色输出:ANSI 转义码

终端里打红字,本质是往输出里塞一段特殊字符序列,终端模拟器识别后改颜色。这段序列以\033[开头、以m结束。

常用颜色码

编码含义
0恢复默认
1加粗
31前景红
32前景绿
33前景黄
34前景蓝
37前景白
41背景红
42背景绿

封装成变量用

RED='\033[31m'GREEN='\033[32m'YELLOW='\033[33m'NC='\033[0m'echo-e"${RED}错误: 备份失败${NC}"echo-e"${GREEN}成功: 备份完成${NC}"echo-e"${YELLOW}警告: 磁盘剩余不足 10%${NC}"

屏幕上三行分别是红、绿、黄。每次打完必须用${NC}复位,否则后面所有输出都带着这个颜色,连下一个命令提示符都会变色。

用tput更可读,也不依赖硬编码码值:

RED=$(tput setaf1)GREEN=$(tput setaf2)NC=$(tput sgr0)echo"${RED}错误${NC}"

⚠️ 注意

重定向到文件时,颜色码会变成一堆^[[31m乱字符写进日志。判断一下输出是不是连着终端,是才上色:

if[-t1];thenRED=$(tput setaf1)GREEN=$(tput setaf2)NC=$(tput sgr0)elseRED="";GREEN="";NC=""fi

-t 1测试标准输出是否连着终端,是写彩色日志的标配。

五、知识扩展:read 为什么非得按回车

read拿到一个字符就返回吗?默认不是。这跟终端工作在 canonical(规范)模式有关:内核的终端驱动会自己把一行字符攒起来、处理退格和 Ctrl+U,攒够一个换行才把整行交给read。所以你敲一半按退格改字,read根本看不到中间过程,它只收到最终那一行。

-n N和-s其实是偷偷把终端切到非规范模式(或关掉回显),读完再切回来。这也是为什么在管道里read -s读不到密码——没有 tty 可切。写交互式脚本遇到"read 怎么不响应按键",先检查是不是被 cron 或管道包着,那种环境下根本没有终端。

ANSI 转义码同理:它不是 shell 干的,是终端模拟器在解释。把带颜色的输出重定向到文件、再cat出来,文件里存的就是那串\033[31m;放到不支持转义的终端(某些串口控制台、老的网络设备 CLI),它就老老实实显示成乱码。颜色是终端的能力,不是文本的一部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询