[英]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 :
您必须传递一个命令,该命令可以是:
shell=True
- that is something you could type at a shell prompt 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.