介绍Python OS模块
os
模块是两大核心系统模块中较大的那个。它包含了在C程序和shell脚本中经常用到的所有操作系统调用。它的调用设计目录、进程和shell变量等。准确地说,该模块提供了POSIX工具,操作系统调用的跨平台移植标准,以及不依赖平台的目录处理工具,如内嵌模块os.path
便携的脚本通常无需改动即可在其他平台上运行。某些平台,os还包括了专属该平台的额外工具。不过总的来说,只要技术上可行,os都能做到跨平台。
os模块中的工具
os模块中常用的一些工具:
任务 | 工具 |
---|---|
Shell变量 | os.enviros |
运行程序 | os.system , os.popen , os.execv , os.swawnv |
派生 | os.fork , os.pipe , os.waitpid , os.kill |
文件描述符,文件锁 | os.open , os.read , os.write |
文件处理 | os.remove , os.rename , os.mkfifo , os.mkdir , os.rmdir |
管理工具 | os.getcwd , os.chdir , os.chmod , os.getpid , os.listdir , os.access |
移植工具 | os.sep , os.pathsep , os.curdir , os.path.split , os.path.join |
路径名工具 | os.path.exists('path') , os.path.isdir('path') , os.path.getsize('path') |
如果交互地查看这个模块的属性, 你会得到一大串名称, 他们会因Python的版本和运行平台而异,所以你必须了解每一个名称的意义才能很好地利用他们:
1 | import os |
除了以上这些,内嵌的os.path
模块还能导出更多的工具,他们大多都涉及可移植地处理文档和目录名称:
1 | import os |
管理工具
和sys
模块一样,os模块也代用一套提供信息、帮助管理的工具:
1 | import os |
os.getpid()
给出调用函数的进程的ID(这是系统为当前运行程序定义的唯一标识符,可用于进程控制和唯一命名),os.fercwd
则返回当前的工作目录。当前的工作目录是你的脚本所打开的文档应当放置的位置,除非文件名里显式的包含了目录路径。
可移植的常量
os
模块同事导出了一组用于简化跨平台编程的名称,包括与具体平台相关的路径和目录分隔符、父目录和当前目录指示器,以及底层计算机所采用的换行符。
1 | import os |
<<<<<<< HEAD:Notes/笔记/Python编程/系统编程/系统工具/os模块.mdos.sep
是Python底层运行平台所采用的目录组分隔符号。他在windows下自动预设为“\” ,POSIX则是“/” ,某些Mac则是“:”。类似的,os.pathsep提供用于在目录列表中分隔目录的字符,POSIX使用“:”,DOS和Windows使用“;”。
os.sep
是Python底层运行平台所采用的目录组分隔符号。他在windows下自动预设为
“\“,POSIX计算机则是”/“, 某些Mac上则使用”:“。类似地,os.pathsep
提供用于在目录列表中分隔目录的字符,POSIX使用”:”,DOS和windows使用”;“。
当我们在脚本中拼装和分解这些系统相关字符串时,借助这些属性,可以充分实现脚本的可移植性。例如,虽然dirpath
在windows中是dir\dir,在Linux中是dir/dir ,但dir path.split(os.sep)
的调用可以准确无误地将与平台相关的目录名称分解为各个部分。在Windows中你通常可以在列出待打开的文件名时用斜杠代替反斜杠,但这些可移植的常量允许脚本在目录处理部分的代码不依赖平台。
另外请注意,os.linesep
在这里使用的是\r\n,这个符号转义码在Windows下代表回车加换行惯例,在使用Python处理文本时一般不会注意这一点。
常见os.path工具
内嵌的os.path
模块提供了一整套目录处理相关工具。举例来说,它提供的可移植函数可以用来检查文件类型(isdir
,isfile
等)、测试文件是否存在(exists
),以及通过文件名来获取文件的大小(getsize
):
1 | import os |
os.path.isdir
和os.path.isfile
调用可以告诉我们文件名是目录还是一个简单的文件。如果文件不存在,二者都会返回False(也就是说不存在即暗示否定的意思)。我们还能得到用于分割和合并目录路径字符串的函数,它们会自动应用Python所在底层平台的目录命名惯例:
1 | import os |
os.path.split
将文件名从它的目录路径中剥离出来,os.path.join
则将它们合并起来,这两个调用都是可移植的,都自动采用所在平台的路径命名惯例。为了简便起见dirname
和basename
则调用返回了split
返回结果的前两项,splitext
则剥离了文件的扩展名。微妙之处在于,使用字符串的split
和join
方法几乎可以起到os.sep
相同的作用,只是有细微区别:
1 | import os |
最后一个join
调用要求传入参数(因此用了*),但因为Windows的驱动器句法的关系不插入第一个斜杠;如果这种区别影响到程序,可以转而使用前面的str.join
方法。如果你的路径混用了
Unix和Windows分隔符,这时normpath
调用显得格外好用:
1 | import os |
这个模块还有abspath
调用,他能可移植地返回文件的完整目录路径名。它负责将当前目录添加为前缀以及处理..父目录句法等:
1 | import os |
在给出完整路径之前,文件名采用基于当前工作目录的相对路径,因此如果你想了解用户展示文件真正存储于哪个目录下,可以使用os.path.abspath
函数来帮忙。
在脚本里运行shell命令
os模块也是使得我们可以从Python脚本运行Shell命令。这个概念与流等密切相关,以下两个os函数使得脚本可以运行你在控制窗口输入的任意命令行:
os.system
在Python脚本中运行shell命令。
os.popen
运行shell命令并与其输入或输出流相连接。
另外,相对较新的subprocess
模块可以对生成的shell命令流进行精细控制,而且可以代替(会增加代码复杂性)。
运行shell命令
虽然Python的命令行脚本有时被误解为“shell工具”,由于os模块的system和popen调用使得Python脚本可以运行底层系统shell可理解的任意命令,物流他们是用Python还是其他语言编写的。例如下面这段代码,他在shell提示符后运行了刚才演示的那两条DOS shell命令:
1 | import os |
与shell命令进行通信
os.system
函数只是简单地执行一条shell命令,os.popen
还会连接到这个命令的标准输入\输出流;我们将得到一个类似文件的对象,他默认与命令的输出相连(向popen传入w模式标识符则与命令的输入流相连)。借助这个对象可以读取popen
所派生的命令的输出结果,从而在输入命令行之后拦截那些在正常情况下将出现在控制窗口中的文字:
1 | import os |
这里,我们先用常用的方法来抓取文件内容,然后将文件内容作为shell中type
命令的输出。通过阅读dir
命令的输出结果,我们可以获取目录中文件的列表,进而对这个列表进行迭代处理。
到目前为止,我们运行了一些基本dos命令。因为这些调用可以运行我们能够在shell提示符输入任何命令,所以它们也可以用来启动其他Python脚本:
1 | import os |
在这些例子里,发送到system和popen的命令行字符串都是硬编码的,然而并不是说Python程序不能在运行时用普通的字符串运算(+,%等)来构建这种字符串。因为可以以这种方式动态地来创建并运行命令,所以system
和popen
将Pyrhon脚本变成了用于启动和组织其他程序灵活而可移植的工具。
替代方法:subprocess模块
一般来说,subprocess
模块要求更多的代码,但对流的连接和使用提供跟完善的控制。当流的链接方式更为复杂时,显得格外有用。
例如,这个新模块的call
函数在运行简单的shell命令方面和os.system
差不多,(运行type
这种Windows下的shell命令是需要额外的协议,不过像“Python”这种普通的可执行程序是不需要的):
1 | import subprocess |
这里请注意,最后一行代码中的shell = True
。这个用法比较微妙,而且与平台相关。
- 在Windows下,需要将
shell = True
的参数传给call
等subprocess
工具和popen
工具,这样才能够运行shell内建命令。像type
之类的Windows命令要求装有额外协议,但像“Python”这种普通程序不需要。 - 在类Unix平台上,当
shell = False
(默认值)时,程序命令行直接由os.execvp
运行,如果参数是True,那么这个程序将转由shell运行,而且你还可以借助其他参数来指定shell。
除了模拟os.system
,我们可以用类似方式让这个模块模拟前面用到的os.popen
函数,在脚本中运行shell命令并获取其标准输出文本。
1 | import subprocess |
这里,我们将stdout
流与管道连接,然后用communicate
来运行命令,并接收它的标准输出流和错误输出流文本;运行完成后,命令的退出状态可作为属性来查看。或者,我们可以用其他接口直接读取命令的标准输出流,然后等待命令退出(并返回退出状态):
1 | import subprocess |
实际上,os.popen
函数和subprocess.Popen
对象之间存在直接的映射关系:
1 | from subprocess import Popen, PIPE |
subprocess
在这些简单的示例中显得多余。但我们需要更灵活地控制其他流时,它的作用就开始显现出来了。实际上,由于它允许我们以类似的方式处理命令的错误流和输出流,在Python3.X中subprocess
取代了原先的os.popen2
、os.popen3
和os.popen4
函数,他们都曾出现在Python2.X中,现在都只是subprocess
对象接口的应用实例。因为这个模块更高级的应用实例涉及到标准流,
shell的局限
system
和popen
的两大局限。首先,尽管两个函数本身具有非常好的可移植性,但其真正分可移植程度决定于所运行的命令。例如前面那个DOS的dir
和type
shell命令的示例只在windows下有效,如果要在类Unix系统下运行,那么必须改为ls
和cat
命令。
其次,像这样将Python文件作为程序运行与导入程序文件再调用其中的函数是截然不同的做法,而且一般来说前者要比后者慢得多。调用os.system
和os.popen
时,他们必须在你的操作系统中启动完全不同的独立程序,当程序文件作为模块导入时,Python解释器只是简单地在同一个进程里加载和运行文件的代码,借此生成模板对象。整个过程中不会派生出其他程序。
将系统作为独立的程序来构建也有许多好处,他们允许程序间往返传递信息,但在很多情况下,导入模块构建系统更加快捷并且直接。
如果打算用这些函数,还应当记住os.system
函数通常会阻塞(其实是暂停)它的调用者,直到所启动的命令行程序退出。在Linux和类Unix平台上,所启动的命令行一般可独立于调用者,并与之并行地运行,只需要在命令行代码末尾追加上shell后台运算符&
即可:
1 | import os |
在windows下,用dos的start
命令通常也能使命令并行启动:
1 | import os |
实际上,新近的Python版本中加入了os.startfile
函数,这个函数会打开一个文件,无论文件类型是什么,就像鼠标单击一样:
1 | import os |
os.popen
函数一般不会阻塞其调用者,但无论在Windows还是Linux下,如果管道对象在所启动的程序退出前关闭(如进行垃圾回收时),或是管道一次性完成读取(如read()
方法),那么仍然有可能阻塞调用者。Unix分os.fork/exec
调用和Windows的os.spawnv
调用有人可用来不受阻塞困扰地调用并行运行程序。
因为os
模块的system
和popen
调用,以及subprocess
模块也可归为程序启动、流量定向和进程间通信手段。
os模块到处的其他工具
工具名 | 功能介绍 |
---|---|
os.environ |
获取和设置shell环境变量 |
os.fork |
在类Unix系统下派生新的子进程 |
os.pipe |
负责程序间通信 |
os.execlp |
启动新程序 |
os.spawnv |
启动带有底层控制的新程序 |
os.open |
打开基于底层描述符的文件 |
os.mkdir |
创建新目录 |
os.mkfifo |
创建新的命名管道 |
os.stat |
获取文件底层信息 |
os.remove |
根据文件名删除文件 |
os.walk |
将函数或循环应用于整个目录树的各个部分 |
诸如此类还有很多,特别需要注意的一点,os
模块提供了一套文件处理调用,如open
、read
和write
,但所有这些涉及底层的文件访问,它们与用Python内建open
函数创建的stdio
文件对象截然不同。通常情况下,除了非常特殊的文件处理需求(比如排他性访问文件锁打开文件),你应当使用内建的open
函数,而不是os
模块,来处理所有文件。