Skip to content

OpenClaw 文件操作完全指南

文件操作是 AI 助理最基础也是最核心的能力之一。OpenClaw 提供 readwriteedit 三个工具,支持读取、创建、修改文件。本文详细介绍如何高效使用这些工具进行文件管理。

一、工具概述

read - 读取文件

支持读取文本文件和图片:

  • 文本文件:自动识别编码
  • 图片文件:jpg, png, gif, webp
  • 大文件:支持分页读取(offset/limit)
  • 自动截断:2000 行或 50KB
javascript
// 读取文本文件
read(path="/home/user/document.txt")

// 读取图片(自动作为附件)
read(path="/home/user/screenshot.png")

// 分页读取大文件
read(path="/var/log/large.log", offset=1000, limit=500)

write - 写入文件

创建或覆盖文件:

  • 自动创建目录(如不存在)
  • 支持文本和二进制内容
  • 覆盖已有文件(谨慎使用)
javascript
// 创建文本文件
write(path="/home/user/notes.md", content="# 笔记内容")

// 自动创建目录
write(path="/home/user/deep/path/file.txt", content="内容")
// 如 deep/path 不存在,会自动创建

edit - 精确编辑

替换文件中的精确文本:

  • 必须完全匹配(包括空白字符)
  • 适合小范围修改
  • 不支持正则表达式
javascript
// 替换文本
edit(
  path="/home/user/config.js",
  oldText="const API_URL = 'http://dev.example.com'",
  newText="const API_URL = 'https://api.example.com'"
)

二、实战案例 1:批量文件整理

场景说明

下载目录积累了大量文件,需要按类型分类整理到不同文件夹。

完整实现

javascript
// 1. 获取下载目录文件列表
files_list = exec(
  command="ls -1 ~/Downloads/*.* 2>/dev/null",
  capture_output=True
).stdout.strip().split('\n')

// 2. 定义分类规则
const categories = {
  '文档': ['.md', '.txt', '.pdf', '.doc', '.docx'],
  '图片': ['.jpg', '.jpeg', '.png', '.gif', '.webp'],
  '代码': ['.js', '.py', '.java', '.cpp', '.html', '.css'],
  '数据': ['.json', '.csv', '.xml', '.yaml', '.yml'],
  '压缩包': ['.zip', '.tar', '.gz', '.rar']
}

// 3. 分类函数
function categorize_file(filename) {
  const ext = '.' + filename.split('.').pop().toLowerCase()
  for (const [category, extensions] of Object.entries(categories)) {
    if (extensions.includes(ext)) {
      return category
    }
  }
  return '其他'
}

// 4. 执行整理
const report = {
  '文档': [],
  '图片': [],
  '代码': [],
  '数据': [],
  '压缩包': [],
  '其他': []
}

for (const filepath of files_list) {
  if (!filepath) continue
  
  const filename = filepath.split('/').pop()
  const category = categorize_file(filename)
  
  // 创建目标目录
  const target_dir = `~/Downloads/organized/${category}`
  exec(command=`mkdir -p ${target_dir}`)
  
  // 移动文件
  exec(command=`mv "${filepath}" "${target_dir}/"`)
  
  report[category].push(filename)
}

// 5. 生成整理报告
const report_content = `
# 文件整理报告 - ${new Date().toLocaleDateString('zh-CN')}

## 整理统计

| 类别 | 文件数 | 示例 |
|------|--------|------|
${Object.entries(report).map(([cat, files]) => 
`| ${cat} | ${files.length} | ${files.slice(0, 3).join(', ')}${files.length > 3 ? '...' : ''} |`
).join('\n')}

## 总计
- 整理文件数:${files_list.filter(f => f).length}
- 分类类别:${Object.keys(report).length}
- 整理位置:~/Downloads/organized/

## 目录结构
${exec(command='tree ~/Downloads/organized -L 2', capture_output=True).stdout}
`

write(
  path="~/Downloads/organized/整理报告.md",
  content=report_content
)

console.log("✅ 文件整理完成!")

整理报告示例

# 文件整理报告 - 2026/3/19

## 整理统计

| 类别 | 文件数 | 示例 |
|------|--------|------|
| 文档 | 15 | 笔记.md, 报告.pdf, 说明.txt... |
| 图片 | 23 | screenshot.png, photo.jpg... |
| 代码 | 8 | app.js, utils.py... |
| 数据 | 12 | config.json, data.csv... |
| 压缩包 | 5 | backup.zip, archive.tar.gz |
| 其他 | 3 | unknown.bin... |

## 总计
- 整理文件数:66
- 分类类别:6
- 整理位置:~/Downloads/organized/

三、实战案例 2:配置文件版本管理

场景说明

管理多个环境的配置文件(开发、测试、生产),确保配置一致性和可追溯性。

完整实现

javascript
// 配置管理函数
class ConfigManager {
  constructor(basePath) {
    this.basePath = basePath
    this.backupDir = `${basePath}/backups`
  }
  
  // 备份当前配置
  async backup(configName) {
    const timestamp = Date.now()
    const backupPath = `${this.backupDir}/${configName}-${timestamp}.bak`
    
    // 确保备份目录存在
    exec(command=`mkdir -p ${this.backupDir}`)
    
    // 读取并备份
    const content = read(path=`${this.basePath}/${configName}`)
    write(path=backupPath, content=content)
    
    console.log(`✅ 已备份 ${configName} -> ${backupPath}`)
    return backupPath
  }
  
  // 更新配置
  async update(configName, updates) {
    // 先备份
    await this.backup(configName)
    
    // 读取当前配置
    let content = read(path=`${this.basePath}/${configName}`)
    
    // 应用更新
    for (const [oldVal, newVal] of Object.entries(updates)) {
      // 使用 edit 精确替换
      try {
        edit(
          path=`${this.basePath}/${configName}`,
          oldText=oldVal,
          newText=newVal
        )
        console.log(`✅ 更新:${oldVal.substring(0, 30)}...`)
      } catch (e) {
        console.log(`⚠️ 更新失败:${oldVal.substring(0, 30)}...`)
      }
    }
    
    // 记录变更日志
    this.logChange(configName, updates)
  }
  
  // 记录变更日志
  logChange(configName, updates) {
    const logEntry = `
## ${new Date().toISOString()}

**文件**: ${configName}

**变更**:
${Object.entries(updates).map(([oldVal, newVal]) => 
`- 从:\`${oldVal.substring(0, 50)}...\`
- 到:\`${newVal.substring(0, 50)}...\``
).join('\n')}

---
`
    
    const logPath = `${this.basePath}/CHANGELOG.md`
    
    // 读取现有日志或创建新日志
    let changelog = "# 配置变更日志\n\n"
    try {
      changelog = read(path=logPath)
    } catch (e) {
      // 文件不存在,创建新日志
    }
    
    // 追加新条目
    write(path=logPath, content=changelog + logEntry)
  }
  
  // 对比配置差异
  diff(configName, backupPath) {
    const current = read(path=`${this.basePath}/${configName}`)
    const backup = read(path=backupPath)
    
    const diffResult = exec(
      command=`diff -u <(echo "${backup}") <(echo "${current}")`,
      capture_output=True
    ).stdout
    
    return diffResult || "无差异"
  }
}

// 使用示例
const configMgr = new ConfigManager('/home/pao/projects/myapp/config')

// 更新生产环境配置
await configMgr.update('production.json', {
  '"api_url": "http://old-api.example.com"': '"api_url": "https://api.example.com"',
  '"debug": true': '"debug": false',
  '"log_level": "debug"': '"log_level": "warn"'
})

// 对比差异
const diff = configMgr.diff('production.json', '/home/pao/projects/myapp/config/backups/production.json-1710820800000.bak')
console.log(diff)

变更日志示例

# 配置变更日志

## 2026-03-19T07:30:00.000Z

**文件**: production.json

**变更**:
- 从:`"api_url": "http://old-api.example.com"...`
- 到:`"api_url": "https://api.example.com"...`
- 从:`"debug": true...`
- 到:`"debug": false...`
- 从:`"log_level": "debug"...`
- 到:`"log_level": "warn"...`

---

四、实战案例 3:日志文件分析

场景说明

分析应用日志,提取错误、警告信息,生成日报。

完整实现

javascript
// 日志分析函数
async function analyze_logs(logPath, outputPath) {
  // 1. 读取日志文件(分页读取大文件)
  let logContent = ''
  let offset = 1
  const limit = 1000
  
  while (true) {
    const chunk = read(path=logPath, offset=offset, limit=limit)
    if (!chunk || chunk.trim() === '') break
    
    logContent += chunk + '\n'
    offset += limit
    
    // 限制总大小,避免内存溢出
    if (logContent.length > 10 * 1024 * 1024) {  // 10MB
      console.log("⚠️ 日志过大,只分析前 10MB")
      break
    }
  }
  
  // 2. 提取错误和警告
  const errors = logContent.match(/\[ERROR\].*/g) || []
  const warnings = logContent.match(/\[WARN\].*/g) || []
  const infos = logContent.match(/\[INFO\].*/g) || []
  
  // 3. 统计错误类型
  const errorTypes = {}
  errors.forEach(err => {
    const type = err.match(/\[ERROR\]\s*\[(\w+)\]/)?.[1] || 'Unknown'
    errorTypes[type] = (errorTypes[type] || 0) + 1
  })
  
  // 4. 提取时间分布
  const hourlyDistribution = {}
  errors.forEach(err => {
    const hour = err.match(/(\d{2}:\d{2}:\d{2})/)?.[1]?.substring(0, 2) || '00'
    hourlyDistribution[hour] = (hourlyDistribution[hour] || 0) + 1
  })
  
  // 5. 生成分析报告
  const report = `
# 日志分析报告 - ${new Date().toLocaleDateString('zh-CN')}

## 概览

| 级别 | 数量 | 占比 |
|------|------|------|
| ERROR | ${errors.length} | ${(errors.length / (errors.length + warnings.length + infos.length) * 100).toFixed(2)}% |
| WARN | ${warnings.length} | ${(warnings.length / (errors.length + warnings.length + infos.length) * 100).toFixed(2)}% |
| INFO | ${infos.length} | ${(infos.length / (errors.length + warnings.length + infos.length) * 100).toFixed(2)}% |

## 错误类型分布

${Object.entries(errorTypes).map(([type, count]) => 
`- **${type}**: ${count} 次`
).join('\n')}

## 时间分布(按小时)

${Object.entries(hourlyDistribution).sort((a, b) => a[0] - b[0]).map(([hour, count]) => 
`${hour}:00 - ${parseInt(hour)+1}:00: ${count} 次错误` + 
' ' + '█'.repeat(Math.min(count, 50))
).join('\n')}

## 最新错误(Top 10)

${errors.slice(-10).reverse().map(err => `
\`\`\`
${err}
\`\`\`
`).join('\n')}

## 建议行动

${errors.length > 100 ? `
1. **立即处理**:错误数量过多(${errors.length}),建议立即排查
2. **重点关注**:${Object.entries(errorTypes).sort((a,b) => b[1]-a[1])[0]?.[0]} 错误最多
` : `
1. 错误数量可控,持续关注
2. 定期清理日志文件
`}
`
  
  // 6. 保存报告
  write(path=outputPath, content=report)
  
  // 7. 如有严重错误,发送告警
  if (errors.length > 100) {
    message(
      action="send",
      channel="feishu",
      target="chat_dev_team",
      message=`⚠️ 日志告警:发现 ${errors.length} 个错误,请查看报告:${outputPath}`
    )
  }
  
  return { errors: errors.length, warnings: warnings.length, infos: infos.length }
}

// 使用示例
const stats = await analyze_logs(
  '/var/log/myapp/app.log',
  '/var/log/myapp/reports/daily-report.md'
)
console.log(`分析完成:${stats.errors} 错误,${stats.warnings} 警告,${stats.infos} 信息`)

分析报告示例

# 日志分析报告 - 2026/3/19

## 概览

| 级别 | 数量 | 占比 |
|------|------|------|
| ERROR | 156 | 5.2% |
| WARN | 423 | 14.1% |
| INFO | 2421 | 80.7% |

## 错误类型分布

- **Database**: 89 次
- **Network**: 45 次
- **Auth**: 15 次
- **Unknown**: 7 次

## 时间分布(按小时)

02:00 - 03:00: 45 次错误 ████████████████████████████████████████████████
03:00 - 04:00: 38 次错误 ██████████████████████████████████████
14:00 - 15:00: 28 次错误 ████████████████████████████

## 最新错误(Top 10)

[ERROR] [Database] 2026-03-19 14:35:22 - Connection timeout


## 建议行动

1. **立即处理**:错误数量过多(156),建议立即排查
2. **重点关注**:Database 错误最多

五、高级技巧

1. 安全写入(避免数据丢失)

javascript
// 先写入临时文件,再原子替换
async function safeWrite(path, content) {
  const tempPath = `${path}.tmp.${Date.now()}`
  
  try {
    // 1. 写入临时文件
    write(path=tempPath, content=content)
    
    // 2. 验证临时文件
    const written = read(path=tempPath)
    if (written !== content) {
      throw new Error("写入验证失败")
    }
    
    // 3. 备份原文件(如存在)
    try {
      const original = read(path=path)
      write(path=`${path}.bak`, content=original)
    } catch (e) {
      // 原文件不存在,跳过备份
    }
    
    // 4. 原子替换
    exec(command=`mv "${tempPath}" "${path}"`)
    
    console.log(`✅ 安全写入完成:${path}`)
  } catch (e) {
    console.log(`❌ 写入失败:${e.message}`)
    // 清理临时文件
    exec(command=`rm -f "${tempPath}"`)
    throw e
  }
}

2. 批量读取多个文件

javascript
// 并行读取多个文件
async function readMultipleFiles(paths) {
  const results = {}
  
  for (const path of paths) {
    try {
      results[path] = {
        success: true,
        content: read(path=path)
      }
    } catch (e) {
      results[path] = {
        success: false,
        error: e.message
      }
    }
  }
  
  return results
}

// 使用示例
const configs = await readMultipleFiles([
  '/etc/app/config.json',
  '/etc/app/database.json',
  '/etc/app/cache.json'
])

3. 文件内容搜索

javascript
// 在文件中搜索特定内容
function searchInFile(path, pattern, contextLines=2) {
  const content = read(path=path)
  const lines = content.split('\n')
  const results = []
  
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].includes(pattern)) {
      // 提取上下文
      const start = Math.max(0, i - contextLines)
      const end = Math.min(lines.length, i + contextLines + 1)
      
      results.push({
        line: i + 1,
        content: lines[i],
        context: lines.slice(start, end).join('\n')
      })
    }
  }
  
  return results
}

// 使用示例
const matches = searchInFile('/var/log/app.log', 'ERROR', 3)
console.log(`找到 ${matches.length} 处匹配`)

4. 文件权限管理

javascript
// 检查和修复文件权限
function checkPermissions(path, expectedMode='644') {
  const stat = exec(
    command=`stat -c "%a %n" "${path}"`,
    capture_output=True
  ).stdout.trim()
  
  const [actualMode] = stat.split(' ')
  
  if (actualMode !== expectedMode) {
    console.log(`⚠️ 权限不符:${path}`)
    console.log(`   期望:${expectedMode}`)
    console.log(`   实际:${actualMode}`)
    
    // 修复权限
    exec(command=`chmod ${expectedMode} "${path}"`)
    console.log(`✅ 已修复权限`)
  } else {
    console.log(`✅ 权限正确:${path}`)
  }
}

六、最佳实践

1. 读取大文件

javascript
// ✅ 好的做法:分页读取
const chunks = []
let offset = 1
while (true) {
  const chunk = read(path='large.log', offset=offset, limit=1000)
  if (!chunk) break
  chunks.push(chunk)
  offset += 1000
}

// ❌ 差的做法:一次性读取整个大文件
const content = read(path='large.log')  // 可能内存溢出

2. 写入前验证

javascript
// ✅ 好的做法:验证路径和内容
if (!path.startsWith('/safe/base/dir/')) {
  throw new Error("不安全的路径")
}
if (content.length > 10 * 1024 * 1024) {
  throw new Error("内容过大")
}
write(path=path, content=content)

// ❌ 差的做法:直接写入
write(path=userInputPath, content=userInputContent)  // 安全风险

3. 错误处理

javascript
// ✅ 好的做法:完整的错误处理
try {
  const content = read(path='/important/file.txt')
  // 处理内容...
} catch (e) {
  if (e.message.includes('ENOENT')) {
    console.log("文件不存在")
  } else if (e.message.includes('EACCES')) {
    console.log("权限不足")
  } else {
    console.log(`读取失败:${e.message}`)
  }
}

// ❌ 差的做法:忽略错误
const content = read(path='/important/file.txt')  // 可能失败

七、常见问题

Q: read 工具支持哪些文件格式?

A:

  • 文本文件:所有文本格式(自动检测编码)
  • 图片:jpg, png, gif, webp(作为附件)
  • 不支持:PDF、视频、二进制文件(需用其他工具)

Q: 如何追加内容到文件?

A: 先读取,拼接后再写入:

javascript
const original = read(path='file.txt')
write(path='file.txt', content=original + '\n新内容')

Q: edit 工具支持正则表达式吗?

A: 不支持。edit 要求精确匹配。如需正则替换,使用 exec 调用 sed

javascript
exec(command=`sed -i 's/old/new/g' file.txt`)

Q: 如何安全地修改重要配置文件?

A:

  1. 先备份原文件
  2. 使用 safeWrite 函数(见高级技巧)
  3. 验证修改后的内容
  4. 保留备份一段时间

八、总结

文件操作是 AI 助理的基础能力:

工具用途注意事项
read读取文件大文件分页读取
write创建/覆盖注意目录是否存在
edit精确修改必须完全匹配

掌握这些技巧,你可以:

  • 批量整理文件
  • 管理配置文件
  • 分析日志文件
  • 安全地读写数据

记住:文件操作无小事,修改前必备份!


相关资源:

Released under the MIT License.