0

我想执行一个字符串,就好像它是 Mininethost.popen()模块中的 shell 脚本一样,它本质上是 Python 的subprocess.Popen(). 脚本如下:

#!/bin/bash

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

如果我将其保存为文件并将其传递给popen(),则输出如预期的那样(打印 nc 命令的持续时间):

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

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

问题是每次我运行这个脚本时,发送的文件somefile.txt都不同,它每秒运行几次,持续几分钟。我不想为每个文件编写一个新的 .sh 脚本。当我尝试将脚本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 )    

输出是

Execution utility for Mininet

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

这是为什么?我是否遗漏了导致不同(意外)输出的格式?

4

4 回答 4

1

我认为您想要的是将文件名正确传递给脚本,这样就可以了?

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

并在脚本中做

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

这应该够了吧。或者可能我完全误解了这个问题。

======

编辑:

作为替代方案(以及对实际问题的更接近的答案),您可以这样做:

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

sh -c告诉 shell 以文字形式执行下一个命令。请注意,您不需要 a shell=True,因为您不需要对代码进行任何解析

编辑2:

这就是你回答太快的结果。您确实可以通过这种方式将完整的 shell 脚本传递给 shell。只是不要做任何逃避(并可能摆脱shebang):

cmd = """echo $(date) HELLO $(date);
   sleep 1;
   echo $(date)
"""
subprocess.Popen(cmd, shell=True)
于 2015-07-30T12:25:23.950 回答
1

最好避免完全使用外壳。您可以通过以下方式更简单地执行您想要的操作:

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

如果您担心子进程的生成时间很重要,请尝试:

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)
于 2015-07-30T12:44:13.473 回答
1

要将 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')

尽管在这种情况下您既不需要 shell,也不需要任何其他外部进程。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,))
于 2015-07-30T16:21:47.723 回答
0

假设mininet popen接口和Subprocess.popen一样,那你写的不对。您必须传递一个命令,可以是:

  • 可执行文件及其参数
  • 或 shell 命令,只要您使用shell=True- 这是您可以在 shell 提示符下键入的内容

但它不能是 shell 脚本的内容

当然,如果脚本可以被视为多行命令,并且您的 shell 支持多行命令,则将执行单个命令。因此,如果您的默认 shell 已经是 bash,它可以工作,但是如果默认 shell 是/bin/ash例如,则所有命令都将由该默认 shell 执行,因为该行将#!/bin/sh被视为仅仅是注释并被忽略。

例子 :

foo.py:

#! /usr/local/bin/python

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

文件执行正确

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

但是文件内容的执行会导致错误,因为 shebang( #!) 被视为一个注释:

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

a = 10
for i in range(10): print i
'
a: not found
Syntax error: "(" unexpected
于 2015-07-30T12:20:38.343 回答