16.1 操作系统接口和命令行
通常,shell会用一些构成OS API的信息来启动应用程序。
- shell会为每个应用程序提供环境变量的集合。在Python中,这些集合可以通过os.environ访问。
- shell会准备3种标准文件。在Python中,这3种文件对应的是sys.stdin、sys.stdout和sys.stderr。还有一些其他的模块,例如fileinput可以访问sys.stdin。
- shell会将命令行解析为一些单词,命令行的一部分可以通过sys.argv来访问。Python会提供原始命令行中的一些信息,我们会在下面的部分中详细介绍这点。对于POSIX操作系统,shell可能会替换shell的环境变量并展开通配符文件名。在Windows中,简单的cmd.exe shell不会为我们展开文件名。
- 操作系统也维护了一些上下文设置,例如当前工作目录、用户ID和用户组。这些可以通过os模块访问。它们没有作为参数在命令行中提供。
OS希望应用程序结束时能够提供一个数字状态码。如果我们想访问一个特定的数字状态码,可以在应用程序中使用sys.exit()。如果我们的程序正常结束,Python会返回0。
shell的操作是操作系统API的重要部分。对于一行给定的输入,基于(更复杂的)引用规则和替换选项,shell会执行一系列的替换操作。然后,它会将生成的结果转换为用空格分隔的一行单词。第1个单词一定是内置的shell命令(例如cd或者set)或者某个文件的名称。shell会在它定义的PATH中搜索这个文件。
命令中的第1个单词指定的文件必须拥有execute (x)权限。shell命令chmod +x somefile.py让一个文件成为可执行文件。如果文件名匹配但是该文件不是可执行的,就会得到一个OS Permission Denied错误。
一个可执行文件开头的一些字节包含一个幻数,shell会基于这个数字来决定执行这个文件的方式。一些幻数指出该文件是可执行二进制文件,shell可以启动一个子shell来执行它。其他的幻数,尤其是b'#!',指出该文件是脚本,需要使用解释器来执行。这种文件第1行的其他部分是解释器的名称。
我们经常用下面这行指令。
#!/usr/bin/env python3.3
如果一个Python文件拥有执行权限并且以这行作为首行,那么shell会运行env程序。env程序的参数(python 3.3)会初始化配置环境并且在Python 3.3程序中运行第1个参数所指定的Python文件。
事实上,通过可执行脚本从操作系统shell到Python的步骤类似下面这样。
1.shell解析ourapp.py -s someinput.csv。第1个单词是ourapp.py。这个文件在shell的PATH中并且拥有x可执行权限。shell打开文件找到#!字节。shell读取这行的剩余部分并找到一个新的命令:/usr/bin/env python3.3。
2.shell解析新的/usr/bin/env命令,这个文件是可执行的二进制文件。所以,shell启动这个程序。然后,这个程序启动python3.3。原来命令行中的单词序列作为操作系统API的一部分提供给Python。
3.Python将从这个取自原始命令行中的单词中提取第1个参数前的所有选项。Python会使用这些最开始的选项。第1个参数是要运行的文件名。这个文件名参数和所有其他的单词会分别保存在sys.argv中。
4.Python基于找到的选项执行正常的启动操作。由于我们使用了-s选项,因此Python可能会使用site模块设置查找路径——sys.path。如果我们使用-m选项,Python将用runpy模块启动我们的应用程序。这些脚本文件可能都会被编译成字节码。
5.我们的应用程序可以用sys.argv解析选项,用argparse模块解析参数。我们的应用程序可以使用os.environ中的环境变量。它也可以解析配置文件,更多关于这个主题的更多信息,可参见第13章“配置文件和持久化”。
如果没有提供文件名,Python解释器会从标准输入中读取。如果标准输入是命令行(在Linux的术语中被称为TTY),那么Python会进入Read-Execute-Print Loop (REPL)并且显示>>>提示符。当作为开发者时会经常使用这种模式,但是通常不会在最终的应用程序中使用这种模式。
另外一种可能是标准输入是一个重定向的文件,例如,python < some fileor some app | python。这两种输入都是正确的,但是可能让人难以理解。
参数和选项
为了能够运行程序,shell将命令行解析为一串单词。所有已经启动的程序都可以使用这串单词。通常,第1个单词是为了让shell可以理解命令。命令行中其余的单词被理解为选项和参数。
有若干用于处理选项和参数的准则,下面这些是基本的规则。
- 选项先出现。它们以-或—为前缀。有两种格式:-letter和—word。有两种选项:带参选项和无参选项。无参选项的例子是用-V或者— version显示版本。带参选项的一个参数是-m module,-m选项后面必须带有一个模块名。
- 不带参数的短格式(单一字母)选项可以组合在单个的-之后。方便起见,我们可以用-bqv作为-b - q -v选项的组合。
- 参数最后出现。它们没有前置的-或者—。有两种常用的参数。
- 对于位置参数,位置是有意义的。我们可能有两个这样的位置参数:一个输入文件名和一个输出文件名。这个顺序很重要是因为输出文件会被修改。当涉及覆盖文件的操作时,通过位置进行简单的区分需要非常小心,因为可能会引起混乱。
- 参数列表,这些参数在语义上是平等的。我们可能有一些代表输入文件名的参数。这种方式适合shell匹配文件名。当我们说process.py .html时,shell会将.html命令扩展为代表文件名的位置参数。(这在Windows中无法工作,所以必须使用glob模块。)
除了上面这些规则外,还有一些细节没有介绍。关于命令行选项的更多信息,可以参考http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1 chap12. html#tag 12 _ 02。Python的命令行包含超过12种选项可以用于控制Python中一些行为的细节。如果想知道这些选项是什么,可参见Python Setup and Usage文档。位置参数对于Python命令行而言是需要运行的脚本的名称,这是我们的应用程序中最顶层的文件。
