Shell脚本学习指南-附录A-文件与文件系统
附录A 文件与文件系统
什么是文件
知识点:UNIX中文件是字节流,一串0或多个字节,不区分二进制/文本的内部格式。
作用:设备、管道、普通文件全部统一抽象成字节流,读写接口完全一致,程序不用关心数据来源是磁盘、网卡、终端。
- 文本文件:字节流以
LF(\n,十进制10)作为行结束;Windows是CRLF(\r\n)。 - 二进制文件:任意字节,文本文件属于二进制子集。
- 空文件:字节数为0,依然是合法文件,常用于标记状态(时间戳标记、告警占位)。
- 虚拟文件
/proc:不是磁盘上真实文件,内核向外暴露进程、硬件信息的虚拟字节流。
示例读取scsi硬件信息:
|
|
警告:文本程序遇到超长行、不可打印控制字符,行为会不可预期。
文件如何命名
知识点1:文件名允许字符限制
- 禁止字符:
NUL(0x00)、斜杠/。/是路径分隔符;NUL是C语言字符串结束标记。 - 现实最佳实践:只用ASCII字母、数字、
-、_、.。 - 不推荐:空格、shell元字符
* ? [] ! $、文件名以-开头(会被识别成命令选项)。
知识点2:大小写敏感
UNIX文件名区分大小写:readme、Readme、README是3个完全不同文件。
修改
LC_ALL=C,启用传统ASCII排序。
知识点3:文件名长度限制
NAME_MAX:单文件名最大字符数,POSIX最小14,现代系统普遍255。PATH_MAX:完整路径总字符上限。
查询系统限制示例:
重要实操:shell脚本中,存放文件名的变量必须双引号包裹,应对文件名带空格、特殊字符。
UNIX层级式文件系统
文件系统架构
知识点1:树状结构
根目录/,所有目录嵌套形成一棵树。
两个特殊目录:
.当前目录本身..父目录;根目录的..指向自己
知识点2:路径规则
- 连续多个斜杠
//tmp///x等价于/tmp/x,POSIX标准会合并。 - 路径末尾带
/,语义代表这是目录。
知识点3:mount 挂载
作用:把一个文件系统“嫁接”到现有目录树上;被挂载目录原有内容会被隐藏,卸载后恢复。
配置文件:/etc/fstab,开机自动挂载配置。
排查无法umount:使用
lsof查看哪些进程占用目录。
文件系统实现概况(inode核心)
知识点1:inode(索引节点)
作用:保存文件元数据:大小、权限、属主、时间戳、磁盘块位置;inode不保存文件名! 文件名保存在目录文件里面:目录存储「文件名 ↔ inode编号」映射。
现实意义:
- 同一个inode可以被多个目录条目引用 → 硬链接
- 文件删除:只是删除目录条目;inode连接计数归0才真正释放磁盘块。
知识点2:硬链接 vs 符号链接(软链接)
| 类型 | 关键点 | 限制 |
|---|---|---|
| 硬链接 | 多个文件名指向同一个inode | 不能跨文件系统;不能链接目录(除 . ..) |
| 符号链接 | 保存文本形式路径,指向另一个文件名 | 可以跨文件系统,可以指向目录;可以循环断裂 |
示例,软链接循环报错:
软链接移动坑:写相对路径的软链接,移动父目录还能正常;写绝对路径移动后大概率断裂。
设备作为UNIX文件
知识点:一切皆文件,设备放在/dev
作用:磁盘、声卡、串口全部抽象成文件,open/read/write统一操作。
常用特殊虚拟设备:
/dev/null:黑洞,写入全部丢弃,读取立刻返回EOF/dev/tty:当前终端/dev/urandom:获取系统随机字节,密码脚本常用
UNIX文件到底可以多大
- 文件大小受inode存储的长度字段、磁盘块大小共同约束;老32位系统单文件上限约2GB。
- 文件系统会预留一部分磁盘空间只允许root使用,防止普通用户把磁盘彻底打满系统崩溃。
查看文件系统磁盘空间:
没有名称的文件
知识点:重定向的文件,进程内部不记住原始文件名
/dev/stdin/dev/stdout/dev/stderr,部分系统提供。- 命令惯例:单独一个
-代表标准输入/标准输出。
UNIX文件属性
文件所有权与权限
知识点1:所有权
每个文件有 uid(用户ID)、gid(组ID);数字ID才是内核识别的,用户名只是/etc/passwd做的映射。
知识点2:基础权限 r w x
对普通文件:
- r:读内容
- w:写修改内容
- x:可以执行(程序/脚本必须有x权限才能运行)
对目录含义完全不一样(高频考点)
- r:可以列出目录里面的文件名
- w:可以在目录创建、删除文件(哪怕你对这个文件没有写权限!)
- x:可以进入cd到此目录、可以访问目录下文件(必须知道文件名)
目录实操演示:
r (目录:Read):读取这张表格 → 列出目录内文件名
通俗比喻:文件夹像一个带盒子:
r = 允许你掀开盒盖,浏览盒子里面全部纸条(文件名列表)
盒子盖子不让你掀开(无 r),但你已经提前记住纸条名字,你依旧伸手可以摸到那张纸条(前提要有 x,允许伸手进去)。
w(目录:Write):修改这张表格 → 创建、删除、重命名条目
通俗比喻:文件夹像一个带盒子:
w = 允许你在盒子里新增、扔掉、改写纸条(增删/重命名文件名条目)
哪怕纸条上的内容你完全看不懂(文件本身没有读写权限),也依旧可以把纸条直接扔掉(删除文件)。
x(目录:Execute,搜索位):进入目录、解析路径,访问内部条目
通俗比喻:文件夹像一个带盒子:
x = 允许你把手伸进盒子内部,定位摸到指定的某一张纸条(按文件名查找文件inode)
盒子不让你伸手进去(无 x),就算你牢牢记住所有纸条的名字,也碰不到盒子里任何一张纸条。
umask:新建文件/目录权限掩码
作用:不是设置权限,是 “要拿掉哪些权限位”。
计算规则:
- 新建文件原始最大权限:0666(rw‑rw‑rw‑,文件默认没有执行 x)
- 新建目录原始最大权限:0777(rwx‑rwx‑rwx,目录需要 x 进入)
最终权限 = 原始权限 减去 umask 掩码的权限位(按位掩码清除)
027 拆解
八进制 027,拆分成 3 组,分别对应 用户(u) 组(g) 其他(o)
- 第一位
0:用户 u → 不拿掉任何权限 - 第二位
2:组 g → 拿掉w(写)权限 - 第三位
7:其他 o → 拿掉r w x全部三种权限
脚本最佳实践:脚本开头明确设置umask,放在一切创建文件操作之前。
① 新建普通文件:touch testfile
文件原始默认最大权限:0666 → -rw‑rw‑rw‑
减去 umask 027:
- 用户:6‑0 =6 →
rw‑ - 组:6‑2 =4 →
r‑‑ - 其他:6‑7 =0 →
‑‑‑
testfile 最终权限:‑rw‑r‑‑‑‑‑(0640)
注意:普通文件天生不会自动带上执行 x,哪怕原始是 666,不是 777。touch 出来的文件永远没有 x 权限。
② 如果新建目录 mkdir testdir
目录原始最大权限是 0777,减去 umask 027
- 用户:7‑0 =7 → rwx
- 组:7‑2 =5 → r‑x
- 其他:7‑7 =0 → —
目录最终权限:drwxr‑x‑‑‑(0750)
特殊权限位:set‑uid、set‑gid、sticky粘滞位
- set‑uid:执行进程拥有文件所有者权限。
重要警告:shell脚本不要设置set‑uid,有大量安全漏洞。
- set‑gid:新建文件继承目录组ID;常用于协作目录。
- sticky粘滞位:典型
/tmp目录;目录开启后,用户只能删除自己创建的文件。
文件时间戳
inode维护3种时间戳
atimeaccess:文件被读取访问时间mtimemodify:文件内容被修改时间ctimechange:inode元数据变更(权限、属主、硬链接增减)
备份工具tar会修改atime,会破坏原始访问时间。
文件连接(硬链接/软链接补充)
- 硬链接:同一个inode;修改任意文件名,全部链接同步看到变更;删除一个链接不会删除数据,链接计数归零才销毁。
- 软链接:保存路径字符串;源文件删除,软链接变成断裂链接。
UNIX扩展文件名惯例
UNIX系统文件扩展名没有内核强制意义,仅仅是人约定俗成。
.shshell脚本、.cC源码、.tar打包文件只是惯例;没有.exe概念,是否可执行看权限x位。
临时备份文件惯例:~、#后缀/前缀,编辑器自动生成备份。
小结
整理本书附录核心要点清单
- UNIX把一切抽象字节流,普通文件、设备、proc虚拟文件统一处理。
- inode保存文件元数据;目录保存文件名‑inode映射;硬链接、软链接机制。
- 文件、目录的r/w/x权限含义目录和普通文件语义不一样;umask控制新建文件默认权限。
- mount挂载构建文件系统树;
/tmp使用sticky粘滞位保护公共目录。 - 文件名要避开空格、特殊元字符,脚本变量引用文件名务必双引号
"$var"。 - 文件3种时间戳 atime/mtime/ctime,各自触发条件不同。
文章作者 会写代码的小郎中
上次更新 2011-07-29
许可协议 CC BY-NC-ND 4.0