问题
你需要写一个涉及到文件查找操作的脚本,比如对日志归档文件的重命名工具,你不想在 Python 脚本中调用 shell,或者你要实现一些 shell 不能做的功能。
解决方案
查找文件,可使用 os.walk() 函数,传一个顶级目录名给它。下面是一个例子,查找特定的文件名并答应所有符合条件的文件全路径:
import os
def findfile(start, name):
for relpath, dirs, files in os.walk(start):
if name in files:
full_path = os.path.join(start, relpath, name)
print(os.path.normpath(os.path.abspath(full_path)))
if __name__ == '__main__':
findfile(sys.argv[1], sys.argv[2])
保存脚本为文件 findfile.py,然后在命令行中执行它。指定初始查找目录以及名字作为位置参数,如下:
讨论
os.walk() 方法为我们遍历目录树,每次进入一个目录,它会返回一个三元组,包含相对于查找目录的相对路径,一个该目录下的目录名列表,以及那个目录下面的文件名列表。
对于每个元组,只需检测一下目标文件名是否在文件列表中。如果是就使用 os.path.join() 合并路径。为了避免奇怪的路径名比如 ././foo//bar ,使用了另外两个函数来修正结果。第一个是 os.path.abspath() , 它接受一个路径,可能是相对路径,最后返回绝对路径。第二个是 os.path.normpath() ,用来返回正常路径,可以解决双斜杆、对目录的多重引用的问题等。
尽管这个脚本相对于 UNIX 平台上面的很多查找来讲要简单很多,它还有跨平台的优势。并且,还能很轻松的加入其他的功能。我们再演示一个例子,下面的函数打印所有最近被修改过的文件:
import os
import time
def modified_within(top, seconds):
now = time.time()
for path, dirs, files in os.walk(top):
for name in files:
fullpath = os.path.join(path, name)
if os.path.exists(fullpath):
mtime = os.path.getmtime(fullpath)
if mtime > (now - seconds):
print(fullpath)
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print('Usage: {} dir seconds'.format(sys.argv[0]))
raise SystemExit(1)
modified_within(sys.argv[1], float(sys.argv[2]))
在此函数的基础之上,使用 os,os.path,glob 等类似模块,你就能实现更加复杂的操作了。可参考 5.11 小节和 5.13 小节等相关章节。读取配置文件
问题
怎样读取普通.ini 格式的配置文件?
解决方案
configparser 模块能被用来读取配置文件。例如,假设你有如下的配置文件:
; Sample configuration file
[installation]
library=%(prefix)s/lib
include=%(prefix)s/include
bin=%(prefix)s/bin
prefix=/usr/local
# Setting related to debug configuration
[debug]
log_errors=true
show_warnings=False
[server]
port: 8080
nworkers: 32
pid-file=/tmp/spam.pid
root=/www/root
signature:
=================================
Brought to you by the Python Cookbook
=================================
下面是一个读取和提取其中值的例子:
>>">>>> cfg = ConfigParser()
>>> cfg.read('config.ini')
['config.ini']
>>> cfg.sections()
['installation', 'debug', 'server']
>>> cfg.get('installation','library')
'/usr/local/lib'
>>> cfg.getboolean('debug','log_errors')
True
>>> cfg.getint('server','port')
8080
>>> cfg.getint('server','nworkers')
32
>>> print(cfg.get('server','signature'))
\=================================
Brought to you by the Python Cookbook
\=================================
>>>
如果有需要,你还能修改配置并使用 cfg.write() 方法将其写回到文件中。例如:
>>">>>> cfg.set('debug','log_errors','False')
>>> import sys
>>> cfg.write(sys.stdout)
library = %(prefix)s/lib
include = %(prefix)s/include
bin = %(prefix)s/bin
prefix = /usr/local
[debug]
log_errors = False
show_warnings = False
[server]
port = 9000
nworkers = 32
pid-file = /tmp/spam.pid
root = /www/root
signature =
=================================
Brought to you by the Python Cookbook
=================================
>>>
讨论
配置文件作为一种可读性很好的格式,非常适用于存储程序中的配置数据。在每个配置文件中,配置数据会被分组(比如例子中的“installation”、“debug”和“server”)。
每个分组在其中指定对应的各个变量值。
对于可实现同样功能的配置文件和 Python 源文件是有很大的不同的。首先,配置文件的语法要更自由些,下面的赋值语句是等效的:
prefix: /usr/local
配置文件中的名字是不区分大小写的。例如:
>>">'/usr/local'
>>> cfg.get('installation','prefix')
'/usr/local'
>>>
在解析值的时候,getboolean() 方法查找任何可行的值。例如下面都是等价的:
log_errors = TRUE
log_errors = Yes
log_errors = 1
或许配置文件和 Python 代码最大的不同在于,它并不是从上而下的顺序执行。文件是安装一个整体被读取的。如果碰到了变量替换,它实际上已经被替换完成了。例如,在下面这个配置中,prefix 变量在使用它的变量之前或之后定义都是可以的:
library=%(prefix)s/lib
include=%(prefix)s/include
bin=%(prefix)s/bin
prefix=/usr/local
ConfigParser 有个容易被忽视的特性是它能一次读取多个配置文件然后合并成一个配置。例如,假设一个用户像下面这样构造了他们的配置文件:
[installation]
prefix=/Users/beazley/test
[debug]
log_errors=False
读取这个文件,它就能跟之前的配置合并起来。如:
>>">>>> cfg.get('installation', 'prefix')
'/usr/local'
>>> # Merge in user-specific configuration
>>> import os
>>> cfg.read(os.path.expanduser('~/.config.ini'))
['/Users/beazley/.config.ini']
>>> cfg.get('installation', 'prefix')
'/Users/beazley/test'
>>> cfg.get('installation', 'library')
'/Users/beazley/test/lib'
>>> cfg.getboolean('debug', 'log_errors')
False
>>>
仔细观察下 prefix 变量是怎样覆盖其他相关变量的,比如 library 的设定值。产生这种结果的原因是变量的改写采取的是后发制人策略,以最后一个为准。你可以像下面这样做试验:
>>">'/Users/beazley/test/lib'
>>> cfg.set('installation','prefix','/tmp/dir')
>>> cfg.get('installation','library')
'/tmp/dir/lib'
>>>
最后还有很重要一点要注意的是 Python 并不能支持.ini 文件在其他程序(比如windows 应用程序)中的所有特性。确保你已经参阅了 configparser 文档中的语法详情以及支持特性。