有条件地 运行 通过 ssh 进行子处理,同时将输出附加到(可能是远程的)文件

Conditionally run subprocess over ssh, while appending output to a (potentially remote) file

我有一个脚本可以 运行 在我的主机和其他几台服务器上。我想使用 ssh 在我的主机和远程机器上将此脚本作为后台进程启动,并将 stdout/stderr 输出到我的主机后台进程的主机和远程机器上的远程机器后台任务。

我试过

subprocess.check_output(['python' ,'script.py' ,'arg_1', ' > file.log ', ' & echo -ne $! ']

但它不起作用。它没有给我 pid 也没有写入文件。它适用于 shell=True 但后来我读到出于安全原因使用 shell=True 是不好的。

然后我尝试了

p = subprocess.Popen(['python' ,'script.py' ,'arg_1', ' > file.log ']

现在我可以获取进程 pid,但输出没有写入远程日志文件。

使用如下建议的 stdout/stderr 参数将在我的主机而非远程机器中打开日志文件。我想改为登录远程计算机。 append subprocess.Popen output to file?

有人可以向我推荐一个命令,它既可以在我的主机上运行,​​也可以通过 ssh 连接到远程服务器并在那里启动后台进程吗?并写入输出文件 ?

<HOW_TO_GET_PID> = subprocess.<WHAT>( ([] if 'localhost' else ['ssh','<remote_server>']) + ['python', 'script.py', 'arg_1' <WHAT>] )

有人可以完成上面的伪代码吗?

谢谢,


# At the beginning you can even program automatic daemonizing
# Using os.fork(), otherwise, you run it with something like:
# nohup python run_my_script.py &
# This will ensure that it continues running even if SSH connection breaks.
from subprocess import Popen, PIPE, STDOUT

p = Popen(["python", "yourscript.py"], stdout=PIPE, stderr=STDOUT, stdin=PIPE)
p.stdin.close()
log = open("logfile.log", "wb")
log.write(b"PID: %i\n\n" % p.pid)
while 1:
    line = p.stdout.readline()
    if not line: break
    log.write(line)
    log.flush()

p.stdout.close()
log.write(b"\nExit status: %i" % p.poll())
log.close()

你不会在 one-liner 中得到安全和正确的东西而不让它变得不可读;最好不要尝试。

请注意,我们在这里使用 shell:在本地情况下我们显式调用 shell=True,而在远程情况下 ssh 始终隐式启动 shell.

import shlex
import subprocess

def startBackgroundCommand(argv, outputFile, remoteHost=None, andGetPID=False):
    cmd_str = ' '.join(shlex.quote(word) for word in argv)
    if outputFile != None:
        cmd_str += ' >%s' % (shlex.quote(outputFile),)
    if andGetPID:
        cmd_str += ' & echo "$!"'
    if remoteHost != None:
        p = subprocess.Popen(['ssh', remoteHost, cmd_str], stdout=subprocess.PIPE)
    else:
        p = subprocess.Popen(cmd_str, stdout=subprocess.PIPE, shell=True)
    return p.communicate()[0]

# Run your command locally
startBackgroundCommand(['python', 'script.py', 'arg_1'],
    outputFile='file.log', andGetPID=True)

# Or run your command remotely
startBackgroundCommand(['python', 'script.py', 'arg_1'],
    remoteHost='foo.example.com', outputFile='file.log', andGetPID=True)