devops-exercises 实战:用 Bash `while read` 循环与 `wc -c` 实现逐行字符计数
devops-exercises 实战:用 Bash while read 循环与 wc -c 实现逐行字符计数
本文以 devops-exercises 仓库中 Count Chars 练习及其官方解法为主线,系统讲解 Bash 交互式输入、while 循环读取与 wc 字符计数的完整套路。读完本文,你将能够独立编写一个"持续读取用户输入、逐行统计字符数、遇到空行自动退出"的 Bash 脚本,并理解 read、echo -n、wc -c 等关键命令的底层行为与常见坑点。
练习背景:这道题在考什么
在 topics/shell/README.md 的 Shell Scripting 练习表中,Count Chars 被明确归类为 "Input + While Loops"(输入 + While 循环),难度标注为 Basic(基础)。它紧跟在 Files Size(For 循环)之后、Sum(函数)之前,是仓库刻意设计的循环控制结构进阶练习:前一道题教你用 for 遍历列表,这道题则要求你改用 while 持续消费"交互式输入流"。
原始练习文档 count_chars.md 给出的目标是:
- 从用户处读取输入,直到得到空字符串为止;
- 对于读到的每一行,统计字符数量并打印出来。
约束条件只有两条:必须使用 while 循环,且假设至少有 3 行输入。也就是说,这道题刻意禁止你用 for、xargs、awk 等旁路方案,强制你掌握 while read 这一经典模式。
官方解法逐行拆解
仓库给出的标准答案位于 solutions/count_chars.md,完整代码如下:
#!/usr/bin/env bash
echo -n "Please insert your input: "
while read line; do
echo -n "$line" | wc -c
echo -n "Please insert your input: "
done
这段脚本虽短,却串联起了四个 Bash 核心知识点,我们逐一展开。
1. Shebang:#!/usr/bin/env bash
首行 #!/usr/bin/env bash 是 shebang 行。与直接写 #!/bin/bash 不同,env 方式会去 PATH 环境变量中查找 bash 的绝对路径,因此在 bash 安装位置不固定的系统上更具可移植性。仓库的 Shell 自学评估章节(见 topics/shell/README.md)也专门解释了 shebang 的含义:/bin/bash 是 Linux 用户登录时最常见的默认 shell,Bourne-again shell 功能更全、语法更完善,因此被广泛采用。
保存为 count_chars.sh 后,可通过以下方式运行:
bash count_chars.sh # 方式一:显式指定解释器
chmod +x count_chars.sh && ./count_chars.sh # 方式二:赋予执行权限后直接运行(shebang 生效)
2. echo -n:不换行输出提示符
echo -n 表示输出后不追加换行符。脚本用它打印 "Please insert your input: ",让光标停留在提示符末尾,用户输入与提示符处于同一行,交互体验更自然。
这一处细节也是后面统计结果正确的关键前提,原因见第 4 节。
3. while read line:循环读取输入直到空串
read 是 Bash 内建命令,作用是从标准输入(或指定文件描述符)读取一行并存入变量。仓库 README 的自学评估部分对此有精辟概括:"Using the keyword read so for example read x will wait for user input and will store it in the variable x"(用 read x 等待用户输入并存入变量 x)。
while read line; do ... done 的完整语义是:
- 每次循环迭代开始时,
read line阻塞等待一行输入; - 读到的内容存入变量
line; - 当读到空串(直接回车)或到达 EOF(如 Ctrl+D)时,
read返回非零退出码,循环条件不成立,while自动终止。
这正是练习目标 1("Read input from the user until you get empty string")的实现机制:无需显式 break 或判断语句,read 的退出状态本身就是天然的循环结束条件。
脚本在循环体末尾再次 echo -n 打印提示符,形成"提示 → 输入 → 统计 → 再提示"的循环;下一轮 read 继续等待。若想一次性输入多行后批量处理(例如输入以文件或管道方式重定向),while read line 同样适用,此时循环会逐行消费标准输入直到 EOF。
4. wc -c:为什么能数对字符数
wc(word count)的 -c 选项统计字节数。管道 echo -n "$line" | wc -c 的执行链条是:
echo -n "$line"输出变量内容(不加换行);- 管道
|把该输出接到wc -c的标准输入; wc -c统计收到的字节数并打印。
这里有两个极易踩坑的细节:
echo必须加-n。echo默认会在末尾追加换行符\n(1 个字节),若写成echo "$line" | wc -c,统计结果会比实际字符数多 1。解法用echo -n正是为了剔除这个"隐形多出的换行字节"。-c统计的是字节而非字符。对纯 ASCII 输入(字母、数字、标点)而言,字节数等于字符数;但如果输入包含 UTF-8 多字节字符(如中文"中"占 3 字节、emoji 占 4 字节),wc -c报出的数字会大于实际字符数。需要按字符数统计时应改用wc -m,或使用 Bash 的字符串长度语法${#line}。练习文档假定的是常规英文交互输入,因此wc -c是合理且最直接的答案。
5. 约束条件如何被满足
- "必须使用 while 循环":解法正体就是
while read line; do ... done,无for、until或其他循环结构; - "假设至少有 3 行输入":保证脚本能至少展示 3 轮完整的"提示—统计"迭代,便于人工验证循环确实在持续工作。
一次完整的运行演示
假设脚本已保存为 count_chars.sh,典型交互过程如下(hello、world、空行依次输入,末尾 Ctrl+D 或直接回车结束):
$ bash count_chars.sh
Please insert your input: hello
5
Please insert your input: world
5
Please insert your input:
0
Please insert your input:
hello为 5 个字符,wc -c输出5;world同为 5 个字符,输出5;- 直接回车输入空串,
echo -n ""输出 0 字节,wc -c输出0,随后read读到空串返回非零状态,循环结束。
注意最后一次提示符之后程序便退出,这正是目标 1"读到空字符串即停止"的直接体现。
纵向扩展:边界情况、健壮性与常见改进
空白行与多字节字符
- 输入为空格串(如
" ")时,read默认按IFS(内部字段分隔符)拆分,可能把行内容"吃掉";严格场景可改用while IFS= read -r line,-r防止反斜杠转义,IFS=避免首尾空白被剥离。 - 需要按字符统计时,
wc -m或${#line}是更好的选择,尤其是涉及中文、日文等 UTF-8 内容时。
管道子 shell 陷阱
while read 若出现在管道右侧(如 cat file | while read line; do ...; done),循环体会在子 shell 中执行,循环内部对变量的修改在循环结束后不会保留到父 shell。本练习直接读取终端输入、不涉及管道,因此不存在此问题;但编写批量文件处理脚本时务必留意这一 Bash 经典坑点。
交互式输入的退出方式
除输入空行外,用户也可用 Ctrl+D 发送 EOF 信号结束循环。两种方式都依赖 read 返回非零状态这一统一机制,脚本无需额外处理。
结合仓库证据:解法与练习文档的对应关系
| 知识点 | 仓库依据 |
|---|---|
| 练习定位(Input + While Loops) | topics/shell/README.md 练习表中的 Count Chars 行 |
| 练习原始目标与约束 | topics/shell/count_chars.md |
| 标准解法 | topics/shell/solutions/count_chars.md |
read 关键字用法说明 |
topics/shell/README.md 自学评估"如何获取用户输入"条目 |
| shebang 语义 | topics/shell/README.md 自学评估第一条 |
| 循环练习题组 | Files Size(For 循环)、Sum(函数) |
从源码结构可以推断,仓库将 Shell 练习按难度递进编排:Hello World(变量)→ Basic date(变量)→ Argument Check(条件判断)→ Files Size(For 循环)→ Count Chars(输入 + While 循环)→ Sum(函数)。count_chars.md 正处于从"循环结构"过渡到"函数封装"的衔接位置,是掌握交互式脚本的基础必修课。
小结
Count Chars 表面上是统计字符的小练习,实际覆盖了 Bash 脚本四大基础能力:shebang 与执行方式、read 的交互式输入语义、while 循环借助退出码实现"读到空即停"、以及 wc -c/echo -n 配合管道做精确字节统计。理解这四点的组合逻辑后,你可以轻松把它改造为更通用的"逐行处理工具"——例如把 wc -c 换成 wc -w 统计单词数、把提示词换成自定义参数、或把输入源从键盘换成文件重定向,从而应对各类交互式或批量文本处理需求。