执行程序时发生了什么
当你双击桌面上的终端程序图标时,就会打开一个载入shell的程序。 你键入的命令不会直接在内核执行,而是先和 shell 进行交互。
Command (eg. `ls -l')

Terminal Program (eg. `gnome-terminal')

Shell (eg Bash)

Kernel (eg. linux 2.6.24)
更多关于进程如何运行的信息:

当你通过 python 执行程序时候,你可以选择直接从内核执行或者通过 shell。 如果你选择直接执行,你就没办法和 bash 同样方式执行命令。 www.codesec.net

我们先看看怎么使用 shell 和那些好玩的特性功能, 然后再通过subprocess来实现同样的功能,

数据流
在 UNIX 和 Linux 下,有三个被称作流的 I/O 通道,它们通过文本终端 (比如用 gnome-terminal 运行 Bash)和其他应用程序(比如通过 Python 的subprocess)这类环境来连接程序。 这几个 I/O 通道分别称为标准输入,标准输出,和标准错误输出, 它们的文件描述符分别为 0,1,2。
句柄名称描述
0stdin标准输入
1stdout标准输出
2stderr标准错误输出
这里你能看到标准输入叫做stdin,标准输出称作stdout,标准错误输出叫做stderr。

流是这样工作的:从终端输出获取输入并通过标准输入发送到程序, 程序返回的正常输出从标准输出输出,错误则返回到环境上下文的标准错误输出。 维基百科有幅图将描述这个过程:

如果你想将流从一个程序重定向到另一个地方,请看下文分解。

使用 Shell
重定向标准输入和输出到文件
你可以在 Bash 中使用>操作符将一个程序的标准输出重定向到一个文件 (在其他 Shell 也许略有语法差异)。这里有个范例:
program1 > file1
program1执行后的输出结果从标准输出流写入file1,并将file1其中现有的内容所替换。如果你只是想追加内容,你可以使用>>操作符:
program1 >> file1
<操作符可以被用来从文件中读取数据并传输到程序的标准输入流:
program1 < file1
同样的,program1会被执行,但是此时file1取代了键盘, 成为了标准输入的数据源。

你可以组合 shell 操作符以完成更复杂的操作。 下面这个范例中,program1从file1获取数据并发送到标准输入。标准输出则从program1输出到file2。

program1 < file1 > file2
也许有时候你需要从一个程序获取输出并将其作为另一个程序的输入。 你可以通过一个临时文件来实现这个操作:
program1 > tempfile1
program2 < tempfile1
rm tempfile1
这种方法有点累赘,因此 shell 提供了方便的机制,称为管道
管道
管道允许一个程序的标准输出直接输入到另一个程序的标准输入流中, 而无须创建临时文件:
program1 | program2
操作符|被称作管道符号,因此这种操作就被称为管道。

这里有一幅来自维基百科的图片来描述管道:

这里有个使用find .(遍历当前目录下的文件和目录)的例子,将输出定向到grep程序来查找特定文件:

find . | grep "The file I'm after.txt"
第一个程序产生的数据是一行一行地导向第二个程序的,所以在第一个程序运行结束之前, 第二个程序就可以开始使用它们。
从文件重定向标准输入和输出
在重定向标准输出的同时,你也可以重定向其他流, 比如重定向标准错误输出到标准输出。我们已经讨论过在 Bash 中, 可以在文件描述符之前使用>,<和>>操作符来重定向数据流 (还记得之前讨论的数字 0,1,2 么)。如果把标准输出代表的数字 1 省略掉看, 会发现我们一直在使用标准输出。

下面这条命令执行program1并将所有标准错误数据输出到file1。

program1 2881064151> file1
执行program1,错误信息就被重定向到file了。

这里有个范例程序让你来测试,将它保存成redirect1.py:

import sys
while 1:
try:
input = sys.stdin.readline()
if input:
sys.stdout.write('Echo to stdout: %s'%input)
sys.stderr.write('Echo to stderr: %s'%input)
except KeyboardError:
sys.exit()
这个程序始终将接受到的输入数据并同时输出到 stdout 和 stderr 。

在 csh 衍生出来的 shell 中,语法则是在重定向符号之后加上&符号, 可以达到同样的效果。(译者注:即|&)

另一个常用的特性是将一个输出流重定向到定一个。 最常见的用法是将标准错误输出重定向到标准输出, 这样就可以把错误信息和正确信息合并在一起,比如:

find / -name .profile > results 2>&1
命令将会找出所有名叫.profile的文件。 如果没有重定向,它将输出命中信息到 stdout,错误信息到 stderr (比如有些目录无权限访问)。如果标准输出定向到文件,错误信息则会显示在命令行上。 为了在结果文件中可以同时看到命中信息和错误信息,我们需要使用2>&1将标准错误输出(2)输出到标准输出(1)。(这次即使在 Bash 中也需要&符。)

虽然语法上可以将2>&1放到>前面,但这样不能正常工作。 事实上,当解析器读取2>&1时候,它还不知道标准输出将重定向到哪里, 所以标准错误输出就不会被合并。

如果使用管道合并输出流,那么合并符号2>&1需要在管道符号|之前。比如:

find / -name .profile 2>&1 | less
Bash 中的合并输出简写形式是:
command > file 2>&1
为:
command &>file
或者:
command >&file
但是最好别用简写形式,否则你会弄糊涂。我提倡宁愿麻烦但是要清晰。

&>操作符同时重定向标准输出和标准错误输出。 它的作用和在 Bourne Shell 中的command > file 2>&1一样。

管道链
重定向可以和管道连接起来组成复杂的命令,比如:
ls | grep '\.sh' | sort > shlist
列出当前目录下所有文件,然后过滤剩下仅包含 .sh 的内容,根据文字编码排序, 然后将最终结果输出到 shlist。这种类型的命令经常在 shell 脚本和批处理文件中使用。
多重输出重定向
标准命令tee可以重定向一个命令到多个地方。
ls -lrt | tee xyz
这将文件列表同时输出到标准输出和文件xyz中。
Here 文档
大部分 Shell,包括 Bash 都支持Here 文档,它允许你使用<<操作符和一些文本作为分隔符将文本块嵌入到命令之中。

在下面的范例中,文本块被传送给tr命令,同时使用END_TEXT作为 Here 文档分隔符来指明文本的开始和结束。

$ tr a-z A-Z <<END_TEXT
> one two three
> uno dos tres
> END_TEXT
ONE TWO THREE
UNO DOS TRES
经过tr处理后,输出的结果是ONE TWO THREE和UNO DOS TRES。

一种常用用法是用 Here 文档向文件添加文本。 默认情况下,文本中的变量是会被替换成真实值的。

$ cat << EOF
> Working dir $PWD
> EOF
Working dir /home/user
通过在 Here 文档标签引上单引号或者双引号,就可以避免这种转义:
$ cat << "EOF"
> Working dir $PWD
> EOF
Working dir $PWD
介绍subprocess
刚才我们讨论过了一些命令行提供的功能,现在让我们体验一下subprocess模块。 你可以在命令行中运行下面这条简单的命令:
$ echo "Hello world!"
Hello world!
让我们试着在 Python 中运行它。

以前我们需要使用一堆各异的标准库来实现进程管理。 从 Python 2.4 开始,所有功能都被精心地整理到subprocess这个模块, 其中的Popen类可以提供所有我们需要的。

注意

如果你对新的Popen如何替换旧模块,[subprocess-doc][subprocess-documentation] 有一个章节解释过去是如何作用以及当前是如何作用。
Popen可以接受一下参数,详情可以在 [using-the-subprocess-module][http://docs.python.org/library/subprocess.html#using-the-subprocess-module]:
subprocess.Popen(args, bufsize=0, executable=None, stdin=None,
stdout=None, stderr=None, preexec_fn=None, close_fds=False,
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0
)
使用 Shell
让我们以 Hello World! 这个例子开始。和之前类似,通过 Python shell 执行下列命令:
>>> import subprocess
>>> subprocess.Popen('echo "Hello world!"', shell=True)
Hello world!
<subprocess.Popen object at 0x...>
如你所见,标准输出和同样打印出Hello world!, 区别在于命令行显示了一个我们创建的subprocess.Popen实例。
如果你将代码保存为process_test.py,然后在命令行执行,你会得到一样的结果:
$ python process_test.py
Hello world!
看上去运行 OK。
你可能在琢磨我们到底使用了哪个 shell。Unix 的默认 shell 是/bin/sh, 而 windows 下面则取决于COMSPEC这个环境变量。 如果你设置shell=True,则可以通过executable参数来自定义 shell。
>>> subprocess.Popen('echo "Hello world!"', shell=True, executable="/bin/bash")
Hello world!
<subprocess.Popen object at 0x...>
和我们之前看到的一样,但是如果你使用特定的 shell , 你也许会发现不同的地方。

让我们探索一下通过 Python 使用 shell 的其他特性:

变量解析:

>>> subprocess.Popen('echo $PWD', shell=True)
/home/james/Desktop
<subprocess.Popen object at 0x...>
管道和重定向:
subprocess.Popen('echo "Hello world!" | tr a-z A-Z 2> errors.txt', shell=True)
<subprocess.Popen object at 0x...>
>>> HELLO WORLD!
errors.txt应该是空的,因为没有任何错误产生。 有趣的是在我电脑上,Popen实例在HELLO WORLD!被打印到标准输出之前出现。 恩,管道和重定向都可以正常工作。

Here 文档:

>>> subprocess.Popen("""
... cat << EOF > new.txt
... Hello World!
... EOF
... """, shell=True)
<subprocess.Popen object at 0xb7dbbe2c>
new.txt文件正常生成,并且包含内容Hello World!。

如我们预料,在 shell 中正常运行的命令同样可以在 Python shell 中运行。

字符串和参数列表
现在可以轻松地在 Python 中执行命令行了,你也许会需要传递变量过去。 假设我们要用echo重写刚才那个函数:
def print_string(string):
print string
你也许想当然这样写:
def print_string(string):
subprocess.Popen('echo "%s"'%string, shell=True)
这种写法,当字符串是Hello World!时候没问题:
>>> print_string('Hello world!')
Hello world!
但这样就有问题:
>>> print_string('nasty " example')
/bin/sh: Syntax error: Unterminated quoted string
这个命令会被执行成echo "nasty" example",唔,这里的转义有问题。

一种解决方式是在代码里面做好转义,但这样会很麻烦, 你需要处理所有可能出现的转义字符和空格等等。

Python 可以帮你处理好,条件是你不能直接操作 shell, 如何操作看下文。

Shell 之外
现在让我们试试不操作 shell 来实现同样的效果:
def print_string(string):
subprocess.Popen(['echo', string], shell=False)
>>> print_string('Hello world!')
Hello world!
>>> print_string('nasty " example')
nasty " example

Shell 之外 试试不操作 shell 来实现同样的效果的更多相关文章

  1. shell浅谈之九子shell与进程处理

    转自:http://blog.csdn.net/taiyang1987912/article/details/39529291 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] ...

  2. linux下的Shell编程(3)shell里的流程控制

    if 语句 if 表达式如果条件命令组为真,则执行 then 后的部分.标准形式: if 判断命令,可以有很多个,真假取最后的返回值 then 如果前述为真做什么 [ # 方括号代表可选,别真打进去了 ...

  3. 【Shell脚本学习8】Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数

    前面已经讲到,变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid,看下面的代码: $echo $$ 运 ...

  4. 【Shell脚本学习1】Shell简介:什么是Shell,Shell命令的两种执行方式

    Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地解释 ...

  5. 登陆shell与交互式非登陆shell的区别

    登录shell 所谓登录shell,指的是当用户登录系统时所取的那个shell,登录shell属于交互式shell. 登录shell将查找4个不同的启动文件来处理其中的命令. bash shell处理 ...

  6. 【转】shell 教程——01 Shell简介:什么是Shell,Shell命令的两种执行方式

    Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地解释 ...

  7. Shell简介:什么是Shell,Shell命令的两种执行方式

    Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的.Shell既是一种命令语言,又是一种程序设计语言.作为命令语言,它交互式地解释 ...

  8. shell脚本等的操作

    1.命令替换:`` 反向单引号,也称重音符.键盘上和~键在一起的那个键呦,千万不要敲成单引号. A. 使用了``后,shell首先替换输出中``括起来的date命令,然后执行整个输出命令. B.命令替 ...

  9. Shell编程和Vim操作

    其实一直不懂什么是shell,安卓adb调试时会使用一些简单的shell命令,总结一下 1.adb调试命令 全称:Android Debug Bridge 设置: export PATH=${PATH ...

随机推荐

  1. 让IIS7.0.0.0支持 .iso .7z .torrent .apk等文件下载的设置方法

    IIS默认支持哪些MIME类型呢,我们可以这样查看:打开IIS管理器(计算机--管理--服务和应用程序--Internet信息服务(IIS)管理器:或者Win+R,输入inetmgr,Enter),在 ...

  2. C#设计模式之观察者

    Iron之观察者 引言 上一篇说的职责链模式,很有意思的一个模式,今天这个模式也是很有意思的一个模式,还是不啰嗦了直接进入主题吧. 场景介绍:在上一遍中说到用到部件检测,很巧妙的让调用者和处理者解耦了 ...

  3. 简单的方向传感器SimpleOrientationSensor

    SimpleOrientationSensor是一个简单的方向传感器.能够识别手机如下表的6种方向信息: SimpleOrientation枚举变量 方向 NotRotated 设备未旋转 Rotat ...

  4. SharePoint 2013 Apps TokenHelper SharePointContext OAuth Provider-Hosted App (抄袭,测试 csc.rsp 用)

    namespace Microshaoft.SharePointApps { using Microsoft.IdentityModel; using Microsoft.IdentityModel. ...

  5. 夺命雷公狗-----React---21--小案例之心情留言板

    这个功能如果是用传统型的jquery来写都要花费很多时间才可以完成的案例, 亲测jquery配合bootstrap来写和bootstrap配合react.js来写,不知不觉中有点震惊... jquer ...

  6. [Asp.net 开发系列之SignalR篇]专题五:SignalR支持的平台

    SignalR支持多种服务器和客户端配置.此外,每种传输方式都有自身的要求限制:如果某种传输方式不被系统支持,SignalR能够优雅地将故障转移到其他类型的传输方式.关于SignalR所支持的传输方式 ...

  7. 怎样增加windows 系统的环境变量Path的默认长度的限制?

    reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" ...

  8. ActiveMQ发布订阅模式

    ActiveMQ的另一种模式就SUB/HUB即发布订阅模式,是SUB/hub就是一拖N的USB分线器的意思.意思就是一个来源分到N个出口.还是上节的例子,当一个订单产生后,后台N个系统需要联动,但有一 ...

  9. C++ 宽字符(wchar_t)与窄字符(char)的转换

    了解 长度 宽字符wchar_t的长度16位,可以用来显示中文等除英文外的其他文字, 窄字符    char   的长度  8 位,只能处理英文. 哪里可以见到 在VS2010, 2012, 2013 ...

  10. BZOJ2375: 疯狂的涂色

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2375 小t非常喜爱画画,但是他还是一个初学者.他最近费尽千辛万苦才拜到已仙逝的达 芬奇为师(神 ...