Как вы выполняете несколько команд за один сеанс в Paramiko? (Питон)

def exec_command(self, command, bufsize=-1):
    #print "Executing Command: "+command
    chan = self._transport.open_session()
    chan.exec_command(command)
    stdin = chan.makefile('wb', bufsize)
    stdout = chan.makefile('rb', bufsize)
    stderr = chan.makefile_stderr('rb', bufsize)
    return stdin, stdout, stderr

При выполнении команды в paramiko всегда сбрасывает сессию при запуске exec_command. Я хочу иметь возможность выполнять sudo или su и по-прежнему иметь эти привилегии, когда я запускаю другую команду exec_command. Другим примером может быть попытка выполнить exec_command("cd /"), а затем снова запустить exec_command и сделать так, чтобы он находился в корневом каталоге. Я знаю, что вы можете сделать что-то вроде exec_command("cd /; ls -l"), но мне нужно сделать это в отдельных вызовах функций.


person Takkun    schedule 01.06.2011    source источник
comment
Возможный дубликат Реализуйте интерактивную оболочку через ssh в Python, используя Парамико?   -  person misha    schedule 15.05.2018


Ответы (7)


Неинтерактивные варианты использования

Это неинтерактивный пример... он отправляет cd tmp, ls, а затем exit.

import sys
sys.stderr = open('/dev/null')       # Silence silly warnings from paramiko
import paramiko as pm
sys.stderr = sys.__stderr__
import os

class AllowAllKeys(pm.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        return

HOST = '127.0.0.1'
USER = ''
PASSWORD = ''

client = pm.SSHClient()
client.load_system_host_keys()
client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST, username=USER, password=PASSWORD)

channel = client.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')

stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()

stdout.close()
stdin.close()
client.close()

Интерактивные варианты использования
Если у вас интерактивный вариант использования, этот ответ не поможет... Лично я бы использовал pexpect или exscript для интерактивных сеансов.

person This    schedule 01.06.2011
comment
Но это решение не позволяет читать вывод первой команды до завершения всех команд. Я прав? - person Nikolai Golub; 29.08.2014
comment
это не работает, потому что stdout.read() читает весь файл. Это означает, что он читает программу, которая была введена в терминал. Это не предполагаемое поведение. Как вы читаете только вывод ls вместо всей программы и вывода ls? - person Jenia Ivanov; 18.06.2015
comment
Я согласен с двумя комментариями выше... Есть ли способ прочитать вывод одной команды до того, как все команды будут завершены? - person dmranck; 29.10.2015
comment
@Mike Pennington Привет, чувак! спасибо за ответ, на самом деле он работает хорошо, однако есть некоторые проблемы с командами sudo, не могли бы вы взглянуть на мой вопрос, пожалуйста? stackoverflow.com/questions/34068218 / - person Leo; 03.12.2015
comment
Привет, у меня есть запрос. Я также столкнулся с той же проблемой и хочу запустить несколько команд. Как я могу передать значение динамически в stdin.write(''' ''')? Я хочу сделать так: cmd=mkdir что-то stdin.write(''' ‹cmd›''') заранее спасибо. - person fresher; 24.12.2015
comment
У меня после stdout.read() висит на неопределенный срок. Есть идеи, почему? - person Matthew Moisen; 26.01.2016
comment
@MatthewMoisen: вы должны использовать команду выхода как последнюю команду. - person Q Caron; 16.06.2016
comment
Работает ли это в случае, скажем, «sudo -u user -i», а затем сохраняет эти привилегии для запуска другой команды, которая выполняет сценарий bash. Я попробовал это, и это, казалось, не работало. Кто-нибудь может помочь? - person Surya Sekhar Mondal; 03.12.2019
comment
@SuryaSekharMondal, вы не можете запустить команду sudo (или иным образом изменить оболочку) внутри stdin.write(), потому что вы отключите стандартный ввод и стандартный вывод с помощью нового sudo или /bin/sh. Этот метод основан на постоянном использовании stdout и stdin во всем скрипте. - person This; 03.12.2019
comment
@MikePennington Хорошо. Итак, нет ли способа выполнить sudo, а затем выполнить сценарий bash с теми же привилегиями на том же канале? - person Surya Sekhar Mondal; 04.12.2019
comment
@SuryaSekharMondal, рассмотрите возможность использования pexpect для вашего варианта использования. sudo с pexpect существенно проще. FWIW, у меня есть pexpect tutorial на моем личном веб-сайте. - person This; 04.12.2019

Попробуйте создать командную строку, разделенную символом \n. Это сработало для меня. За. например ssh.exec_command("command_1 \n command_2 \n command_3")

person Sandeep    schedule 21.07.2015

Строго говоря, нельзя. Согласно спецификации ssh:

Сеанс — это удаленное выполнение программы. Программа может быть оболочкой, приложением, системной командой или некоторой встроенной подсистемой.

Это означает, что после выполнения команды сеанс завершается. Вы не можете выполнять несколько команд в одном сеансе. Однако вы МОЖЕТЕ запустить удаленную оболочку (== одна команда) и взаимодействовать с этой оболочкой через стандартный ввод и т. д. (подумайте о выполнении скрипта Python вместо запуска интерактивного интерпретатора)

person Steven    schedule 01.06.2011
comment
SSH RFC не говорит о том, должна ли сессия завершаться сразу после выполнения команды. Если вы просматривали большинство ssh-клиентов, они продолжают открывать Exec/Shell после установки сеанса. Пользователю разрешено вводить любую числовую команду. Когда пользовательские типы выходят, завершается только сеанс. - person Sujal Sheth; 29.10.2014

Вы можете сделать это, вызвав оболочку на клиенте и отправив команды. См. здесь
страница имеет код для python 3.5. Я немного изменил код, чтобы он работал с pythin 2.7. Добавление кода здесь для справки

import threading, paramiko

strdata=''
fulldata=''

class ssh:
    shell = None
    client = None
    transport = None

    def __init__(self, address, username, password):
        print("Connecting to server on ip", str(address) + ".")
        self.client = paramiko.client.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
        self.client.connect(address, username=username, password=password, look_for_keys=False)
        self.transport = paramiko.Transport((address, 22))
        self.transport.connect(username=username, password=password)

        thread = threading.Thread(target=self.process)
        thread.daemon = True
        thread.start()

    def close_connection(self):
        if(self.client != None):
            self.client.close()
            self.transport.close()

    def open_shell(self):
        self.shell = self.client.invoke_shell()

    def send_shell(self, command):
        if(self.shell):
            self.shell.send(command + "\n")
        else:
            print("Shell not opened.")

    def process(self):
        global strdata, fulldata
        while True:
            # Print data when available
            if self.shell is not None and self.shell.recv_ready():
                alldata = self.shell.recv(1024)
                while self.shell.recv_ready():
                    alldata += self.shell.recv(1024)
                strdata = strdata + str(alldata)
                fulldata = fulldata + str(alldata)
                strdata = self.print_lines(strdata) # print all received data except last line

    def print_lines(self, data):
        last_line = data
        if '\n' in data:
            lines = data.splitlines()
            for i in range(0, len(lines)-1):
                print(lines[i])
            last_line = lines[len(lines) - 1]
            if data.endswith('\n'):
                print(last_line)
                last_line = ''
        return last_line


sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"


connection = ssh(sshServer, sshUsername, sshPassword)
connection.open_shell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata)    # print the last line of received data
print('==========================')
print(fulldata)   # This contains the complete data received.
print('==========================')
connection.close_connection()
person Nagabhushan S N    schedule 22.01.2018
comment
Вы хотели ввести sendShell вместо send_shell? Или send_shell встроен в вызываемый вами класс paramiko.ssh? - person Fields; 19.04.2018
comment
@Fields, хороший улов. Я обновил код. Я забыл переименовать sendShell в send_shell в классе ssh. Спасибо! - person Nagabhushan S N; 21.04.2018

Вы можете запустить несколько команд, используя описанную ниже технику. Используйте точку с запятой для разделения команд Linux, например:

chan.exec_command("date;ls;free -m")
person Community    schedule 12.01.2021

Вы можете выполнить весь файл сценария BASH для лучшего использования, вот код для этого:

import paramiko

hostname = "192.168.1.101"
username = "test"
password = "abc123"

# initialize the SSH client
client = paramiko.SSHClient()
# add to known hosts
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
    client.connect(hostname=hostname, username=username, password=password)
except:
    print("[!] Cannot connect to the SSH Server")
    exit()

# read the BASH script content from the file
bash_script = open("script.sh").read()
# execute the BASH script
stdin, stdout, stderr = client.exec_command(bash_script)
# read the standard output and print it
print(stdout.read().decode())
# print errors if there are any
err = stderr.read().decode()
if err:
    print(err)
# close the connection
client.close()

Это запустит локальный script.sh файл на удаленной 192.168.1.101 машине Linux.

script.sh (просто пример):

cd Desktop
mkdir test_folder
cd test_folder
echo "$PATH" > path.txt

В этом руководстве это подробно объясняется: Как выполнять команды BASH в Удаленная машина в Python.

person rockikz    schedule 11.11.2019
comment
На самом деле это не bash-скрипт. Вы выполняете содержимое файла как команду в оболочке пользователя по умолчанию. - person Martin Prikryl; 11.11.2019
comment
Также, используя AutoAddPolicy таким образом, вы теряете защиту от MITM-атак. - person Martin Prikryl; 11.11.2019
comment
@MartinPrikryl Можете ли вы указать разницу между чтением содержимого файла таким образом или выполнением сценария BASH? - person rockikz; 12.11.2019
comment
Кроме того, не могли бы вы рассказать мне, как AutoAddPolicy вызывает это? - person rockikz; 12.11.2019
comment
Я не понимаю, как ваш комментарий относится к моему. Я говорил, что вы выполняете свою команду не в bash, а в пользовательской оболочке по умолчанию (какой может быть любая другая оболочка). - person Martin Prikryl; 12.11.2019
comment
Ваш код будет слепо принимать любой ключ хоста и, что еще хуже, любой измененный ключ хоста. Таким образом, ваш код уязвим для MITM-атак. - person Martin Prikryl; 12.11.2019

Если вы хотите, чтобы каждая команда влияла на следующую команду, вы должны использовать:

stdin, stdout, stderr = client.exec_command("command1;command2;command3")

но в некоторых случаях я обнаружил, что когда ; не работает, использование && работает.

stdin, stdout, stderr = client.exec_command("command1 && command2 && command3")
person Niv Cohen    schedule 20.05.2021