Выполнение функции локальной оболочки на удаленном хосте через 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()