Files
X-Agents/account/admin/skills/file-manager/scripts/file_ops.py
2026-03-11 16:26:22 +08:00

357 lines
11 KiB
Python

#!/usr/bin/env python3
"""
文件操作脚本
用法:
python file_ops.py <command> [options]
命令:
list 列出目录内容
read 读取文件
write 写入文件
copy 复制文件
move 移动/重命名文件
delete 删除文件或目录
search 搜索文件
info 获取文件信息
"""
import argparse
import json
import os
import shutil
import sys
from datetime import datetime
from pathlib import Path
def format_size(size: int) -> str:
"""格式化文件大小"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size < 1024:
return f"{size:.2f} {unit}"
size /= 1024
return f"{size:.2f} PB"
def list_directory(path: str, recursive: bool = False, pattern: str = None) -> dict:
"""列出目录内容"""
p = Path(path)
if not p.exists():
return {"success": False, "error": f"Path not found: {path}"}
if not p.is_dir():
return {"success": False, "error": f"Not a directory: {path}"}
files = []
directories = []
if recursive:
items = p.rglob(pattern or "*")
else:
items = p.glob(pattern or "*")
for item in items:
if item.is_file():
files.append(str(item.relative_to(p)))
elif item.is_dir():
directories.append(str(item.relative_to(p)))
return {
"success": True,
"operation": "list",
"path": str(p.absolute()),
"data": {
"files": sorted(files),
"directories": sorted(directories),
"file_count": len(files),
"dir_count": len(directories),
}
}
def read_file(path: str, encoding: str = "utf-8") -> dict:
"""读取文件内容"""
p = Path(path)
if not p.exists():
return {"success": False, "error": f"File not found: {path}"}
if not p.is_file():
return {"success": False, "error": f"Not a file: {path}"}
try:
content = p.read_text(encoding=encoding)
return {
"success": True,
"operation": "read",
"path": str(p.absolute()),
"data": {
"content": content,
"size": len(content),
"lines": content.count('\n') + 1,
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def write_file(path: str, content: str, append: bool = False, encoding: str = "utf-8") -> dict:
"""写入文件"""
p = Path(path)
try:
# 确保目录存在
p.parent.mkdir(parents=True, exist_ok=True)
mode = "a" if append else "w"
with open(p, mode, encoding=encoding) as f:
f.write(content)
return {
"success": True,
"operation": "write",
"path": str(p.absolute()),
"data": {
"bytes_written": len(content.encode(encoding)),
"append": append,
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def copy_file(source: str, destination: str) -> dict:
"""复制文件或目录"""
src = Path(source)
dst = Path(destination)
if not src.exists():
return {"success": False, "error": f"Source not found: {source}"}
try:
if src.is_dir():
shutil.copytree(src, dst)
else:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
return {
"success": True,
"operation": "copy",
"data": {
"source": str(src.absolute()),
"destination": str(dst.absolute()),
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def move_file(source: str, destination: str) -> dict:
"""移动/重命名文件或目录"""
src = Path(source)
dst = Path(destination)
if not src.exists():
return {"success": False, "error": f"Source not found: {source}"}
try:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(src, dst)
return {
"success": True,
"operation": "move",
"data": {
"source": str(src.absolute()),
"destination": str(dst.absolute()),
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def delete_path(path: str, recursive: bool = False) -> dict:
"""删除文件或目录"""
p = Path(path)
if not p.exists():
return {"success": False, "error": f"Path not found: {path}"}
try:
if p.is_dir():
if recursive:
shutil.rmtree(p)
else:
p.rmdir() # 只能删除空目录
else:
p.unlink()
return {
"success": True,
"operation": "delete",
"data": {
"path": str(p.absolute()),
"was_directory": p.is_dir() if p.exists() else None,
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def search_files(directory: str, pattern: str, content: str = None) -> dict:
"""搜索文件"""
p = Path(directory)
if not p.exists():
return {"success": False, "error": f"Directory not found: {directory}"}
matches = []
for file_path in p.rglob(pattern):
if not file_path.is_file():
continue
match_info = {
"path": str(file_path.relative_to(p)),
"size": file_path.stat().st_size,
}
# 如果指定了内容搜索
if content:
try:
text = file_path.read_text(encoding="utf-8", errors="ignore")
if content.lower() in text.lower():
# 找到匹配的行
lines = []
for i, line in enumerate(text.split('\n'), 1):
if content.lower() in line.lower():
lines.append({"line": i, "content": line.strip()[:100]})
match_info["matches"] = lines[:10] # 最多10个匹配
matches.append(match_info)
except Exception:
pass
else:
matches.append(match_info)
return {
"success": True,
"operation": "search",
"data": {
"directory": str(p.absolute()),
"pattern": pattern,
"content_search": content,
"matches": matches,
"count": len(matches),
}
}
def get_info(path: str) -> dict:
"""获取文件/目录信息"""
p = Path(path)
if not p.exists():
return {"success": False, "error": f"Path not found: {path}"}
stat = p.stat()
info = {
"path": str(p.absolute()),
"name": p.name,
"is_file": p.is_file(),
"is_directory": p.is_dir(),
"size": stat.st_size,
"size_formatted": format_size(stat.st_size),
"created": datetime.fromtimestamp(stat.st_ctime).isoformat(),
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
"accessed": datetime.fromtimestamp(stat.st_atime).isoformat(),
}
if p.is_file():
info["extension"] = p.suffix
elif p.is_dir():
items = list(p.iterdir())
info["item_count"] = len(items)
info["file_count"] = sum(1 for i in items if i.is_file())
info["dir_count"] = sum(1 for i in items if i.is_dir())
return {
"success": True,
"operation": "info",
"data": info,
}
def main():
parser = argparse.ArgumentParser(description="File Manager")
subparsers = parser.add_subparsers(dest="command", required=True)
# list
list_parser = subparsers.add_parser("list", help="List directory contents")
list_parser.add_argument("path", help="Directory path")
list_parser.add_argument("--recursive", "-r", action="store_true")
list_parser.add_argument("--pattern", "-p", help="Glob pattern")
# read
read_parser = subparsers.add_parser("read", help="Read file")
read_parser.add_argument("path", help="File path")
read_parser.add_argument("--encoding", "-e", default="utf-8")
# write
write_parser = subparsers.add_parser("write", help="Write file")
write_parser.add_argument("path", help="File path")
write_parser.add_argument("--content", "-c", required=True)
write_parser.add_argument("--append", "-a", action="store_true")
write_parser.add_argument("--encoding", "-e", default="utf-8")
# copy
copy_parser = subparsers.add_parser("copy", help="Copy file/directory")
copy_parser.add_argument("source", help="Source path")
copy_parser.add_argument("destination", help="Destination path")
# move
move_parser = subparsers.add_parser("move", help="Move/rename file/directory")
move_parser.add_argument("source", help="Source path")
move_parser.add_argument("destination", help="Destination path")
# delete
delete_parser = subparsers.add_parser("delete", help="Delete file/directory")
delete_parser.add_argument("path", help="Path to delete")
delete_parser.add_argument("--recursive", "-r", action="store_true")
# search
search_parser = subparsers.add_parser("search", help="Search files")
search_parser.add_argument("directory", help="Directory to search")
search_parser.add_argument("--pattern", "-p", required=True, help="Glob pattern")
search_parser.add_argument("--content", "-c", help="Content to search for")
# info
info_parser = subparsers.add_parser("info", help="Get file/directory info")
info_parser.add_argument("path", help="Path")
args = parser.parse_args()
if args.command == "list":
result = list_directory(args.path, args.recursive, args.pattern)
elif args.command == "read":
result = read_file(args.path, args.encoding)
elif args.command == "write":
result = write_file(args.path, args.content, args.append, args.encoding)
elif args.command == "copy":
result = copy_file(args.source, args.destination)
elif args.command == "move":
result = move_file(args.source, args.destination)
elif args.command == "delete":
result = delete_path(args.path, args.recursive)
elif args.command == "search":
result = search_files(args.directory, args.pattern, args.content)
elif args.command == "info":
result = get_info(args.path)
else:
result = {"success": False, "error": f"Unknown command: {args.command}"}
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()