Выполнение функции локальной оболочки на удаленном хосте через ssh с использованием Python

Мой .profile определяет функцию

myps () {
        ps -aef|egrep "a|b"|egrep -v "c\-"
}

Я хотел бы выполнить его из моего скрипта Python

import subprocess
subprocess.call("ssh user@box \"$(typeset -f); myps\"", shell=True)

Возвращаем ошибку

bash: -c: line 0: syntax error near unexpected token `;'
bash: -c: line 0: `; myps'

Побег; результаты в

bash: ;: command not found

3 ответа

Решение

Первоначальная команда не интерпретировала ; до myps должным образом. С помощью sh -c исправляет это, но... (см. комментарии Чарльза Даффи ниже).

Использование комбинации одинарных / двойных кавычек иногда делает синтаксис более легким для чтения и менее подверженным ошибкам. Имея это в виду, безопасный способ запуска команды (при условии, что функции в .profile фактически доступны в оболочке, запущенной объектом subprocess.Popen):

subprocess.call('ssh user@box "$(typeset -f); myps"', shell=True),  

Альтернативным (менее безопасным) методом будет использование sh -c для команды subshell:

subprocess.call('ssh user@box "sh -c $(echo typeset -f); myps"', shell=True) 
# myps is treated as a command

Это, казалось бы, вернуло тот же результат:

subprocess.call('ssh user@box "sh -c typeset -f; myps"', shell=True) 

Определенно есть альтернативные методы для выполнения задач такого типа, однако, это может дать вам представление о том, в чем проблема исходной команды.

script='''
. ~/.profile # load local function definitions so typeset -f can emit them
ssh user@box ksh -s <<EOF
$(typeset -f)
myps
EOF
'''

import subprocess
subprocess.call(['ksh', '-c', script]) # no shell=True

Здесь есть несколько подходящих предметов:

  • Точечный файл, определяющий эту функцию, должен быть вызван локально перед запуском typeset -f сбросить определение функции по проводам. По умолчанию неинтерактивная оболочка не запускает большинство файлов точек (любой из указанных ENV переменная окружения является исключением).

    В данном примере это обслуживается . ~/profile Команда в сценарии.

  • Оболочка должна быть одной поддерживающей typesetтак должно быть bash или же kshне sh (как используется script=True по умолчанию), который может быть предоставлен ash или же dashне хватает этой функции.

    В данном примере это передается ['ksh', '-c'] это первые два аргумента массива argv.

  • typeset должен быть запущен локально, поэтому он не может находиться в позиции argv, кроме первой с script=True, (Чтобы привести пример: subprocess.Popen(['''printf '%s\n' "$@"''', 'This is just literal data!', '$(touch /tmp/this-is-not-executed)'], shell=True) оценивает только printf '%s\n' "$@" как сценарий оболочки; This is just literal data! а также $(touch /tmp/this-is-not-executed) передаются как литеральные данные, поэтому ни один файл с именем /tmp/this-is-not-executed создано).

    В данном примере это обсуждается не используя script=True,

  • Явное обращение ksh -s (или же bash -s(при необходимости) гарантирует, что оболочка, оценивающая ваши определения функций, соответствует оболочке, для которой вы написали эти функции, а не передает их sh -c, как было бы иначе.

    В данном примере это обслуживается ssh user@box ksh -s внутри сценария.

Я закончил тем, что использовал это.

import subprocess
import sys
import re

HOST = "user@" + box
COMMAND = 'my long command with many many flags in single quotes'

ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
                       shell=False,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
Другие вопросы по тегам