简体   繁体   English

将“ shell脚本”作为字符串传递给Python子进程。

[英]Passing a “shell script” as a string to Python subprocess.Popen

I want to execute a string as if it were a shell script in Mininet's host.popen() module, which is essentially a wrapper for Python's subprocess.Popen() . 我想,如果它是在Mininet的一个shell脚本来执行字符串host.popen()模块,它本质上是Python的包装subprocess.Popen() The script is as follows: 脚本如下:

#!/bin/bash

T="$(date +%s%N)" 
nc 10.0.0.7 1234 < somefile.txt
T="$(($(date +%s%N)-T))" 
echo $T

If I save it as a file and pass that into popen() , the output is as expected (prints the duration of the nc command): 如果我将其保存为文件并将其传递给popen() ,则输出与预期的一样(打印nc命令的持续时间):

dst_cmd = 'nc -l 1234 > /dev/null'
dst.popen( dst_cmd, shell=True )

p = src.popen( ['sh', './timer.sh'], stdout=subprocess.PIPE )

The thing is that the file getting sent, somefile.txt , is different every time I run this script, and it is run several times a second for a few minutes. 问题是,每次我运行此脚本时,发送的文件somefile.txt都不相同,并且每秒运行几次,持续几分钟。 I'd prefer not to have to write a new .sh script for every file. 我希望不必为每个文件编写一个新的.sh脚本。 When I try to pass the script into popen() as a string, like this, 当我尝试将脚本作为字符串传递给popen()

dst_cmd = 'nc -l 1234 > /dev/null'
dst.popen( dst_cmd, shell=True )

src_cmd = '''\
    #!/bin/bash

    T=\"$(date +%s%N)\" 
    nc 10.0.0.7 1234 < somefile.txt
    T=\"$(($(date +%s%N)-T))\" 
    echo $T'''

p = src.popen( dedent(src_cmd), shell=True, 
                stdout=subprocess.PIPE )    

the output is 输出是

Execution utility for Mininet

Usage: mnexec [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...
...

Why is that? 这是为什么? Am I missing something about the formatting that's causing a different (unexpected) output? 我是否缺少有关导致不同(意外)输出的格式的信息?

It's best to avoid using the shell entirely. 最好避免完全使用外壳。 You can do exactly what you want more simply by: 您可以通过以下方法更轻松地完成您想要的事情:

from subprocess import Popen, DEVNULL
from time import time

start = time()
with open("somefile.txt", "rb") as f:
    p = Popen(["nc", "10.0.0.7", "1234"], stdin=f, stdout=DEVNULL)
end = time()

duration = end - start

If you're worried about the time of the spawning of the subprocess being significant then try: 如果您担心子流程的产生时间很长,请尝试:

from subprocess import Popen, DEVNULL, PIPE
from time import time
from os import devnull


with open("somefile.txt", "rb") as f:
    data = f.read()

p = Popen(["nc", "10.0.0.7", "1234"], stdin=PIPE, stdout=DEVNULL)
start = time()
p.communicate(data)
end = time()

duration = end - start

print(duration)

To pass a bash script as a string, specify executable parameter: 要将bash脚本作为字符串传递,请指定executable参数:

#!/usr/bin/env python
import subprocess

bash_string = r'''#!/bin/bash
T="$(date +%s%N)" 
nc 10.0.0.7 1234 < somefile.txt
T="$(($(date +%s%N)-T))" 
echo $T
'''
output = subprocess.check_output(bash_string, shell=True, executable='/bin/bash')

Though you don't need neither the shell nor any other external process in this case. 尽管在这种情况下,您不需要外壳程序或任何其他外部进程。 You could reimplement the shell script in pure Python using socket module: 您可以使用socket模块在纯Python中重新实现Shell脚本:

#!/usr/bin/env python3
import socket
import sys
from shutil import copyfileobj
from timeit import default_timer as timer

start = timer()
with socket.create_connection(('10.0.0.7', 1234)) as s, \
     open('somefile.txt', 'rb') as input_file:
    s.sendfile(input_file) # send file
    with s.makefile() as f: # read response
        copyfileobj(f, sys.stdout)
print("It took %.2f seconds" % (timer() - start,))

Assuming that mininet popen interface is the same as Subprocess.popen, what you write is wrong. 假设mininet popen接口与Subprocess.popen相同,那么您编写的内容是错误的。 You must pass a command, that can be : 您必须传递一个命令,该命令可以是:

  • either an executable file and its parameters 可执行文件及其参数
  • or a shell command, provided you use shell=True - that is something you could type at a shell prompt 或shell命令,只要您使用shell=True您可以在shell提示符下键入

But it cannot be the content of a shell script 但这不能是Shell脚本的内容

Of course, if the script can be seen as a multi-line command and if your shell supports multi-line commands, the individual commands will be executed. 当然,如果脚本可以看作是多行命令,并且您的外壳支持多行命令,则将执行各个命令。 So if you default shell is already bash, it could work, but if the default shell is /bin/ash for example, all the commands will be executed by that default shell, because the line #!/bin/sh will be seen as a mere comment and ignored. 因此,如果您的默认外壳程序已经是bash,它可以工作,但是例如,如果默认外壳程序是/bin/ash ,则所有命令将由该默认外壳程序执行,因为#!/bin/sh行将被视为只是评论而被忽略。

Example : 范例:

foo.py : foo.py:

#! /usr/local/bin/python

a = 5
for i in range(10): print i

Execution of file is correct 文件执行正确

$ sh -c ./foo.py
0
1
2
3
4

But execution of the content of the file causes an error because the shebang ( #! ) is seen as a mete comment : 但是执行文件的内容会导致错误,因为shebang( #! )被看作是一个简短的注释:

$ sh -c '#! /usr/local/bin/python

a = 10
for i in range(10): print i
'
a: not found
Syntax error: "(" unexpected

I think that what you want is pass the filename to the script right, that would do the trick? 我认为您想要的是将文件名正确传递给脚本,那可以解决问题吗?

p = src.popen( ['sh', './timer.sh', 'filename.txt'], stdout=subprocess.PIPE )

and in the script do 并在脚本中

FILENAME="$1"
....
nc 10.0.0.7 1234 < "$FILENAME"

That should do the trick. 这应该够了吧。 Or possibly I'm completely misunderstanding the problem. 也许我完全误解了这个问题。

====== ======

EDIT: 编辑:

As an alternative (and a closer answer to the actual question), you could do: 作为替代方案(以及对实际问题的更精确答案),您可以执行以下操作:

cmd = """echo $(date) HELLO $(date)"""
p = subprocess.Popen(["sh", "-c", cmd])

sh -c tells shell to execute the next command as literal. sh -c告诉shell以文字形式执行下一个命令。 Note that you don't need a shell=True , since you don't need any parsing of the code 请注意,您不需要shell=True ,因为您不需要任何代码解析

EDIT 2: 编辑2:

That's what you get for answering too fast. 这就是您回答得太快的结果。 You can indeed just pass a full shell script to the shell this way. 你的确可以只通过一个完整的shell脚本壳这种方式。 Just don't do any escaping (and possibly get rid of the shebang): 只是不要做任何转义(并可能摆脱shebang):

cmd = """echo $(date) HELLO $(date);
   sleep 1;
   echo $(date)
"""
subprocess.Popen(cmd, shell=True)

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

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