使用Python通过ssh将文件传输到远程bash脚本

4

我将使用subprocess.popen和shlex来调用远程bash脚本,使用ssh进行连接。在bash中,这个命令可以正常运行。但是一旦我尝试将其转换为Python并使用shlex与subprocess.popen一起使用时,它会出错。

远程bash脚本:

#!/bin/bash
tmp="";     
while read -r line;
do
    tmp="$tmp $line\n";
done;
echo $tmp;

BASH CMD RESULT(在命令行上调用远程bash脚本)

$> ssh x.x.x.x cat < /tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt " | /path/to/bash/script.sh;"
Bar\n
$> 

Python 代码

import shlex
import subprocess

fn = '/tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt'
s = """                                                                    
ssh x.x.x.x cat < {localfile} '| /path/to/bash/script.sh;'
""".format(localfile=fn)

print s

lexer = shlex.shlex(s)                                                     
lexer.quotes = "'"                                                         
lexer.whitespace_split = True                                              
sbash = list(lexer)                                                        
print sbash                                                                

# print buildCmd                                                           
proc=subprocess.Popen(sbash,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 
out,err=proc.communicate()                                                 

print "Out: " + out                                                        
print "Err: " + err                                                        

PYTHON脚本结果:

$> python rt.py

    ssh x.x.x.x cat < /tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt '| /path/to/bash/script.sh'
['ssh', 'x.x.x.x', 'cat', '<', '/tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt', "'| /path/to/bash/script.sh'"]
Out: 
Err: bash: /tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt: No such file or directory
$>

我错过了什么?


你的Python脚本为什么在参数列表中多了一个“<'”,而在Bash脚本中似乎没有出现?(或者是我理解有误?) - mgilson
我错过了,打错字了。我通过在bash命令行中放置<符号进行了更正。 - Chris
你的/tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt文件是在本地还是远程系统上?如果是本地的话,我认为我会以不同的方式运行你的命令,如cat /tmp/bef69a1d-e580-5780-8963-6a9b950e529f.txt | ssh x.x.x.x /path/to/bash/script.sh。无需在远程端运行cat。(我不确定这是否能解决你的Python问题。) - Blckknght
@Blckknght 在这个例子中使用了UUOC(无用的cat)。ssh x.x.x.x /path/to/bash/script.sh < /tmp/bef...与您上面写的语句是等效的。 - mgilson
1个回答

4
问题在于你在命令中使用了 shell 重定向,但是当使用 subprocess 时没有生成 shell。
考虑下面这个(非常简单的)程序:
import sys
print sys.argv

现在,如果我们像你使用ssh一样运行它(假设foofile.txt存在),我们将得到以下结果:
python argcheck.py ssh cat < foofile.txt " | /path/to/bash/script.sh;"
['argcheck.py', 'ssh', 'cat', ' | /path/to/bash/script.sh;']

请注意,< foofile.txt没有出现在Python的命令行参数中。这是因为Bash解析器拦截了<及其后面的文件,并将该文件的内容重定向到您的程序的stdin中。换句话说,ssh从stdin读取文件。同样使用Python将文件传递给ssh的stdin。
s = """                                                                    
ssh x.x.x.x cat '| /path/to/bash/script.sh;'
"""

#<snip>

proc=subprocess.Popen(sbash,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
                            stdin=subprocess.PIPE)
out,err=proc.communicate(open(fn).read())

预计会起作用。


以下对我有效:

import subprocess
from subprocess import PIPE

with open('foo.h') as f:
    p = subprocess.Popen(['ssh','mgilson@XXXXX','cat','| cat'],stdin=f,stdout=PIPE,stderr=PIPE)
    out,err = p.communicate()
    print out
    print '#'*80
    print err

bash中,相应的命令为:

ssh mgilson@XXXXX cat < foo.h '| cat'

其中foo.h是我本地机器上的一个文件。


很好,我从来没有想过使用communicate()作为我的stdin。现在我确实得到了一个不同的错误输出:Out: Err: bash: cat | /path/to/bash/script.sh: 没有这个文件或目录看起来它正在寻找我的本地机器,并且失败了。 - Chris
@Chris -- 我认为你可以直接将打开的文件对象传递给 Popen 构造函数中的 stdin。这可能是一种更加简洁的方法。 - mgilson
同样的事情。错误:bash: cat | /path/to/bash/script.sh:没有这个文件或目录。 - Chris
你太棒了,谢谢,它运行正常。我真的很感激你为此付出的努力。 :) - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接