第18章 File类与Dir类

掌握文件重命名/复制/删除、目录遍历/创建/删除、属性和路径操作,并用Find、Tempfile、FileUtils建立受控递归与原子替换。

学习目标

  • 能解释变更、复制与删除文件与目录内容的读取在“第18章 File类与Dir类”中的责任边界
  • 能围绕“怎样让路径、目录遍历、临时文件和复制删除操作保持原子与可回退?”运行正常与故障轨迹并定位首个分岔
  • 能用“第18章 File类与Dir类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”证明“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”

来源、版次与运行边界

“第18章 File类与Dir类”以作者维护的第 5 版支持页核定 2016 年 3 月 12 日首刷、两位作者、松本行弘监修以及四部分 23 章目录;逐章程序清单练习答案勘误只作为公开支持材料,不被冒充为原书全文。

对“第18章 File类与Dir类”而言,中文解释、示例、交互、练习和答案均为独立教学重写;站内中文章名是与官方 23 章顺序对应的课程映射,不宣称是日文小节的逐字翻译,也不从公开程序清单复制整段实现。

“第18章 File类与Dir类”固定在 Ruby 2.3 语境;Ruby 2.3.0 官方文档稳定版发布说明用于核对当时可用的语言和标准库行为。现代 Ruby 的差异只能另列迁移说明,不能静默改变本页示例的版本结论。

围绕“怎样让路径、目录遍历、临时文件和复制删除操作保持原子与可回退?”,本页验收“第18章 File类与Dir类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”。先预测正常轨迹,再只注入“先删除目标再写新文件,中途失败后同时失去旧版本和新版本”;若无法定位第一处状态分岔,就拒绝当前解释。

从“路径字符串指向一个稳定文件”开始纠正

Path是namespace中的name,不是稳定object capability;检查后到操作前,它可被替换、重命名或经symlink指向别处。先于File API。

先预测:File.file?(path)为true后再delete是否安全?不保证;另一个process可在两步间替换path。普通工具尽量直接执行目标operation并处理exception;高敏感场景使用directory handle/sandbox/platform secure API减少TOCTOU。

File类:变更、复制与删除文件

File继承IO能力并增加path/metadata operations。File.rename更改directory entry,同filesystem上常可作为atomic replacement基础,但cross-device会失败,overwrite与open-handle behavior因OS不同。必须按部署平台验证。

source = "draft.txt"
destination = "published.txt"
 
File.rename(source, destination)

复制文件

FileUtils.cp复制contents并有metadata options;IO.copy_stream在IO/path间传输bytes。Copy不是atomic:中途失败可能留下partial destination。写新文件使用temp destination,完整flush/验证后rename;保留mode/owner/timestamp是否需要由contract决定。

require "fileutils"
 
FileUtils.cp("source.bin", "copy.bin", preserve: true)
raise "copy mismatch" unless File.size("source.bin") == File.size("copy.bin")

Size相等不是内容完整证明;关键artifact用digest并注意读取期间source变化。Copy symlink本身还是target、是否跨mount、sparse file/xattr怎样处理都需选API。

删除文件

File.delete/unlink移除name,missing/permission抛异常;打开handle在Unix/Windows表现不同。避免rescue nil吞错。

批量删除先固定root与manifest,逐项记录result,支持dry-run;不要rm_rf拼接用户input。需要trash/recovery不是unlink,而是move到受控区并管理retention。

目录的操作:目录内容的读取

Dir.entries包含./..Dir.children/each_child在支持版本排除;Ruby 2.3 availability需验证,可手工reject。返回names通常相对dir,顺序来自filesystem,不是sorted contract。保证输出可复现。

entries = Dir.entries(".")
  .reject { |name| name == "." || name == ".." }
  .sort
 
entries.each { |name| puts name }

Enumerate后entry可消失/改变;逐项operation处理ENOENT。Huge directory避免一次entries materialize,使用foreach/each_child,并限制count/time。

目录的创建与删除

Dir.mkdir(path, mode)创建一层,FileUtils.mkdir_p创建parents;mode还受umask。Existing path可能是file/symlink/directory,不能统一当success。。

Dir.rmdir只删empty directory;recursive remove由FileUtils.rm_r/rm_rf,但危险面大。Required root不可为/、home或empty expansion;解析absolute/canonical policy并使用allowlisted workspace。递归删除遇symlink应删除link不跟随target,目标API需测试。

文件与目录的属性

File.stat跟随symlink,lstat查看link本身;Stat提供type、size、mode、uid/gid和times。Predicate file?directory?symlink?方便,但都是snapshot。不能当长期授权证据。

Permission check如readable?也可能与实际open race,且ACL/container privileges复杂;直接open并处理error。Size用于budget初筛但file可增长,读取仍需hard cap。

文件名的操作

File.join按platform separator构造,basenamedirnameextname拆name,expand_path解析relative path。不使用字符串拼"root/" + input

expand_path消除./..但不等于解析所有symlink;realpath要求存在并解析links,仍有之后race。判断path在root内不能只用prefix(/safe2前缀问题),应比较path components/Pathname ancestry,并针对case-insensitive platform测试。

Extension不是可信file type,basename可能为空/特殊,Windows reserved names和NUL等由platform限制。Upload保存使用server-generated name,原name仅作metadata/display。

与文件操作相关的库:find、tempfile、fileutils

find库

Find.find递归yield paths,Find.prune跳过subtree。防cycle和资源耗尽。

require "find"
 
Find.find(root) do |path|
  if File.directory?(path) && File.basename(path) == ".git"
    Find.prune
  else
    puts path
  end
end

Predicate仍有race;不要跟随untrusted symlink。输出排序若要求稳定,需收集(受size上限)或分层sort;Find order不是portable promise。

tempfile库

Tempfile以unique restrictive file减少name collision,block/create形式自动cleanup更可靠。。

Atomic replace的temp应位于destination同directory/filesystem,写完flush/fsync、chmod/chown policy、rename,并必要时sync directory。Tempfile path不应泄露敏感data,异常后ensure cleanup。

fileutils库

FileUtils提供cp/mv/mkdir_p/rm_r等high-level operations,但不会替你选择trust root、conflict、rollback和durability。。

正式节点与章专属证据

  • :第 1 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 2 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 3 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 4 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 5 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 6 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 7 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。
  • :第 8 个正式节点要能回到“路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。”,并说明故障发生前后的第一处差异。

对象与状态模型

从输入、接收者到可观察证据

怎样让路径、目录遍历、临时文件和复制删除操作保持原子与可回退?

输入与接收者

固定变更、复制与删除文件所需的原始值、Ruby 版本和调用入口。

状态变化

在执行前记录接收者身份,并声明目录内容的读取的允许状态。

观察证据

保存第18章 File类与Dir类的初值、参数、编码或资源位置。

正式节点:变更、复制与删除文件、目录内容的读取、目录的创建与删除、文件与目录的属性、文件名的操作、find库、tempfile库、fileutils库

控制与消息轨迹

在相同初值下定位首个分岔

  1. 01固定变更、复制与删除文件的输入和接收者
  2. 02执行目录内容的读取并记录状态
  3. 03观察目录的创建与删除的返回或副作用
  4. 04用fileutils库核对不变量并复位

运行不变量:路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。

边界故障探针

一次只破坏一个前提

基线满足:路径归属、符号链接策略、权限与替换顺序在每次文件变更前明确。

本章回顾:Path是变化的namespace,不是稳定capability

  1. Rename/copy/delete有不同atomicity、metadata、filesystem和partial failure;atomic replace使用同目录temp流程。
  2. Directory enumeration不保证order且entries会变化;创建/删除固定root、existing type、mode和recursion。
  3. Stat/lstat是否跟随symlink不同,metadata只是snapshot,operation仍处理实际error。
  4. Path helpers替代字符串拼接,但canonicalization不自动解决symlink race与platform差异。
  5. Find有traversal budget,Tempfile有lifecycle,FileUtils只封装mechanics不承担security policy。

练习与答案

练习

  1. 问题 1:建立正常轨迹。 回答“怎样让路径、目录遍历、临时文件和复制删除操作保持原子与可回退?”,并写出四步执行记录。
  1. 问题 2:注入单一故障。 只制造“先删除目标再写新文件,中途失败后同时失去旧版本和新版本”,应从哪里开始定位?
  1. 问题 3:覆盖正式节点。 用一个证据包串联变更、复制与删除文件、目录内容的读取、目录的创建与删除、文件与目录的属性、文件名的操作、find库、tempfile库、fileutils库,说明为什么结论可由另一位读者独立复核。

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

变更、复制与删除文件

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

目录内容的读取

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

目录的创建与删除

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

文件与目录的属性

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

文件名的操作

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

find库

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

tempfile库

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

fileutils库

“第18章 File类与Dir类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

资料与写作方式声明

本章以Ruby 基础教程第 5 版作者支持资料合法公开试读核定可见范围,并以目录限定未公开部分,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…