简体   繁体   English

在 Python 中运行 Bash 命令

[英]Running Bash commands in Python

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 bashCommand which 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.当然,我再次收到错误(由os.system(bashCommand)引起),但在该错误之前它会在终端中打印命令。 Then I just copied that output and did a copy paste into the terminal and hit enter and it works...然后我只是复制了 output 并将复制粘贴到终端并按回车,它可以工作......

Does anyone have a clue what's going on?有谁知道发生了什么?

Don't use os.system .不要使用os.system It has been deprecated in favor of subprocess .它已被弃用,取而代之的是subprocess From the docs : "This module intends to replace several older modules and functions: os.system , os.spawn ".来自文档:“这个模块打算替换几个旧模块和功能: os.systemos.spawn ”。

Like in your case:就像你的情况一样:

import subprocess

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

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()喜欢subprocess.run() () 而不是 subprocess.check_call( subprocess.check_call()和朋友喜欢subprocess.call() os.system() subprocess.Popen()不是 os.system() 而不是os.popen()
  • Understand and probably use text=True , aka universal_newlines=True .理解并可能使用text=True ,又名universal_newlines=True
  • Understand the meaning of shell=True or shell=False and how it changes quoting and the availability of shell conveniences.了解shell=Trueshell=False的含义以及它如何更改引用和 shell 便利的可用性。
  • Understand differences between sh and Bash了解sh和 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.避免将 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.调用子流程的推荐方法是对它可以处理的所有用例使用run()函数。 For more advanced use cases, the underlying Popen interface can be used directly.对于更高级的用例,可以直接使用底层的Popen接口。

Unfortunately, the availability of these wrapper functions differs between Python versions.不幸的是,这些包装函数的可用性因 Python 版本而异。

  • subprocess.run() was officially introduced in Python 3.5. subprocess.run()在 Python 3.5 中正式引入。 It is meant to replace all of the following.它旨在替换以下所有内容。
  • subprocess.check_output() was introduced in Python 2.7 / 3.1. subprocess.check_output()在 Python 2.7 / 3.1 中引入。 It is basically equivalent to subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout它基本上相当于subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
  • subprocess.check_call() was introduced in Python 2.5. subprocess.check_call()是在 Python 2.5 中引入的。 It is basically equivalent to subprocess.run(..., check=True)它基本上相当于subprocess.run(..., check=True)
  • subprocess.call() was introduced in Python 2.4 in the original subprocess module ( PEP-324 ). subprocess.call()是在 Python 2.4 的原始subprocess模块 ( PEP-324 ) 中引入的。 It is basically equivalent to subprocess.run(...).returncode它基本上相当于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.重构和扩展的subprocess.run()比它取代的旧的遗留函数更符合逻辑和更通用。 It returns a CompletedProcess object 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.它返回一个CompletedProcess对象,该对象具有多种方法,允许您从已完成的子流程中检索退出状态、标准输出以及一些其他结果和状态指示符。

subprocess.run() is the way to go if you simply need a program to run and return control to Python.如果您只需要一个程序来运行并将控制权返回给 Python,那么subprocess.run()就是要走的路。 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.对于更多涉及的场景(后台进程,可能与 Python 父程序的交互式 I/O),您仍然需要使用subprocess.Popen()并自己处理所有管道。 This requires a fairly intricate understanding of all the moving parts and should not be undertaken lightly.这需要对所有活动部件有相当复杂的了解,不应轻率地进行。 The simpler Popen object represents the (possibly still-running) process which needs to be managed from your code for the remainder of the lifetime of the subprocess.更简单的Popen对象表示(可能仍在运行的)进程,需要在子进程的剩余生命周期内从您的代码中进行管理。

It should perhaps be emphasized that just subprocess.Popen() merely creates a process.或许应该强调一下,只是subprocess.Popen()只是创建一个进程。 If you leave it at that, you have a subprocess running concurrently alongside with Python, so a "background" process.如果你把它留在那里,你就会有一个子进程与 Python 一起同时运行,因此是一个“后台”进程。 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.如果它不需要输入或输出或以其他方式与您协调,它可以与您的 Python 程序并行执行有用的工作。

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

Since time eternal (well, since Python 2.5) theos module documentation has contained the recommendation to prefer subprocess over os.system() :自永恒以来(好吧,自 Python 2.5 起),os模块文档包含了更喜欢subprocess而不是os.system()的建议:

The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; subprocess模块提供了更强大的工具来生成新进程并检索它们的结果; using that module is preferable to using this function.使用该模块优于使用此功能。

The problems with system() are that it's obviously system-dependent and doesn't offer ways to interact with the subprocess. system()的问题在于它显然依赖于系统,并且不提供与子进程交互的方法。 It simply runs, with standard output and standard error outside of Python's reach.它只是运行,标准输出和标准错误超出了 Python 的范围。 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). Python 收到的唯一信息是命令的退出状态(零表示成功,尽管非零值的含义在某种程度上也取决于系统)。

PEP-324 (which was already mentioned above) contains a more detailed rationale for why os.system is problematic and how subprocess attempts 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. 2.6 版后已弃用:此功能已过时。 Use the subprocess module.使用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() .您还会注意到os.system()与 os.system( subprocess.call()有许多相同的限制。 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).在日常使用中,一般应该检查进程是否成功完成, subprocess.check_call()subprocess.check_output()是做什么的(后者也返回完成的子进程的标准输出)。 Similarly, you should usually use check=True with subprocess.run() unless you specifically need to allow the subprocess to return an error status.同样,您通常应该将check=Truesubprocess.run()一起使用,除非您特别需要允许子流程返回错误状态。

In practice, with check=True or subprocess.check_* , Python will throw a CalledProcessError exception if the subprocess returns a nonzero exit status.在实践中,使用check=True或 subprocess.check_ subprocess.check_* ,如果子进程返回非零退出状态,Python 将抛出CalledProcessError异常

A common error with subprocess.run() is to omit check=True and 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 eg when grep did not find a match.另一方面, check_call()check_output()的一个常见问题是,当出现异常时,例如当grep找不到匹配项时,盲目使用这些函数的用户会感到惊讶。 (You should probably replace grep with native Python code anyway, as outlined below.) (无论如何,您可能应该用本机 Python 代码替换grep ,如下所述。)

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=True aka universal_newlines=True理解并可能使用text=True aka universal_newlines=True

Since Python 3, strings internal to Python are Unicode strings.从 Python 3 开始,Python 内部的字符串是 Unicode 字符串。 But there is no guarantee that a subprocess generates Unicode output, or strings at all.但是不能保证子进程会生成 Unicode 输出,或者根本不会生成字符串。

(If the differences are not immediately obvious, Ned Batchelder's Pragmatic Unicode is 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 bytes buffer and interpret it somehow.在内心深处,Python 必须获取一个bytes缓冲区并以某种方式对其进行解释。 If it contains a blob of binary data, it shouldn't be 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.如果它包含一团二进制数据,则不应将其解码为 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 的能力将其解码为 Python (Unicode) 字符串(通常是 UTF-8中度更新的系统,也许 Windows 除外?)

If that's not what you request back, Python will just give you bytes strings in the stdout and stderr strings.如果这不是您要求的,Python 只会在stdoutstderr字符串中为您提供bytes字符串。 Maybe at some later point you do know that they were text strings after all, and you know their encoding.也许稍后您确实知道它们毕竟是文本字符串,并且您知道它们的编码。 Then, you can decode them.然后,您可以对它们进行解码。

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 text for the keyword argument which was previously somewhat misleadingly called universal_newlines . Python 3.7 为关键字参数引入了更短、更具描述性和更易理解的别名text ,以前被称为universal_newlines有点误导。

Understand shell=True vs shell=False了解shell=Trueshell=False

With shell=True you pass a single string to your shell, and the shell takes it from there.使用shell=True您将单个字符串传递给您的 shell,然后 shell 从那里获取它。

With shell=False you pass a list of arguments to the OS, bypassing the shell.使用shell=False ,您可以绕过 shell 将参数列表传递给操作系统。

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.当您没有外壳时,您可以保存一个过程并消除 相当多的隐藏复杂性,这些复杂性可能存在也可能不存在错误甚至安全问题。

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=True and then still pass Python a list of tokens, or vice versa.一个常见的错误是使用shell=True然后仍然向 Python 传递一个标记列表,反之亦然。 This happens to work in some cases, but is really ill-defined and could break in interesting ways.这恰好在某些情况下有效,但实际上定义不明确并且可能以有趣的方式中断。

# 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.常见的反驳“但它对我有用”不是一个有用的反驳,除非你确切地了解它在什么情况下会停止工作。

To briefly recap, correct usage looks like简要回顾一下,正确的用法看起来像

subprocess.run("string for 'the shell' to parse", shell=True)
# or
subprocess.run(["list", "of", "tokenized strings"]) # shell=False

If you want to avoid the shell but are too lazy or unsure of how to parse a string into a list of tokens, notice that shlex.split() can do this for you.如果您想避免使用 shell,但太懒或不确定如何将字符串解析为令牌列表,请注意shlex.split()可以为您完成此操作。

subprocess.run(shlex.split("no string for 'the shell' to parse"))  # shell=False
# equivalent to
# subprocess.run(["no", "string", "for", "the shell", "to", "parse"])

The regular split() will not work here, because it doesn't preserve quoting.常规的split()在这里不起作用,因为它不保留引用。 In the example above, notice how "the shell" is a single string.在上面的示例中,请注意"the shell"是如何成为单个字符串的。

Refactoring Example重构示例

Very often, the features of the shell can be replaced with native Python code.很多时候,shell 的功能可以用本机 Python 代码替换。 Simple Awk or sed scripts should probably just be translated to Python instead.简单的 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 '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 'min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Some things to note here:这里需要注意的一些事项:

  • With shell=False you don't need the quoting that the shell requires around strings.使用shell=False您不需要 shell 需要围绕字符串进行引用。 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.这使您可以从 Python 代码中更好地控制执行。
  • Having said that, complex shell pipelines are tedious and sometimes challenging to reimplement in 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.重构的代码还以非常简洁的语法说明了 shell 真正为您做了多少——无论好坏。 Python says explicit is better than implicit but the Python code is rather verbose and arguably looks more complex than this really is. Python 说显式优于隐式,但 Python 代码相当冗长,并且可以说看起来比实际情况更复杂。 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.另一方面,它提供了许多点,您可以在其他事情的中间抓住控制权,我们可以轻松地将主机名与 shell 命令输出一起包含在内的增强功能就是一个简单的例子。 (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 中进行的挑战,但是以另一个转移和可能另一个过程为代价。)

Common Shell Constructs常见的外壳结构

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 .通配符又名通配符扩展可以用glob.glob()代替,或者经常用简单的 Python 字符串比较代替,例如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 ~account to the home directory of another user) Bash 有各种其他扩展工具,例如.{png,jpg}大括号扩展和{1..100}以及波浪号扩展( ~扩展至您的主目录,更一般地~account扩展至另一个用户的主目录)
  • Shell variables like $SHELL or $my_exported_var can sometimes simply be replaced with Python variables.$SHELL$my_exported_var这样的 Shell 变量有时可以简单地替换为 Python 变量。 Exported shell variables are available as eg os.environ['SHELL'] (the meaning of export is 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 subprocess methods 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).导出的 shell 变量可用作例如os.environ['SHELL']export的含义是使变量可用于子进程 - 子进程不可用的变量显然对于作为子进程运行的 Python 不可用shell,反之亦然。 subprocess方法的env=关键字参数允许您将子进程的环境定义为字典,因此这是使 Python 变量对子进程可见的一种方法)。 With shell=False you will need to understand how to remove any quotes;使用shell=False您将需要了解如何删除任何引号; for example, cd "$HOME" is equivalent to os.chdir(os.environ['HOME']) without quotes around the directory name.例如, cd "$HOME"等价于os.chdir(os.environ['HOME'])目录名称不带引号。 (Very often cd is not useful or necessary anyway, and many beginners omit the double quotes around the variable and get away with ituntil one day ... ) (通常cd无论如何都没有用或没有必要,许多初学者省略了变量周围的双引号,直到有一天......
  • Redirection allows you to read from a file as your standard input, and write your standard output to a file.重定向允许您从文件中读取作为标准输入,并将标准输出写入文件。 grep 'foo' <inputfile >outputfile opens outputfile for writing and inputfile for reading, and passes its contents as standard input to grep , whose standard output then lands in outputfile . grep 'foo' <inputfile >outputfile inputfile outputfile写入的输出文件和用于读取的输入文件,并将其内容作为标准输入传递给grep ,然后其标准输出位于outputfile中。 This is not generally hard to replace with native Python code.这通常不难用本机 Python 代码替换。
  • Pipelines are a form of redirection.管道是重定向的一种形式。 echo foo | nl echo foo | nl runs two subprocesses, where the standard output of echo is the standard input of nl (on the OS level, in Unix-like systems, this is a single file handle). echo foo | nl运行两个子进程,其中echo的标准输出是nl的标准输入(在操作系统级别,在类 Unix 系统中,这是单个文件句柄)。 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 pipes module in the Python standard library or a number of more modern and versatile third-party competitors).如果你不能用原生 Python 代码替换管道的一端或两端,也许考虑使用 shell,特别是如果管道有两个或三个以上的进程(尽管查看Python 标准库中的pipes模块或更多现代和多才多艺的第三方竞争对手)。
  • 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.作业控制允许您中断作业、在后台运行它们、将它们返回到前台等等。停止和继续进程的基本 Unix 信号当然也可以从 Python 获得。 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.但是作业是 shell 中更高级别的抽象,它涉及进程组等,如果你想从 Python 做这样的事情,你必须了解这些。
  • Quoting in the shell is potentially confusing until you understand that everything is basically a string.在您了解所有内容基本上都是字符串之前,在 shell 中引用可能会令人困惑。 So ls -l / is equivalent to 'ls' '-l' '/' but the quoting around literals is completely optional.所以ls -l /等同于'ls' '-l' '/'但文字周围的引用是完全可选的。 Unquoted strings which contain shell metacharacters undergo parameter expansion, whitespace tokenization and wildcard expansion;包含 shell 元字符的不带引号的字符串经过参数扩展、空白标记化和通配符扩展; 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 (a remote shell command, for example).这在理论上很简单,但可能会令人困惑,尤其是当有多层解释时(例如,远程 shell 命令)。

Understand differences between sh and Bash了解sh和 Bash 之间的区别

subprocess runs your shell commands with /bin/sh unless you specifically request otherwise (except of course on Windows, where it uses the value of the COMSPEC variable). subprocess使用/bin/sh运行您的 shell 命令,除非您另有特别要求(当然在 Windows 上除外,它使用COMSPEC变量的值)。 This means that various Bash-only features like arrays, [[ etc are not available.这意味着数组、 [[等各种仅限 Bash 的功能不可用。

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 语法,您可以将 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 subprocess is separate from its parent, and cannot change it subprocess与其父进程是分开的,并且不能改变它

A somewhat common mistake is doing something like一个有点常见的错误是做类似的事情

subprocess.run('cd /tmp', shell=True)
subprocess.run('pwd', shell=True)  # Oops, doesn't print /tmp

The same thing will happen if the first subprocess tries to set an environment variable, which of course will have disappeared when you run another subprocess, etc.如果第一个子进程尝试设置环境变量,也会发生同样的事情,当您运行另一个子进程等时,它当然会消失。

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).子进程完全独立于 Python 运行,当它完成时,Python 不知道它做了什么(除了可以从子进程的退出状态和输出推断出的模糊指示符)。 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.它无法设置变量、更改工作目录,或者,换句话说,在没有父级合作的情况下与父级通信。

The immediate fix in this particular case is to run both commands in a single subprocess;在这种特殊情况下的直接解决方法是在单个子进程中运行这两个命令;

subprocess.run('cd /tmp; pwd', shell=True)

though obviously this particular use case isn't very useful;虽然显然这个特定的用例不是很有用; instead, use the cwd keyword argument, or simply os.chdir() before running the subprocess.相反,在运行子进程之前使用cwd关键字参数,或者干脆os.chdir() Similarly, for setting a variable, 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 echo is 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.在某些情况下,将 Python 解释器作为 Python 脚本的子进程运行是有意义的,甚至是绝对要求。 But very frequently, the correct approach is simply to import the other Python module into your calling script and call its functions directly.但很多时候,正确的方法是将另一个 Python 模块import到您的调用脚本中并直接调用它的函数。

If the other Python script is under your control, and it isn't a module, consider turning it into one .如果另一个 Python 脚本在您的控制之下,并且它不是一个模块,请考虑将其转换为一个. (This answer is too long already so I will not delve into details here.) (这个答案已经太长了,所以我不会在这里深入细节。)

If you need parallelism, you can run Python functions in subprocesses with the multiprocessing module.如果您需要并行性,您可以使用multiprocessing模块在子进程中运行 Python 函数。 There is also threading which 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 .)还有在单个进程中运行多个任务的threading (它更轻量级并为您提供更多控制,但也更受限制,因为进程中的线程紧密耦合,并绑定到单个GIL 。)

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

It is possible you use the bash program, with the parameter -c for execute the commands:您可以使用带有参数 -c 的 bash 程序来执行命令:

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

You can use subprocess , but I always felt that it was not a 'Pythonic' way of doing it.您可以使用subprocess ,但我一直觉得这不是一种“Pythonic”的方式。 So I created Sultan (shameless plug) that makes it easy to run command line functions.所以我创建了 Sultan(无耻插件),可以轻松运行命令行功能。

https://github.com/aeroxis/sultan https://github.com/aeroxis/sultan

According to the error you are missing a package named swap on the server.根据错误,您在服务器上缺少一个名为swap的包。 This /usr/bin/cwm requires it.这个/usr/bin/cwm需要它。 If you're on Ubuntu/Debian, install python-swap using aptitude.如果您使用的是 Ubuntu/Debian,请使用 aptitude 安装python-swap

Also you can use 'os.popen'.你也可以使用'os.popen'。 Example:例子:

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

To run the command without a shell, pass the command as a list and implement the redirection in Python using [subprocess] :要在没有 shell 的情况下运行命令,请将命令作为列表传递并使用[subprocess]在 Python 中实现重定向:

#!/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.nt at the end.注意:最后没有> test.nt stdout=file implements the redirection. stdout=file实现重定向。


To run the command using the shell in Python, pass the command as a string and 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.nt is in the command).这是负责输出重定向的shell( > test.nt在命令中)。


To run a bash command that uses bashisms, specify the bash executable explicitly eg, 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')

subprocess.Popen() is prefered over os.system() as it offers more control and visibility. os.system() subprocess.Popen()它提供了更多的控制和可见性。 However, If you find subprocess.Popen() too verbose or complex, peasyshell is a small wrapper I wrote above it, which makes it easy to interact with bash from Python.但是,如果您发现 subprocess.Popen subprocess.Popen()过于冗长或复杂, peasyshell是我在上面编写的一个小型包装器,它可以轻松地从 Python 与 bash 交互。

https://github.com/davidohana/peasyshell https://github.com/davidohana/peasyshell

The pythonic way of doing this is using subprocess.Popen这样做的pythonic方法是使用subprocess.Popen

subprocess.Popen takes 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

copy paste this:复制粘贴这个:

def run_bash_command(cmd: str) -> Any:
    import subprocess

    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
    output, error = process.communicate()
    if error:
        raise Exception(error)
    else:
        return output

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM