在 Python 中运行 Bash 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4256107/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 14:56:54  来源:igfitidea点击:

Running Bash commands in Python

pythonbash

提问by mkn

On my local machine, I run a python script which contains this line

在我的本地机器上,我运行了一个包含这一行的 python 脚本

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)

This works fine.

这工作正常。

Then I run the same code on a server and I get the following error message

然后我在服务器上运行相同的代码,并收到以下错误消息

'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import  diag
ImportError: No module named swap

So what I did then is I inserted a print bashCommandwhich prints me than the command in the terminal before it runs it with os.system().

所以我当时所做的是插入一个print bashCommand比终端中的命令打印我的命令,然后再使用os.system().

Of course, I get again the error (caused by os.system(bashCommand)) but before that error it prints the command in the terminal. Then I just copied that output and did a copy paste into the terminal and hit enter and it works...

当然,我再次收到错误(由 引起os.system(bashCommand))但在该错误之前它会在终端中打印命令。然后我只是复制了该输出并将复制粘贴到终端中并按回车键,它就可以工作了......

Does anyone have a clue what's going on?

有谁知道发生了什么?

回答by Jakob Bowyer

Call it with subprocess

用子进程调用它

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

The error you are getting seems to be because there is no swap module on the server, you should install swap on the server then run the script again

你得到的错误似乎是因为服务器上没有交换模块,你应该在服务器上安装交换然后再次运行脚本

回答by user225312

Don't use os.system. It has been deprecated in favor of subprocess. From the docs: "This module intends to replace several older modules and functions: os.system, os.spawn".

不要使用os.system. 它已被弃用,取而代之的是subprocess。来自文档:“此模块打算替换几个较旧的模块和功能:os.systemos.spawn”。

Like in your case:

就像你的情况:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

回答by kichik

According to the error you are missing a package named swapon the server. This /usr/bin/cwmrequires it. If you're on Ubuntu/Debian, install python-swapusing aptitude.

根据错误,您在服务器上缺少一个名为swap的包。这/usr/bin/cwm需要它。如果您使用的是 Ubuntu/Debian,请python-swap使用 aptitude 进行安装。

回答by Razor

It is possible you use the bash program, with the parameter -c for execute the commands:

您可以使用 bash 程序,并使用参数 -c 来执行命令:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])

回答by David Daniel

You can use subprocess, but I always felt that it was not a 'Pythonic' way of doing it. So I created Sultan (shameless plug) that makes it easy to run command line functions.

你可以使用subprocess,但我一直觉得这不是一种“Pythonic”的做法。所以我创建了 Sultan(无耻插件),它可以轻松运行命令行功能。

https://github.com/aeroxis/sultan

https://github.com/aeroxis/sultan

回答by RewordedAnswers

The pythonic way of doing this is using subprocess.Popen

这样做的pythonic方法是使用 subprocess.Popen

subprocess.Popentakes a list where the first element is the command to be run followed by any command line arguments.

subprocess.Popen接受一个列表,其中第一个元素是要运行的命令,后跟任何命令行参数。

As an example:

举个例子:

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line

回答by ricardo130

Also you can use 'os.popen'. Example:

你也可以使用'os.popen'。例子:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

Output:

输出:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None

回答by tripleee

To somewhat expand on the earlier answers here, there are a number of details which are commonly overlooked.

为了稍微扩展这里的早期答案,有许多通常被忽视的细节。

  • Prefer subprocess.run()over subprocess.check_call()and friends over subprocess.call()over subprocess.Popen()over os.system()over os.popen()
  • Understand and probably use text=True, aka universal_newlines=True.
  • Understand the meaning of shell=Trueor shell=Falseand how it changes quoting and the availability of shell conveniences.
  • Understand differences between shand Bash
  • Understand how a subprocess is separate from its parent, and generally cannot change the parent.
  • Avoid running the Python interpreter as a subprocess of Python.
  • 身高subprocess.run()subprocess.check_call()和朋友过subprocess.call()subprocess.Popen()os.system()os.popen()
  • 理解并可能使用text=True,又名universal_newlines=True
  • 了解shell=Trueor的含义shell=False以及它如何改变引用和 shell 便利的可用性。
  • 了解shBash 和 Bash之间的差异
  • 了解子流程如何与其父流程分离,并且通常无法更改父流程。
  • 避免将 Python 解释器作为 Python 的子进程运行。

These topics are covered in some more detail below.

下面将更详细地介绍这些主题。

Prefer subprocess.run()or subprocess.check_call()

偏好subprocess.run()subprocess.check_call()

The subprocess.Popen()function is a low-level workhorse but it is tricky to use correctly and you end up copy/pasting multiple lines of code ... which conveniently already exist in the standard library as a set of higher-level wrapper functions for various purposes, which are presented in more detail in the following.

subprocess.Popen()函数是一个低级的主力,但正确使用很棘手,你最终会复制/粘贴多行代码......这些代码已经很方便地存在于标准库中,作为一组用于各种目的的高级包装函数,下面将更详细地介绍这些内容。

Here's a paragraph from the documentation:

这是文档中的一段:

The recommended approach to invoking subprocesses is to use the run()function for all use cases it can handle. For more advanced use cases, the underlying Popeninterface can be used directly.

调用子流程的推荐方法是将该run()函数用于它可以处理的所有用例。对于更高级的用例,Popen可以直接使用底层接口。

Unfortunately, the availability of these wrapper functions differs between Python versions.

不幸的是,这些包装函数的可用性因 Python 版本而异。

  • subprocess.run()was officially introduced in Python 3.5. It is meant to replace all of the following.
  • subprocess.check_output()was introduced in Python 2.7 / 3.1. It is basically equivalent to subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call()was introduced in Python 2.5. It is basically equivalent to subprocess.run(..., check=True)
  • subprocess.call()was introduced in Python 2.4 in the original subprocessmodule (PEP-324). It is basically equivalent to subprocess.run(...).returncode
  • subprocess.run()在 Python 3.5 中正式引入。它旨在取代以下所有内容。
  • subprocess.check_output()是在 Python 2.7 / 3.1 中引入的。它基本上相当于subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call()在 Python 2.5 中引入。它基本上相当于subprocess.run(..., check=True)
  • subprocess.call()在 Python 2.4 的原始subprocess模块 ( PEP-324) 中引入。它基本上相当于subprocess.run(...).returncode

High-level API vs subprocess.Popen()

高级 API 与 subprocess.Popen()

The refactored and extended subprocess.run()is more logical and more versatile than the older legacy functions it replaces. It returns a CompletedProcessobject which has various methods which allow you to retrieve the exit status, the standard output, and a few other results and status indicators from the finished subprocess.

重构和扩展subprocess.run()比它取代的旧的遗留功能更合乎逻辑和更通用。它返回一个CompletedProcess对象,该对象具有各种方法,允许您从已完成的子流程中检索退出状态、标准输出以及其他一些结果和状态指示符。

subprocess.run()is the way to go if you simply need a program to run and return control to Python. For more involved scenarios (background processes, perhaps with interactive I/O with the Python parent program) you still need to use subprocess.Popen()and take care of all the plumbing yourself. This requires a fairly intricate understanding of all the moving parts and should not be undertaken lightly. The simpler Popenobjectrepresents the (possibly still-running) process which needs to be managed from your code for the remainder of the lifetime of the subprocess.

subprocess.run()如果您只需要一个程序来运行并将控制权返回给 Python,这是一种可行的方法。对于更多涉及的场景(后台进程,可能与 Python 父程序的交互式 I/O),您仍然需要自己使用subprocess.Popen()和处理所有管道。这需要对所有活动部件有相当复杂的了解,不应轻易进行。更简单的Popen对象表示(可能仍在运行)进程,在子进程的剩余生命周期中需要从您的代码管理该进程。

It should perhaps be emphasized that just subprocess.Popen()merely creates a process. If you leave it at that, you have a subprocess running concurrently alongside with Python, so a "background" process. If it doesn't need to do input or output or otherwise coordinate with you, it can do useful work in parallel with your Python program.

或许应该强调的是,这subprocess.Popen()仅仅是创建一个过程。如果你把它留在那里,你有一个与 Python 同时运行的子进程,所以是一个“后台”进程。如果它不需要进行输入或输出或以其他方式与您协调,它可以与您的 Python 程序并行执行有用的工作。

Avoid os.system()and os.popen()

避免os.system()os.popen()

Since time eternal (well, since Python 2.5) the osmodule documentationhas contained the recommendation to prefer subprocessover os.system():

自永恒以来(好吧,自 Python 2.5 以来),os模块文档已包含建议优先subprocessos.system()

The subprocessmodule provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function.

subprocess模块提供了更强大的工具来生成新进程并检索它们的结果;使用该模块比使用此功能更可取。

The problems with system()are that it's obviously system-dependent and doesn't offer ways to interact with the subprocess. It simply runs, with standard output and standard error outside of Python's reach. The only information Python receives back is the exit status of the command (zero means success, though the meaning of non-zero values is also somewhat system-dependent).

问题system()在于它显然依赖于系统,并且不提供与子流程交互的方法。它只是运行,标准输出和标准错误超出了 Python 的范围。Python 收到的唯一信息是命令的退出状态(零表示成功,但非零值的含义也与系统有关)。

PEP-324(which was already mentioned above) contains a more detailed rationale for why os.systemis problematic and how subprocessattempts to solve those issues.

PEP-324(上面已经提到过)包含更详细的理由,说明为什么os.system会出现问题以及如何subprocess尝试解决这些问题。

os.popen()used to be even more strongly discouraged:

os.popen()曾经更强烈地劝阻

Deprecated since version 2.6:This function is obsolete. Use the subprocessmodule.

2.6 版后已弃用:此功能已过时。使用subprocess模块。

However, since sometime in Python 3, it has been reimplemented to simply use subprocess, and redirects to the subprocess.Popen()documentation for details.

但是,自从在 Python 3 中的某个时候,它已被重新实现为简单地使用subprocess,并重定向到subprocess.Popen()文档以获取详细信息。

Understand and usually use check=True

了解并经常使用 check=True

You'll also notice that subprocess.call()has many of the same limitations as os.system(). In regular use, you should generally check whether the process finished successfully, which subprocess.check_call()and subprocess.check_output()do (where the latter also returns the standard output of the finished subprocess). Similarly, you should usually use check=Truewith subprocess.run()unless you specifically need to allow the subprocess to return an error status.

您还会注意到它subprocess.call()os.system(). 在常规使用中,一般应该检查进程是否成功完成,whichsubprocess.check_call()subprocess.check_output()do(其中后者也返回完成的子进程的标准输出)。同样,您通常应该使用check=Truewith ,subprocess.run()除非您特别需要允许子进程返回错误状态。

In practice, with check=Trueor subprocess.check_*, Python will throw a CalledProcessErrorexceptionif the subprocess returns a nonzero exit status.

在实践中,如果子进程返回非零退出状态,则使用check=Truesubprocess.check_*,Python 将抛出CalledProcessError异常

A common error with subprocess.run()is to omit check=Trueand be surprised when downstream code fails if the subprocess failed.

一个常见的错误subprocess.run()check=True如果子进程失败,则在下游代码失败时忽略并感到惊讶。

On the other hand, a common problem with check_call()and check_output()was that users who blindly used these functions were surprised when the exception was raised e.g. when grepdid not find a match. (You should probably replace grepwith native Python code anyway, as outlined below.)

另一方面,check_call()and 的一个常见问题check_output()是盲目使用这些功能的用户在引发异常时感到惊讶,例如当grep没有找到匹配项时。(grep无论如何,您应该使用本机 Python 代码替换,如下所述。)

All things counted, you need to understand how shell commands return an exit code, and under what conditions they will return a non-zero (error) exit code, and make a conscious decision how exactly it should be handled.

所有的事情都计算在内,您需要了解 shell 命令如何返回退出代码,以及在什么条件下它们将返回非零(错误)退出代码,并有意识地决定应该如何处理它。

Understand and probably use text=Trueaka universal_newlines=True

理解并可能使用text=Trueakauniversal_newlines=True

Since Python 3, strings internal to Python are Unicode strings. But there is no guarantee that a subprocess generates Unicode output, or strings at all.

从 Python 3 开始,Python 内部的字符串是 Unicode 字符串。但是不能保证子进程生成 Unicode 输出或字符串。

(If the differences are not immediately obvious, Ned Batchelder's Pragmatic Unicodeis recommended, if not outright obligatory, reading. There is a 36-minute video presentation behind the link if you prefer, though reading the page yourself will probably take significantly less time.)

(如果差异不是很明显,建议使用Ned Batchelder 的Pragmatic Unicode,如果不是完全必须阅读的话。如果您愿意,链接后面有一个 36 分钟的视频演示,尽管您自己阅读页面可能会花费更少的时间。 )

Deep down, Python has to fetch a bytesbuffer and interpret it somehow. If it contains a blob of binary data, it shouldn'tbe decoded into a Unicode string, because that's error-prone and bug-inducing behavior - precisely the sort of pesky behavior which riddled many Python 2 scripts, before there was a way to properly distinguish between encoded text and binary data.

在内心深处,Python 必须获取一个bytes缓冲区并以某种方式解释它。如果它包含二进制数据的 blob,则不应将其解码为 Unicode 字符串,因为这是容易出错和引发错误的行为 - 正是这种令人讨厌的行为使许多 Python 2 脚本百思不得其解,之前有一种方法可以正确区分编码文本和二进制数据。

With text=True, you tell Python that you, in fact, expect back textual data in the system's default encoding, and that it should be decoded into a Python (Unicode) string to the best of Python's ability (usually UTF-8 on any moderately up to date system, except perhaps Windows?)

使用text=True,您告诉 Python 实际上,您期望返回系统默认编码中的文本数据,并且应该将其解码为 Python (Unicode) 字符串以尽 Python 的最大能力(通常是 UTF-8日期系统,也许除了 Windows?)

If that's notwhat you request back, Python will just give you bytesstrings in the stdoutand stderrstrings. Maybe at some later point you doknow that they were text strings after all, and you know their encoding. Then, you can decode them.

如果这不是您请求返回的内容,Python 只会bytesstdoutstderr字符串中为您提供字符串。也许稍后您确实知道它们毕竟是文本字符串,并且您知道它们的编码。然后,您可以解码它们。

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

Python 3.7 introduced the shorter and more descriptive and understandable alias textfor the keyword argument which was previously somewhat misleadingly called universal_newlines.

Python 3.7text为关键字参数引入了更短、更具描述性和更易于理解的别名,以前有些误导性地将其称为universal_newlines.

Understand shell=Truevs shell=False

了解shell=Trueshell=False

With shell=Trueyou pass a single string to your shell, and the shell takes it from there.

shell=True您将单个字符串传递给您的外壳时,外壳会从那里获取它。

With shell=Falseyou pass a list of arguments to the OS, bypassing the shell.

随着shell=False你传递的参数列表的OS,绕过外壳。

When you don't have a shell, you save a process and get rid of a fairly substantial amount of hidden complexity, which may or may not harbor bugs or even security problems.

当您没有 shell 时,您可以节省一个进程并摆脱大量隐藏的复杂性,这些复杂性可能存在也可能不存在错误甚至安全问题。

On the other hand, when you don't have a shell, you don't have redirection, wildcard expansion, job control, and a large number of other shell features.

另一方面,当您没有 shell 时,您就没有重定向、通配符扩展、作业控制和大量其他 shell 功能。

A common mistake is to use shell=Trueand then still pass Python a list of tokens, or vice versa. This happens to work in some cases, but is really ill-defined and could break in interesting ways.

一个常见的错误是使用shell=True然后仍然传递给 Python 一个令牌列表,反之亦然。这在某些情况下碰巧有效,但实际上定义不明确,可能会以有趣的方式中断。

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

The common retort "but it works for me" is not a useful rebuttal unless you understand exactly under what circumstances it could stop working.

常见的反驳“但它对我有用”并不是有用的反驳,除非您确切了解在什么情况下它会停止工作。

Refactoring Example

重构示例

Very often, the features of the shell can be replaced with native Python code. Simple Awk or sedscripts should probably simply be translated to Python instead.

很多时候,shell 的特性可以用原生 Python 代码替换。简单的 awk 或sed脚本可能应该简单地转换为 Python。

To partially illustrate this, here is a typical but slightly silly example which involves many shell features.

为了部分说明这一点,这里是一个典型但有点傻的例子,它涉及许多 shell 功能。

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Some things to note here:

这里需要注意的一些事项:

  • With shell=Falseyou don't need the quoting that the shell requires around strings. Putting quotes anyway is probably an error.
  • It often makes sense to run as little code as possible in a subprocess. This gives you more control over execution from within your Python code.
  • Having said that, complex shell pipelines are tedious and sometimes challenging to reimplement in Python.
  • 因为shell=False你不需要 shell 需要在字符串周围的引用。无论如何加上引号可能是一个错误。
  • 在子进程中运行尽可能少的代码通常是有意义的。这使您可以更好地控制 Python 代码中的执行。
  • 话虽如此,复杂的 shell 管道很乏味,有时在 Python 中重新实现也很有挑战性。

The refactored code also illustrates just how much the shell really does for you with a very terse syntax -- for better or for worse. Python says explicit is better than implicitbut the Python code israther verbose and arguably looks more complex than this really is. On the other hand, it offers a number of points where you can grab control in the middle of something else, as trivially exemplified by the enhancement that we can easily include the host name along with the shell command output. (This is by no means challenging to do in the shell, either, but at the expense of yet another diversion and perhaps another process.)

重构后的代码还以非常简洁的语法说明了 shell 到底为您做了多少——无论好坏。蟒蛇说明确优于隐式,但Python代码相当冗长,可以说是看起来复杂得多,这确实是。另一方面,它提供了许多点,您可以在其中获取控制权,例如我们可以轻松地将主机名与 shell 命令输出一起包含在内的增强功能。(这也绝不是在 shell 中进行的挑战,而是以另一种转移和可能的另一个过程为代价。)

Common Shell Constructs

常见的 Shell 构造

For completeness, here are brief explanations of some of these shell features, and some notes on how they can perhaps be replaced with native Python facilities.

为了完整起见,这里是对这些 shell 功能中的一些的简要说明,以及关于如何用原生 Python 工具替换它们的一些说明。

  • Globbing aka wildcard expansion can be replaced with glob.glob()or very often with simple Python string comparisons like for file in os.listdir('.'): if not file.endswith('.png'): continue. Bash has various other expansion facilities like .{png,jpg}brace expansion and {1..100}as well as tilde expansion (~expands to your home directory, and more generally ~accountto the home directory of another user)
  • Shell variables like $SHELLor $my_exported_varcan sometimes simply be replaced with Python variables. Exported shell variables are available as e.g. os.environ['SHELL'](the meaning of exportis to make the variable available to subprocesses -- a variable which is not available to subprocesses will obviously not be available to Python running as a subprocess of the shell, or vice versa. The env=keyword argument to subprocessmethods allows you to define the environment of the subprocess as a dictionary, so that's one way to make a Python variable visible to a subprocess). With shell=Falseyou will need to understand how to remove any quotes; for example, cd "$HOME"is equivalent to os.chdir(os.environ['HOME'])without quotes around the directory name. (Very often cdis not useful or necessary anyway, and many beginners omit the double quotes around the variable and get away with it until one day ...)
  • Redirection allows you to read from a file as your standard input, and write your standard output to a file. grep 'foo' <inputfile >outputfileopens outputfilefor writing and inputfilefor reading, and passes its contents as standard input to grep, whose standard output then lands in outputfile. This is not generally hard to replace with native Python code.
  • Pipelines are a form of redirection. echo foo | nlruns two subprocesses, where the standard output of echois the standard input of nl(on the OS level, in Unix-like systems, this is a single file handle). If you cannot replace one or both ends of the pipeline with native Python code, perhaps think about using a shell after all, especially if the pipeline has more than two or three processes (though look at the pipesmodule in the Python standard libraryor a number of more modern and versatile third-party competitors).
  • Job control lets you interrupt jobs, run them in the background, return them to the foreground, etc. The basic Unix signals to stop and continue a process are of course available from Python, too. But jobs are a higher-level abstraction in the shell which involve process groups etc which you have to understand if you want to do something like this from Python.
  • Quoting in the shell is potentially confusing until you understand that everythingis basically a string. So ls -l /is equivalent to 'ls' '-l' '/'but the quoting around literals is completely optional. Unquoted strings which contain shell metacharacters undergo parameter expansion, whitespace tokenization and wildcard expansion; double quotes prevent whitespace tokenization and wildcard expansion but allow parameter expansions (variable substitution, command substitution, and backslash processing). This is simple in theory but can get bewildering, especially when there are several layers of interpretation (an remote shell command, for example).
  • Globbing aka 通配符扩展可以替换为glob.glob()或经常替换为简单的 Python 字符串比较,例如for file in os.listdir('.'): if not file.endswith('.png'): continue. Bash 有各种其他扩展工具,如.{png,jpg}大括号扩展和{1..100}波浪号扩展(~扩展到您的主目录,更一般~account地扩展到另一个用户的主目录)
  • $SHELL$my_exported_var有时可以简单地用 Python 变量替换Shell变量。导出的shell变量可以作为eg使用os.environ['SHELL'](意思export是让变量对子进程可用——子进程不可用的变量显然对作为shell子进程运行的Python不可用,反之亦然。env=关键字subprocess方法的参数允许您将子进程的环境定义为字典,因此这是使 Python 变量对子进程可见的一种方法)。随着shell=False你需要了解如何消除任何报价; 例如,cd "$HOME"相当于os.chdir(os.environ['HOME'])目录名周围没有引号。(常常cd无论如何都没有用或必要,许多初学者省略了变量周围的双引号,直到有一天...
  • 重定向允许您从文件中读取作为标准输入,并将标准输出写入文件。为写入和读取而grep 'foo' <inputfile >outputfile打开,并将其内容作为标准输入传递给,然后其标准输出进入。这通常不难用原生 Python 代码替换。outputfileinputfilegrepoutputfile
  • 管道是重定向的一种形式。echo foo | nl运行两个子进程,其中的标准输出echo是标准输入nl(在操作系统级别,在类 Unix 系统中,这是单个文件句柄)。如果你不能用原生 Python 代码替换管道的一端或两端,也许毕竟考虑使用 shell,特别是如果管道有两个或三个以上的进程(尽管查看pipesPython 标准库中的模块或数字更多现代和多功能的第三方竞争对手)。
  • 作业控制允许您中断作业、在后台运行它们、将它们返回到前台等。停止和继续进程的基本 Unix 信号当然也可以从 Python 中获得。但是作业是 shell 中的一个更高级别的抽象,它涉及进程组等,如果你想从 Python 做这样的事情,你必须了解这些。
  • 在您了解所有内容基本上都是字符串之前,在 shell 中引用可能会令人困惑。Sols -l /等价于'ls' '-l' '/'但文字周围的引用是完全可选的。包含 shell 元字符的未加引号的字符串经过参数扩展、空白标记化和通配符扩展;双引号可防止空格标记化和通配符扩展,但允许参数扩展(变量替换、命令替换和反斜杠处理)。这在理论上很简单,但可能会令人困惑,尤其是当有多层解释时(例如,远程 shell 命令)。

Understand differences between shand Bash

了解shBash 和 Bash之间的差异

subprocessruns your shell commands with /bin/shunless you specifically request otherwise (except of course on Windows, where it uses the value of the COMSPECvariable). This means that various Bash-only features like arrays, [[etcare not available.

subprocess/bin/sh除非您特别要求,否则运行您的 shell 命令(当然在 Windows 上除外,它使用COMSPEC变量的值)。这意味着各种 Bash-only 功能(如数组[[等)不可用。

If you need to use Bash-only syntax, you can pass in the path to the shell as executable='/bin/bash'(where of course if your Bash is installed somewhere else, you need to adjust the path).

如果您需要使用 Bash-only 语法,您可以将路径传递给 shell executable='/bin/bash'(当然,如果您的 Bash 安装在其他地方,则需要调整路径)。

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessis separate from its parent, and cannot change it

Asubprocess与其父级分开,并且无法更改它

A somewhat common mistake is doing something like

一个有点常见的错误是做类似的事情

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

which aside from the lack of elegance also betrays a fundamental lack of understanding of the "sub" part of the name "subprocess".

除了缺乏优雅之外,这还表明对“子流程”名称的“子”部分缺乏基本的理解。

A child process runs completely separate from Python, and when it finishes, Python has no idea what it did (apart from the vague indicators that it can infer from the exit status and output from the child process). A child generally cannot change the parent's environment; it cannot set a variable, change the working directory, or, in so many words, communicate with its parent without cooperation from the parent.

子进程完全独立于 Python 运行,当它完成时,Python 不知道它做了什么(除了它可以从子进程的退出状态和输出推断出的模糊指示符)。孩子一般无法改变父母的环境;它无法设置变量,更改工作目录,或者,在没有父级合作的情况下,无法与其父级通信。

The immediate fix in this particular case is to run both commands in a single subprocess;

在这种特殊情况下的直接解决方法是在单个子进程中运行这两个命令;

subprocess.run('foo=bar; echo "$foo"', shell=True)

though obviously this particular use case doesn't require the shell at all. Remember, you can manipulate the environment of the current process (and thus also its children) via

尽管显然这个特殊用例根本不需要外壳。请记住,您可以通过以下方式操作当前进程(及其子进程)的环境

os.environ['foo'] = 'bar'

or pass an environment setting to a child process with

或将环境设置传递给子进程

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(not to mention the obvious refactoring subprocess.run(['echo', 'bar']); but echois a poor example of something to run in a subprocess in the first place, of course).

(更不用说明显的重构subprocess.run(['echo', 'bar'])echo当然,这是首先在子流程中运行的东西的一个糟糕的例子)。

Don't run Python from Python

不要从 Python 运行 Python

This is slightly dubious advice; there are certainly situations where it does make sense or is even an absolute requirement to run the Python interpreter as a subprocess from a Python script. But very frequently, the correct approach is simply to importthe other Python module into your calling script and call its functions directly.

这是一个有点可疑的建议;在某些情况下,将 Python 解释器作为 Python 脚本的子进程运行确实有意义,甚至是绝对要求。但很多时候,正确的做法是简单地import将其他 Python 模块放入您的调用脚本中并直接调用其函数。

If the other Python script is under your control, and it isn't a module, consider turning it into one. (This answer is too long already so I will not delve into details here.)

如果另一个 Python 脚本在您的控制之下,并且它不是一个模块,请考虑将其转换为一个. (这个答案已经太长了,所以我不会在这里深入研究细节。)

If you need parallelism, you can run Python functions in subprocesses with the multiprocessingmodule.There is also threadingwhich runs multiple tasks in a single process (which is more lightweight and gives you more control, but also more constrained in that threads within a process are tightly coupled, and bound to a single GIL.)

如果您需要并行性,您可以使用multiprocessing模块在子进程中运行 Python 函数还有threadingwhich 在单个进程中运行多个任务(它更轻量级并为您提供更多控制,但也受到更多限制,因为进程内的线程紧密耦合并绑定到单个GIL。)

回答by jfs

To run the command without a shell, pass the command as a listand implement the redirection in Python using [subprocess]:

要在没有 shell 的情况下运行命令,请将命令作为列表传递并使用 Python 实现重定向[subprocess]

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

Note: no > test.ntat the end. stdout=fileimplements the redirection.

注意:最后没有> test.ntstdout=file实现重定向。



To run the command using the shell in Python, pass the command as a stringand enable shell=True:

要在 Python 中使用 shell 运行命令,请将命令作为字符串传递并启用shell=True

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      shell=True)

Here's the shell is responsible for the output redirection (> test.ntis in the command).

这里的shell负责输出重定向(> test.nt在命令中)。



To run a bash command that uses bashisms, specify the bash executable explicitly e.g., to emulate bash process substitution:

要运行使用 bashisms 的 bash 命令,请明确指定 bash 可执行文件,例如,模拟 bash 进程替换

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')