☰
devops-exercises Shell 实战:用 for 循环 + `ls`/`du`/`cut` 统计当前目录下所有文件与目录的大小
2026/10/2 17:20:39 网站建设 项目流程
  • 文档
  • 教程
  • 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

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

导读

本文以 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.2M

cut在仓库 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

完整执行流程

把整段脚本放到一起看,它做了三件事:

  1. ls -S1生成"按大小降序、一行一个"的条目列表;
  2. for 循环逐个取出条目名;
  3. 对每个条目执行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提示。这样一道基础练习题就能成长为一个具备参数校验、错误处理和友好输出的完整运维工具。

六、小结与自查

通过这道题,你实际掌握了以下核心能力:

  1. ls -S1选项组合:-S按大小降序、-1单行输出,构造适合循环的条目列表;
  2. 命令替换$(...):把命令输出变成循环遍历的数据源;
  3. du -sh:以人类可读单位输出文件/目录递归占用大小;
  4. cut -f1:按 TAB 分隔符提取字段,精准拿到大小列;
  5. 引号与单词分割:理解"$i"为什么必要,以及何时改用while read;
  6. 可维护性改进:参数默认值、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

项目地址:https://gitcode.com/GitHub_Trending/de/devops-exercises
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询