One place for hosting & domains

      запуска

      Как использовать субпроцесс для запуска внешних программ в Python 3


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Python 3 включает модуль subprocess для запуска внешних программ и чтения их выводов в коде Python.

      Модуль subprocess может оказаться вам полезен, если вы хотите использовать на своем компьютере другую программу из вашего кода Python. Например, вы можете запустить git из своего кода Python для извлечения в ваш проект, отслеживаемый в системе управления версиями git. Поскольку с помощью модуля subprocess можно контролировать любую программу, доступную на вашем компьютере, приведенные здесь примеры применимы к любым внешним программам, которые вы можете запустить из своего кода Python.

      subprocess содержит несколько классов и функций, но в этом обучающем модуле мы расскажем об одной из самых полезных функций модуля subprocess: subprocess.run. Мы изучим разные схемы его применения и основные аргументы ключевых слов.

      Предварительные требования

      Чтобы получить максимум знаний из этого обучающего модуля, рекомендуется иметь небольшое представление о программировании на Python 3. Дополнительную информацию можно найти в следующих обучающих руководствах:

      Запуск внешней программы

      Теперь вы можете использовать функцию subprocess.run для запуска внешней программы с кода Python. Прежде всего необходимо импортировать в программу модули subprocess и sys:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "print('ocean')"])
      

      Если вы запустите их, вы увидите следующий вывод:

      Output

      ocean

      Рассмотрим этот пример:

      • sys.executable — абсолютный путь к исполняемому файлу Python, из которого изначально была запущена ваша программа. Например, sys.executable может быть путем, таким как /usr/local/bin/python.
      • subprocess.run получает список строк, состоящих из компонентов команды, которую мы пытаемся запустить. Поскольку первой строкой мы передаем sys.executable, мы предписываем subprocess.run запустить новую программу Python.
      • Компонент -c — это опция командной строки python, позволяющая передать строку с целой программой Python для исполнения. В нашем случае мы передаем программу, которая распечатывает строку ocean.

      Вы можете представить каждую запись в списке, отправляемом в subprocess.run, как отделенную пробелом. Например, [sys.executable, "-c", "print('ocean')"] примерно эквивалентно /usr/local/bin/python -c "print('ocean')". Обратите внимание, что subprocess автоматически заключает в кавычки компоненты команды перед их запуском в операционной системе, так что вы можете передать имя файла с пробелами.

      Предупреждение. Никогда не передавайте в subprocess.run строки из ненадежных источников. Поскольку subprocess.run может выполнять на компьютере произвольные команды, злоумышленники могут использовать его для манипуляции компьютером непредвиденным образом.

      Захват вывода внешней программы

      Теперь мы можем вызывать внешнюю программу subprocess.run. Далее мы покажем, как захватывать вывод этой программы. Например, этот процесс может быть полезным, если бы мы хотели использовать файлы git ls для вывода всех файлов, хранящихся в системе контроля версий.

      Примечание. Для примеров, показанных в этом разделе, требуется версия Python 3.7 или выше. В частности, в версию Python 3.7, выпущенную в июне 2018 г. были добавлены аргументы capture_output и ключевое слово text.

      Дополним наш предыдущий пример:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      stdout: ocean stderr:

      Этот пример в целом аналогичен рассмотренному в первом разделе, и мы все еще запускаем субпроцесс для печати ocean. Однако теперь мы передаем аргументы ключевых слов capture_output=True и text=True в subprocess.run.

      subprocess.run возвращает объект subprocess.CompletedProcess, который привязан к результату. Объект subprocess.CompletedProcess содержит детали о коде выхода внешней программы и ее выводе. Благодаря capture_output=True в result.stdout и result.stderr вносится соответствующий вывод внешней программы. По умолчанию result.stdout и result.stderr привязаны как байты, но аргумент ключевого слова text=True предписывает Python декодировать байты в строки.

      В разделе вывода stdout — это ocean (плюс новая строка в конце, где print добавляет явность), и у нас нет stderr.

      Рассмотрим пример, где значение stderr не пустое:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
      )
      print("stdout:", result.stdout)
      print("stderr:", result.stderr)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

      Этот код запускает субпроцесс Python, который немедленно выдает ошибку ValueError. При просмотре окончательного результата мы не видим ничего в stdout и видим результат Traceback ошибки ValueError в stderr. Это связано с тем, что по умолчанию Python записывает Traceback необработанного исключения в stderr.

      Создание исключения при коде ошибки выхода

      Иногда бывает полезно создать исключение, если при выходе из работающей программы выдается код ошибки выхода. Программы, выдающие нулевой код ошибки при выходе считаются успешными, однако при ненулевом коде ошибки выхода считается, что в программе произошла ошибка. Например, данная система может быть полезной, если нам нужно выдавать исключение при запуске файлов git ls в каталоге, не являющемся репозиторием git.

      Мы можем использовать аргумент ключевого слова check=True, чтобы subprocess.run выдавал исключение в случае возврата внешней программой ненулевого кода ошибки выхода:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
      

      Если запустить этот код, вывод будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Этот вывод показывает, что мы запустили субпроцесс, выдавший ошибку, которая выводится в stderr на нашем терминале. Затем subprocess.run добросовестно выдал ошибку subprocess.CalledProcessError от нашего имени в нашей основной программе Python.

      Также модуль subprocess включает метод subprocess.CompletedProcess.check_returncode, который можно использовать с похожим результатом:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
      result.check_returncode()
      

      Если мы запустим этот код, мы получим:

      Output

      Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

      Поскольку мы не передали check=True в subprocess.run, мы успешно привязали экземпляр subprocess.CompletedProcess к результату, хотя наша программа выполнила выход с ненулевым кодом ошибки. При этом при вызове result.check_returncode() выдается ошибка subprocess.CalledProcessError, поскольку модуль обнаруживает код ошибки выхода для завершенного процесса.

      Использование timeout для раннего выхода из программ

      subprocess.run имеет аргумент timeout, позволяющий остановить внешнюю программу, если ее выполнение занимает слишком много времени:

      import subprocess
      import sys
      
      result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
      

      Если запустить этот код, результат будет выглядеть следующим образом:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

      Субпроцесс, который мы попытались запустить, использовал функцию time.sleep для сна в течение 2 секунд. Однако мы передали аргумент с ключевым словом timeout=1 в subprocess.run, чтобы закрыть наш субпроцесс по таймауту по прошествии 1 секунды. Это объясняет, почему при вызове subprocess.run в конечном итоге было выдано исключение subprocess.TimeoutExpired.

      Обратите внимание, что аргумент ключевого слова timeout для subprocess.run является приблизительным. Python постарается остановить субпроцесс после заданного в аргументе timeout количества секунд, но это не обязательно будет точным.

      Передача ввода в программы

      Иногда программы ожидают передачи ввода через stdin.

      Аргумент ключевого слова input для subprocess.run позволяет передавать данные в stdin субпроцесса. Например:

      import subprocess
      import sys
      
      result = subprocess.run(
          [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
      )
      

      Запустив этот код, мы получим примерно следующий вывод:

      Output

      underwater

      В данном случае мы передали байты underwater в input. Наш целевой субпроцесс использовал sys.stdin для чтения переданного в stdin аргумента (underwater) и вывел его в результатах.

      Аргумент ключевого слова input может быть полезен, если вы хотите создать цепочку из нескольких вызовов subprocess.run с передачей вывода одной программы как ввода в другую программу.

      Заключение

      Модуль subprocess — это мощный компонент стандартной библиотеки Python, позволяющий легко запускать внешние программы и просматривать их вывод. В этом обучающем модуле вы научились использовать subprocess.run для управления внешними программами, передачи ввода в эти программы, анализа их вывода и проверки возвращаемых ими кодов.

      Модуль subprocess представляет дополнительные классы и утилиты, которые мы не охватили в этом обучающем модуле. Изучив основы, вы можете использовать документацию модуля subprocess для получения дополнительной информации о других имеющихся классах и утилитах.



      Source link

      Использование nsh для запуска защищенных команд в Ubuntu 18.04


      Введение

      Часто бывает трудно управлять несколькими машинами на ежедневной основе. Хотя Security Shell (SSH) — это хороший инструмент для удаленного доступа, сам протокол имеет определенные недостатки в отношении удобства и безопасности.

      Например, удаленные компьютеры должны иметь публичный IP-адрес и порт для перенаправления, через которые можно получить к ним доступ, в результате чего они будут доступны из сети Интернет или, по крайней мере, из крупной сети. Это особенно касается случаев, когда вы используете пароль для аутентификации вместо пары ключей (публичного и частного). Кроме того, если вы заранее не будете знать публичный ключ удаленной машины, вы можете стать объектом атаки через посредника. Многие удаленные компьютеры, к которым требуется доступ, либо не имеют публичного IP-адреса, либо имеют динамический IP-адрес, который вы, возможно, не знаете.

      Кроме того, SSH требует отдельного подключения для каждого сеанса удаленного подключения. Если пользователю потребуется запустить одну команду на сотнях или тысячах машин, потребуется сначала создать соединение для каждой машины с помощью TCP рукопожатия, что не очень эффективно.

      NKN Shell, или nsh, — это альтернатива SSH, которая обеспечивает удобный и безопасный способ запуска удаленных команд. nsh использует глобальную публичную сеть NKN, которая обеспечивает защищенную и децентрализованную передачу данных. Архитектура использует уникальные адреса, которые содержат публичный ключ, используемый как для маршрутизации, так и для сквозного шифрования без какой-либо инфраструктуры открытых ключей (PKI). Сеть также не требует, чтобы удаленный сервер имел публичный IP-адрес. Удаленный сервер должен только иметь доступ к Интернету и иметь возможность устанавливать исходящие подключения через HTTP и websocket. В результате ваши удаленные машины оказываются закрыты от внешнего Интернета.

      В этом руководстве мы будем использовать приложения демон оболочки NKN ​​и клиент Xterm оболочки NKN для выполнения команд на удаленной машине. Для это необходимо установить и настроить демон NKN Shell на удаленной машине с доступом в Интернет, создать пару ключей и установить подключение с помощью клиента.

      Предварительные требования

      Для выполнения этого руководства вам потребуется следующее:

      Шаг 1 — Настройка демона NKN Shell на удаленном сервере

      Во-первых, установите демон NKN shell (nsd) на вашем сервере. Это приложение будет вызывать nkn-multiclient для подключения к публичной сети NKN и получения адреса для маршрутизации. Затем демон будет прослушивать входящие команды оболочки от аутентифицированных и добавленных в белый список клиентов, выполнять эти команды и затем отправлять результаты назад.

      Начнем с загрузки последней версии предварительно подготовленного бинарного файла nshd из GitHub:

      • wget https://github.com/nknorg/nkn-shell-daemon/releases/latest/download/linux-amd64.tar.gz

      Распакуйте файл:

      • tar -zxvf linux-amd64.tar.gz

      Затем переместите файлы в директорию /usr/local/bin, чтобы они были доступны в рамках всей системы:

      • sudo mv ./linux-amd64/* /usr/local/bin/

      Затем вы должны будете настроить запуск процесса-демона с помощью Systemd, чтобы активировать перезапуск при перезагрузке сервера.

      Создайте файл с названием nshd.service в /etc/systemd/system:

      • sudo nano /etc/systemd/system/nshd.service

      Добавьте следующее определение службы в файл для настройки службы:

      /etc/systemd/system/nshd.service

      [Unit]
      Description=NKN Shell Daemon
      After=network.target
      
      [Service]
      Type=simple
      User=root
      Group=root
      Restart=always
      ExecStart=/usr/local/bin/nshd
      
      [Install]
      WantedBy=multi-user.target
      

      Узнайте больше о юнит-файлах Systemd в статье Знакомство с юнитами и юнит-файлами Systemd.

      Сохраните файл и выйдите из редактора. Затем активируйте и запустите службу nshd с помощью следующих команд:

      • sudo systemctl enable nshd.service
      • sudo systemctl start nshd.service

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

      • sudo systemctl status nshd.service

      Вы увидите, что статус активный:

      Output

      ● nshd.service - NKN Shell Daemon Loaded: loaded (/etc/systemd/system/nshd.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-02-19 19:16:02 UTC; 7s ago Main PID: 3457 (nshd) Tasks: 10 (limit: 1152) CGroup: /system.slice/nshd.service └─3457 /usr/local/bin/nshd Feb 19 19:16:02 your_hostname systemd[1]: Started NKN Shell Daemon. Feb 19 19:16:03 your_hostname nshd[3457]: Create directory /etc/nshd/ Feb 19 19:16:03 your_hostname nshd[3457]: Create password and save to file /etc/nshd/wallet.pswd Feb 19 19:16:03 your_hostname nshd[3457]: Create wallet and save to file /etc/nshd/wallet.json Feb 19 19:16:03 your_hostname nshd[3457]: Create authorized pubkeys file /etc/nshd/authorized_pubkeys Feb 19 19:16:03 your_hostname nshd[3457]: Listening at d46567b883a3070ee3fe879d9fa2d5dc55a95f79ff2797c42df36c6979e5c4Aba

      Чтобы подключиться к вашему серверу, вам потребуется его адрес NKN, который можно найти в выводе предыдущей команды. Также вы можете запустить следующую команду для получения адреса:

      В результате вы получите нужный вам адрес:

      Output

      e70ca28ede84fc0659f2869255e8a393aef35b4fa5a7e036f29127c7dba75383

      Запишите этот адрес, так как он потребуется вам для подключения к вашему серверу.

      Теперь, когда демон запущен и работает, вы можете настроить веб-клиент для коммуникации с сервером.

      Шаг 2 — Настройка разрешений для клиента оболочки NKN

      Вам потребуется совместимый клиент, который может подключаться к удаленной машине. В этом обучающем модуле мы будем использовать клиент Xterm оболочки NKN, веб-клиент оболочки NKN. Вы можете воспользоваться несколькими способами:

      • Используйте версию размещенную на странице https://nsh.nkn.org/. Обратите внимание, что хотя эта веб-страница размещается на сервере, на самом деле, это чистое локальное веб-приложение, которое запускается в вашем браузере.
      • Получите исходный код и разместите его у себя.
      • Воспользуйтесь расширением nShell для Chrome.

      В этом руководстве вы будете использовать в размещенную на сервере версию. Откройте браузер на локальном компьютере и перейдите на страницу https://nsh.nkn.org. Вы увидите экран приветствия:

      Клиент оболочки

      Нажмите Сгенерировать новую пару ключей. Ваши ключи будут сгенерированы и отображены, как показано на изображении ниже:

      Сгенерированная пара ключей

      Примечание: при создании новой пары ключей вы увидите secret seed. Сохраните этот secret seed в защищенном и безопасном месте, как и в случае в приватным ключом SSH. Любой, кто владеет этим secret seed, может использовать его для восстановления публичного ключа и запуска команд на удаленных машинах. Ваш браузер будет запоминать этот seed, но вам следует скопировать его в безопасное место, чтобы вы снова могли использовать его на новой машине.

      Сохраните secret seed в безопасном месте. Вы можете использовать его позже для восстановления публичного ключа, чтобы вы могли подключиться с другой клиентской машины.

      Поскольку это новая пара ключей, вы должны добавить публичный ключ в файл /etc/nshd/authorized_pubkeys​​​ на вашем сервере.

      /etc/nshd/authorized_pubkeys играет ту же роль, что и файл ~/authorized_keys, который определяет, какие публичные ключи SSH можно использовать для входа. Файл authorized_pubkeys может указать, какой пользователь связан с ключом. В целях безопасности вы можете выполнить вход с помощью пользователя без прав root, используемого в данном руководстве, так что вы будете привязывать сгенерированный публичный ключ к пользователю sammy, созданному согласно указаниям руководства по начальной настройке сервера.

      Для привязки пользователя к публичному ключу вам потребуется идентификатор пользователя (UID) и идентификатор группы (GID) этого пользователя. Выполните следующую команду на вашем сервере во время входа с помощью пользователя sammy:

      Вы увидите UID и GID пользователя:

      Output

      uid=1000(sammy) gid=1000(sammy) groups=1000(sammy),27(sudo)

      Откройте файл authorized_pubkeys​​​ в своем редакторе:

      • sudo nano /etc/nshd/authorized_pubkeys

      Добавьте одну строку, содержащую публичный ключ, uid и gid, разделенные пробелами:

      authorized_pubkeys

      5d5367a5730427c205904a4457392051d2045dbce0186518fb6eb24dd9e41ba6 1000 1000
      

      Сохраните файл.

      Убедитесь в корректности содержимого файла:

      • cat /etc/nshd/authorized_pubkeys

      Вы увидите ваш ключ, выведенный на экране:

      Output

      5d5367a5730427c205904a4457392051d2045dbce0186518fb6eb24dd9e41ba6 1000 1000

      Затем перезапустить демон nshd для применения изменений:

      • sudo systemctl restart nshd.service

      Теперь давайте проведем тест с помощью подключения к серверу и запуска команды.

      Шаг 3 — Отправка команды удаленной машины и получение ответа

      В клиенте оболочки NKN введите ваш удаленный nshd адрес, полученный в шаге 1, а также опциональный идентификатор клиента:

      Сайт nsh с предоставленным удаленным адресом

      Нажмите Подключиться для установки соединения.

      Вы подключитесь к удаленному компьютеру и увидите в браузере командную строку терминала. Здесь вы можете использовать его также, как и в случае с SSH. Например, выполните следующую команду для перехода в каталог /etc/nshd:

      Затем получите список его содержимого:

      Вы увидите содержимое каталога:

      Output

      authorized_pubkeys wallet.json wallet.pswd

      Вы можете отключиться с помощью команды exit. Когда вам потребуется перезапустить подключение, повторно откройте веб-интерфейс и введите данные для подключения. Если вы создадите новую пару ключей, вам потребуется добавить новый публичный ключ в ваш сервер.

      Заключение

      В этом обучающем модуле мы установили и настроили nsh для безопасной и удобной отправки команд на удаленный компьютер. nsh — это отличный способ доступа к удаленным компьютерам, когда требуется быстро запускать команду для получения последнего статуса службы или просмотра ряда настроек конфигурации. Это приложение работает на базе глобальной публичной сети NKN и может свободно использоваться, поэтому вы можете включить его в ваше приложение или рабочий процесс.

      Также вы можете ознакомиться с nkn-tunnel, приложением, которое поддерживает SSH или любые другие приложения, основанные на TCP.



      Source link