- 文档
- 教程
- DevOps
- 运维
【免费下载链接】devops-exercises
Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions
导读
本文以 devops-exercises 仓库中 Files Size 练习 及其 官方解答 为骨架,系统讲解如何用 Bash 编写一个"遍历当前目录、逐项打印文件与目录名称和占用空间大小"的脚本。文中会逐行拆解ls -S1、du -sh、cut -f1与for循环的组合用法,并补充参数化、健壮性改进、替代实现与相关练习,帮助你掌握"遍历目录 + 解析命令输出 + 循环输出"这一 DevOps 日常运维中最常用的脚本套路。读完本文,你能独立写出可复用、可扩展的目录大小统计脚本,并理解每一步背后的原理。
一、练习背景:它在 Shell 练习题中的位置
在仓库的 Shell 脚本练习总表 中,Files Size 属于For Loops(for 循环)主题,难度标记为Basic(基础),与 Hello World(变量)、Basic date(变量)、Factors(算术)、Argument Check(条件判断)、Count Chars(输入 + while 循环)、Sum(函数)等题目一起构成了一条从变量、条件、循环到函数的循序渐进学习路径。
原题目标非常简洁:
打印当前路径下每一个文件和目录的名称与大小;并且必须使用至少一个 for 循环。
这道题的训练点非常明确:在必须使用 for 循环的前提下,把"命令输出"变成"循环输入",再把每条命令的输出按需要"切片",最后格式化打印。这正是日常巡检脚本、CI 日志整理、磁盘占用分析中反复出现的模式。
二、官方解答逐行拆解
仓库给出的标准解答保存在 solutions/files_size.md,完整脚本如下:
#!/usr/bin/env bash for i in $(ls -S1); do echo $i: $(du -sh "$i" | cut -f1) done下面逐行说明它的工作原理。
1. Shebang:#!/usr/bin/env bash
脚本第一行的 Shebang 告诉操作系统用哪个解释器来执行本脚本。仓库 Shell 自测部分 对 Shebang 有专门解释:/bin/bash是最常用的默认登录 shell,Bash(Bourne-again shell)功能丰富、语法完善,能执行绝大多数脚本。
这里使用#!/usr/bin/env bash而不是写死#!/bin/bash,好处是让系统在PATH中查找bash,在不同发行版上兼容性更好。要让 Shebang 生效,还需要给脚本赋予执行权限并直接运行:
chmod +x files_size.sh ./files_size.sh(注意:仓库是只读的,脚本可直接在本地按上述方式创建和运行,无需改动仓库。)
2. 命令替换:$(ls -S1)
$(...)是命令替换(command substitution):先执行括号内的命令,再把它的标准输出作为字符串代入到外层语句中。这里是整个脚本的数据来源。
ls -S1由两个选项组成:
-S:按文件大小降序排序(大的在前);-1(数字 1):每行只输出一个条目,避免多列排版。
于是$(ls -S1)展开后就是一行一个文件/目录名的列表,例如:
big_file.bin script.sh some_dir small.txt这正是 for 循环理想的输入格式——按行分隔、无需额外处理。需要注意的是,ls默认不输出隐藏文件(以.开头的文件),因此本脚本统计的是当前目录下的非隐藏条目。
3. for 循环:for i in $(ls -S1); do ... done
这是原题硬性要求(use at least one for loop!)的核心。循环把上面命令替换得到的每个单词(默认按空白/换行切分)依次赋给变量i,每次迭代执行循环体内的命令:
for i in $(ls -S1); do echo $i: $(du -sh "$i" | cut -f1) done仓库 Shell 自测部分也要求读者理解"循环是什么、有哪些类型、如何演示使用"——本题的 for 循环正是最经典的演示案例:for遍历列表、do...done界定循环体。
4. 统计大小:du -sh "$i"
对每个条目$i执行du:
-s(summary):只输出总计大小,不递归列出内部子项;-h(human-readable):以人类可读的单位(K、M、G)输出,而非纯字节数;"$i":用双引号包裹变量,这是 Bash 最佳实践——防止文件名含空格或特殊字符时被错误切分(例如my file.txt不加引号会被拆成两个参数)。
du对目录输出的是该目录递归占用的总空间,对文件输出的是该文件的磁盘占用大小(通常以 4K 块为最小单位对齐,因此可能略大于文件字节数)。例如输出:
1.2M big_file.bin注意du -sh的默认输出是大小<TAB>路径两列。
5. 切出大小列:cut -f1
cut以分隔符切分每行并取出指定字段。默认分隔符是TAB 制表符,-f1表示取第 1 个字段,也就是把du输出的1.2M big_file.bin中的1.2M单独提取出来:
1.2Mcut在仓库 Shell 练习中的其他解法里也频繁出现,例如 Count Chars 解答 使用wc -c统计字符数,Directories Comparison 解答 使用awk -F " " '{print $1}'提取 checksum 字段——可见"命令输出 → 提取字段"是贯穿整个练习题集的基本功。
6. 输出:echo $i: ...
把名称和大小拼成一行打印:
big_file.bin: 1.2M script.sh: 4.0K some_dir: 10K small.txt: 4.0K完整执行流程
把整段脚本放到一起看,它做了三件事:
ls -S1生成"按大小降序、一行一个"的条目列表;- for 循环逐个取出条目名;
- 对每个条目执行
du -sh并cut -f1提取大小,最后拼接打印。
由于ls -S按大小降序,输出天然就是"从大到小"的排行榜,非常适合快速找出哪个文件/目录最占空间。
三、实际运行效果示例
在当前目录放置一个测试目录demo/,内含一个大文件、一个脚本、一个子目录和一个小文本文件,运行上述脚本,输出大致如下:
$ ./files_size.sh big_file.bin: 512M data_dir: 98M script.sh: 4.0K readme.txt: 4.0K可以看到:大文件排在最前(-S降序生效);目录data_dir输出的是其递归总占用;每个条目的名称与人类可读大小一一对应。
四、进阶改进与健壮性增强
官方解答足够简洁,但直接用于生产环境时,有几个可以明显加固的点(以下均为在本地脚本中可实践的改进,不改动仓库文件)。
1. 文件名含空格的问题
for i in $(ls -S1)依赖单词分割:只要文件名里含空格(如my file.txt),命令替换的结果就会被拆成两个单词,循环会把my和file.txt当成两个条目,导致du报错。更稳健的做法是改用按行读取的方式:
#!/usr/bin/env bash while IFS= read -r entry; do size=$(du -sh "$entry" | cut -f1) printf "%s: %s\n" "$entry" "$size" done < <(ls -S1)这里:
IFS= read -r关闭分隔符处理并按行原样读取(-r防止反斜杠转义);< <(...)使用进程替换(process substitution),把ls -S1的输出作为 while 循环的输入流——仓库自测部分也专门考过进程替换的用法(diff <(ls /tmp) <(ls /var/tmp)),其用途正是"把命令输出传给另一条命令当输入,且无需经过管道"。
2. 隐藏文件与更精确的大小
若希望把隐藏文件也纳入统计,可改用ls -AS1(加-A输出除.和..之外的所有条目)。若希望拿到精确字节数而非人类可读大小,可以把du -sh换成du -s(去掉-h),再结合cut -f1输出纯数字,便于在脚本里做比较或汇总。
3. 对单条命令失败做容错
du对无权限读取的目录会输出错误信息,可以把标准错误丢弃并给出友好提示:
size=$(du -sh "$entry" 2>/dev/null | cut -f1) [ -n "$size" ] || size="N/A"4. 参数化:把"当前目录"换成"目标目录"
原题限定"当前路径",若要统计任意目录,只需接受第一个参数并cd或直接传入du:
#!/usr/bin/env bash target="${1:-.}" while IFS= read -r entry; do size=$(du -sh "$target/$entry" 2>/dev/null | cut -f1) printf "%s: %s\n" "$entry" "${size:-N/A}" done < <(ls -S1 "$target")"${1:-.}"是参数默认值展开——没有传参时用当前目录.,这正是仓库自测里考察的"${1:-yay}"模式的实际应用。
五、把本题放进更大的练习上下文
这道题不是孤立的。把本解答与仓库其他 Shell 练习放在一起,可以看出一条清晰的技能递进线:
| 练习文件 | 主题 | 与本题的关联 |
|---|---|---|
| Hello World / 解答 | 变量 | 理解$i这类变量赋值与展开 |
| Argument Check / 解答 | 条件判断 | 为脚本增加参数校验(如检查目录是否存在) |
| Count Chars / 解答 | 输入 + while 循环 | 对比 while 按行读与 for 按单词遍历的取舍 |
| Sum / 解答 | 函数 | 把"取大小并格式化"封装成函数复用 |
| Directories Comparison / 解答 | Misc | 展示ls | sort | md5sum | awk的命令链式处理风格 |
例如,参考 Sum 解答 中"先用正则校验输入、失败即exit 2"的写法,可以给 Files Size 脚本加上"参数必须是存在的目录,否则退出并提示用法"的前置校验;参考 Directories Comparison 里test $# -ne 2的参数数量检查,可以补全脚本的USAGE提示。这样一道基础练习题就能成长为一个具备参数校验、错误处理和友好输出的完整运维工具。
六、小结与自查
通过这道题,你实际掌握了以下核心能力:
ls -S1选项组合:-S按大小降序、-1单行输出,构造适合循环的条目列表;- 命令替换
$(...):把命令输出变成循环遍历的数据源; du -sh:以人类可读单位输出文件/目录递归占用大小;cut -f1:按 TAB 分隔符提取字段,精准拿到大小列;- 引号与单词分割:理解
"$i"为什么必要,以及何时改用while read; - 可维护性改进:参数默认值、
2>/dev/null容错、进程替换。
可以试着用仓库 Shell 自测部分 的问题验证自己的理解:#!/bin/bash是什么?脚本默认在命令失败时是否退出?$@与$*的区别?${var//*.}这类子串操作如何影响你对输出字段的处理?把这些概念与本题的实战代码结合起来,就是一次完整的 Shell 循环与文本处理训练。
最后,把脚本放到任意真实目录里跑一遍(例如du -sh常用作排查磁盘占用的第一步),对照输出检查是否与du -sh .的总量一致,你的 for 循环 + 文本切片技能就真正落地了。
- 文档
- 教程
- DevOps
- 运维
【免费下载链接】devops-exercises
Linux, Jenkins, AWS, SRE, Prometheus, Docker, Python, Ansible, Git, Kubernetes, Terraform, OpenStack, SQL, NoSQL, Azure, GCP, DNS, Elastic, Network, Virtualization. DevOps Interview Questions
相关推荐
devops-exercises Shell 实战:用 for 循环打印当前目录下所有文件与目录的大小(Files Size 练习精解)
devops exercises Shell 实战:用 for 循环打印当前目录下所有文件与目录的大小(Files Size 练习精解) 本篇技术指南围绕 de
文档教程DevOps运维React Native环境配置与编译:10分钟快速搭建开发环境
React Native环境配置与编译:10分钟快速搭建开发环境 🚀 React Native环境配置 是每个移动开发者的必经之路!无论你是前端开发者想要进军
实战 Bash 脚本:删除目录(含子目录)中所有空文件的完整指南 —— devops-exercises 之 Empty Files 习题解析
实战 Bash 脚本:删除目录(含子目录)中所有空文件的完整指南 —— devops exercises 之 Empty Files 习题解析 本篇指南围绕开源
文档教程DevOps运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考