在资源受限的计算机上启动子流程

时间:2018-08-12 01:03:40

标签: python bash ssh

编辑:

这个问题的初衷是寻找一种通过Python脚本启动交互式ssh会话的方法。我之前尝试过subprocess.call(),并在所有内容输出到终端之前都得到了Killed响应。我只是认为这是subprocess模块的问题/局限性,而不是其他地方的问题。当我在非资源受限的计算机上运行脚本时,情况并非如此,并且运行良好。

然后,问题变成了:如何在资源受限的情况下运行交互式ssh会话?

向查尔斯·达菲(Charles Duffy)大喊大叫,他在尝试诊断所有这些方面都提供了巨大帮助。

以下是原始问题:

背景:

所以我有一个当前用bash编写的脚本。它解析一些控制台功能的输出,然后基于这些解析的输出打开ssh会话。

它目前可以正常工作,但是我想通过添加一些标志参数来扩展它的功能。我之前曾与argparse合作,并且非常喜欢它。我尝试用bash做一些标志工作,而我们只说这还有很多不足之处。

实际问题:

是否可以让python在控制台 中进行处理,然后将用户置于该控制台中?

类似于使用subprocess在当前查看的控制台上运行一系列命令吗?这与subprocess的正常运行方式相反,在正常情况下,该命令运行命令,然后关闭中间控制台

特定示例,因为我不确定所描述的内容是否有意义:

所以这是我想要的功能的基本清单:

  1. 运行python脚本
  2. 让该脚本运行一些控制台命令并解析输出
  3. 运行以下命令:

    ssh -t $correctnode "cd /local_scratch/pbs.$jobid; bash -l"

此命令将SSH到$correctnode,更改目录,然后在该节点中打开bash窗口。

我已经知道如何做第1部分和第2部分。这是我不知道的第三部分。任何帮助将不胜感激。

编辑:与this question不同,我不是只是尝试运行命令。我正在尝试显示由命令创建的外壳。具体来说,我想显示通过ssh命令创建的bash shell。

2 个答案:

答案 0 :(得分:1)

这绝不是真正的答案,只是我的评论的一个例证。

假设您有一个Python脚本 test.py

import argparse


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('myarg', nargs="*")
    args = parser.parse_args()

    print("echo Hello world! My arguments are: " + " ".join(args.myarg))

因此,您可以围绕它创建一个bash包装器, test.sh

set -e
$(python test.py $*)

这就是你得到的:

$ bash test.sh
Hello world! My arguments are:
$ bash test.sh one two
Hello world! My arguments are: one two

这是怎么回事:

  • python脚本不执行命令。而是输出bash脚本将运行的命令(在此示例中为echo)。根据您的情况,最后一个命令将是ssh blabla
  • bash执行python脚本($(...)部分)的输出,并传递其所有参数($*部分)
  • 您可以在python脚本中使用argparse;如果自变量有任何问题,该消息将被放入stderr,并且不会由bash执行; bash脚本将由于set -e标志而停止

答案 1 :(得分:1)

读者背景

OP在非常受资源限制(尤其是受过程限制)的Jumphost框上运行,在其中,ssh的子过程开始的python进程超出了相关限制(可能有多少个进程?)


方法A:用交互式流程替换Python解释器

使用exec*()系列的系统调用会导致您的原始进程不再在内存中(不同于用于在保持父进程运行的同时启动子进程的fork()+exec*()组合),因此不会不会计入帐户的限额。

import argparse
import os

try:
    from shlex import quote
except ImportError:
    from pipes import quote

parser = argparse.ArgumentParser()
parser.add_argument('node')
parser.add_argument('jobid')
args = parser.parse_args()

remote_cmd_str = 'cd /local_scratch/pbs.%s && exec bash -i' % (quote(args.jobid))
local_cmd = [
  '/usr/bin/env', 'ssh', '-tt', node, remote_cmd_str
]
os.execv("/usr/bin/env", local_cmd)

方法B:从Python生成Shell命令

如果我们使用Python生成shell命令,则只有在Python进程退出后,shell才能调用该命令,这样我们就不会受到外部强制的进程限制。

首先,一种更可靠的方法来生成可eval的输出:

import argparse

try:
    from shlex import quote
except ImportError:
    from pipes import quote

parser = argparse.ArgumentParser()
parser.add_argument('node')
parser.add_argument('jobid')
args = parser.parse_args()

remoteCmd = ['cd', '/local_scratch/pbs.%s' % (args.jobid)]
remoteCmdStr = ' '.join(quote(x) for x in remoteCmd) + ' && bash -l'
cmd = ['ssh', '-t', args.correctnode, remoteCmdStr]
print(' '.join(pipes.quote(x) for x in cmd)

如果上面的名称为genSshCmd,则要从shell运行此命令:

#!/bin/sh
eval "$(genSshCmd "$@")"

请注意,这里有两个单独的引用层:一层用于运行eval的本地shell,第二层用于由SSH启动的远程shell。这很关键-您不希望$(rm -rf ~)的Jobid实际调用rm