Python中递归控制台工具的最佳实践
对于一个处理目录树中选定文件的命令行工具,最好的实践是什么(接口和实现)?
我想到一个例子,但我在寻找一个“最佳实践”:
flipcase foo.txt foo2.txt
可以处理 foo.txt 并将结果保存为 foo2.txt。
flipcase -rv *.txt
可以处理当前目录中的所有文本文件。
-r
或 --recursive
会包括所有子目录。
-v
在处理时会打印一些信息到标准输出。
我看到这个例子有一个问题,就是 *.txt
这个参数有时会被 shell 扩展(在 Unix 和 Vista 上),所以在遍历子目录时我不能使用这个模式。
我猜原因是,在 Unix 上这样的工具通常会和 find
命令结合使用,但在 Windows 上似乎不太常见。这也让最后打印总结变得困难。
需求:
- 必须在 Unix、Windows XP、Windows 7 和 Mac 上运行
- 应该遵循这些平台上的常见约定。(是的,我知道。但我在寻找一个合理的折中方案。例如,在 Windows 上使用
-
而不是/
是可以的。) - 不应该依赖于单独的 find 命令,就像 grep 那样。
- 必须支持单个文件、文件模式和目录层次中的模式。
- 应该使用标准的 Python 库构建,例如
OptionParser
和os.walk
。 - 可以处理多个模式,例如
*.txt,*.html
。
关于设计决策的其他问题:
- 这个工具应该返回什么(状态码)?
- 这个工具应该处理哪些控制键,方式是什么?
- 应该支持标准输入而不是单个文件吗?可配置还是自动检测?
- 应该支持输出重定向吗?可配置还是自动检测?在这种情况下,如何处理调试输出?
- 模式应该是通配符语法,还是正则表达式?
- 是否有支持递归的通用模式语法?也许
recursive:*.txt
,在这种情况下就不需要-r
选项了。 - 创建修改文件的备份的最佳实践是什么?使用
-b
选项,还是默认有备份并添加--no-backup
选项? - 对于单个文件,应该能够指定目标文件名。怎么做?
- 应该打印哪些状态信息,如何配置?默认应该详细打印,还是允许使用
-q
来安静模式?或者总是打印一点点,允许使用-v
(或-vv
)来增强输出,或者使用-q
完全静音?
我并不期待得到一个单一的正确答案,但希望能得到一些想法和指向好的示例项目的建议。
4 个回答
命令行工具处理目录树中选定文件的最佳实践(接口和实现)是什么?
我觉得没有一个统一的标准或者“最佳实践”来实现命令行工具。不过,通过查看和尝试一些做得很好的工具,比如GNU核心工具集,你会获得很多有用的见解。
另外,我觉得你可能也在寻找这样的内容:http://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html
了解和尝试Unix的做法,实际上可以帮助你解决很多关于设计决策的问题。
我看到这个例子有一个问题,就是*.txt这个参数有时候会被shell(Unix和Vista)自动扩展,所以在遍历子目录时我不能使用这个模式。
在Unix中,*
会被自动扩展。我不太确定Windows的情况,但如果我没记错的话,*
不会被扩展,所以你可以直接使用glob.glob(sys.argv[1])
。对于Unix来说,一个解决办法是对通配符进行转义,但应该还有更好的方法。
关于你提到的“通配符匹配”部分,实际上你提到的列表中,唯一不支持的就是Windows。UNIX系统的处理方式,以及一种不错的方法,就是让命令行自己来处理通配符匹配。你只需要得到一个文件列表。我不知道有什么UNIX工具会自己处理通配符匹配(在这种基本情况下)。我建议你也不要自己去做,而是依赖命令行。
在Windows上,你可以建议大家使用Cygwin这样的命令行工具。当然,Windows用户通常不太喜欢使用命令行,所以如果你做一个图形界面(GUI),他们会更开心。
这部分没有涉及到你的-r
选项。不过这就变得复杂了。你想给用户提供一个功能,让他们可以指定“所有子目录中扩展名为.txt的文件”吗?要注意,像ZSH这样的现代命令行工具可以处理递归的通配符匹配,比如:
rm **/*.tmp
而且,正如你所说的,你总是可以使用find
命令来替代。所以在这里的建议确实需要考虑到你工具的具体情况。rsync
实现了自己的-r
选项是有好处的,但一个假设的flipcase
可能就不需要了。
根据我的经验,最好的起步方式是创建一个遵循基本Unix原则的工具——也就是说,它应该从标准输入读取数据,然后写入标准输出。这样,大家就可以灵活地使用你的工具:
flipcase input.txt > output.txt
othercommand | flipcase > output.txt
flipcase | othercommand > ouput.txt
flipcase input1.txt input2.txt > output.txt
下一个功能可能是就地编辑:
# Modify input files directly.
flipcase -i input.txt
# Create backup copies before modifying originals.
flipcase -i --backup-suffix '_BAK' input.txt
flipcase -i --backup-prefix 'BAK_' input.txt
# Regex for power users.
flipcase -i --backup-regex 's/foo/bar/' input.txt
在详细模式下,这个工具不应该写入标准输出,因为这会和上面提到的基本原则冲突。它应该写入标准错误或者用户自定义的日志文件。
flipcase -v input.txt > output.txt
flipcase -v log.txt input.txt > output.txt
接下来,你可以添加递归功能。这里的方向不太明确,但我可以给你一些想法。在典型的递归情况下,程序的参数可能是目录,用户需要提供额外的选项来定义不同的过滤行为(也就是说,处理哪些类型的文件)。
flipcase -r -i --backup-suffix '_BAK' --filter-glob '*.txt' dir1 dir2
flipcase -r -i --backup-suffix '_BAK' --filter-glob '*.txt' --filter-glob 'log*.dat' dir
flipcase -r -i --backup-suffix '_BAK' --filter-regex 'log\w+\.(txt|log)$' dir1 dir2
# Don't do in-place editing. Instead create new files within the structure.
flipcase -r --newname-suffix '_NEW' --filter-glob '*.txt' dir1 dir2
flipcase -r --newname-regex 's/\.txt$/_new.txt/' --filter-glob '*.txt' dir1 dir2
# Create the backups or the new files in a parallel directory
# structure rather than within the original structure.
flipcase -r -i --backup-tree 'backup_dir' --filter-glob '*.txt' dir1 dir2
flipcase -r -i --new-tree 'newfiles_dir' --filter-glob '*.txt' dir1 dir2