Сообщить об ошибке.

Частые вопросы по модулю sh в Python

В разделе рассмотрены частые вопросы, задаваемые начинающими пользователями модуля sh, которые по каким-то причинам небыли освещены в предыдущих разделах.

Содержание:


Будет ли поддерживаться Windows?

Нет никаких планов по поддержке Windows.

Как выполнить встроенный bash?

import sh
sh.bash("-c", "your_builtin")

# или

builtins = sh.bash.bake("-c")
builtins("your_builtin")

Почему символ звездочки * не работает в качестве аргумента команды терминала?

Расширение glob - это функция оболочки, такой как Bash, и выполняется оболочкой перед передачей результатов в программу для выполнения. Поскольку модуль sh - это не оболочка, а инструмент для непосредственного выполнения программ, она не может в принципе обрабатывать расширение терминала glob, как оболочка.

Итак, чтобы использовать символ звездочки*, как в командной строке, сначала необходимо передать ее в функцию Python glob.glob():

import sh
import glob
sh.ls(glob.glob("*.py"))

Как вызвать программу, которая не находится в $PATH?

Используйте конструктор sh.Command() для непосредственного создания экземпляра команды, а затем выполните его:

import sh
cmd = sh.Command("/path/to/command")
cmd("-v", "arg1")

Как выполнить программу с тире в названии?

Если она находится в системном $PATH, то замените тире подчеркиванием:

import sh
# запустит команду `google-chrome http://google.com`
sh.google_chrome("http://google.com")

Примечание. Если в вашей системе существует программа с именем google_chrome, то будет вызываться она вместо google-chrome. В этом случае, чтобы выполнить программу с тире в имени, нужно будет использовать метод, описанный здесь.

Как выполнить программу со специальным символом в ее имени?

Программы с не буквенно-цифровыми символами и без тире в их именах не могут быть выполнены, при обращении к ним точечной нотацией.

Например, это не сработает:

import sh
sh.mkfs.ext4()

В Python такие символы, как точка '.' имеют особое значение, в данном случае доступ к атрибутам. В приведенном выше примере, модуль sh пытается найти программу mkfs (которая может существовать, а может и не существовать), а затем выполнить поиск подкоманды с именем ext4. Другими словами, он попытается вызвать mkfs с аргументом ext4, что, вероятно, не то, что нужно.

Обходной путь - создание экземпляра класса sh.Command() со строкой программы, которую необходимо вызвать:

import sh
mkfsext4 = sh.Command("mkfs.ext4")
mkfsext4()

Почему конвейер не асинхронен по умолчанию?

Существует неочевидная причина, по которой асинхронный конвейер по умолчанию невозможен. Рассмотрим следующий пример:

import sh

sh.cat(sh.echo("test\n1\n2\n3\n"))

При его запуске sh.echo выполняется и завершается, а затем вся выходная строка передается в sh.cat. Очень хотелось, чтобы каждый фрагмент, разделенный новой строкой, передавался в sh.cat постепенно.

Но для того, чтобы в этом примере данные передавались асинхронно от команды echo до cat, команда echo не должна блокироваться. Но как может внутренняя команда знать контекст ее выполнения? Она не может этого знать без чего-то явного.

Вот почему был введен специальный аргумент _piped. По умолчанию выполняемые команды блокируются до тех пор, пока они не будут завершены, поэтому для того, чтобы внутренняя команда не блокировалась, _piped=True сигнализирует внутренней команде, что она не должна блокироваться. Таким образом, запускается внутренняя команда, а вскоре после этого запускается внешняя команда, и обе выполняются одновременно. Затем данные могут передаваться от внутренней команды к внешней команде асинхронно:

import sh

sh.cat(sh.echo("test\n1\n2\n3\n", _piped=True))

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

Как запустить команду и подключить ее к sys.stdout и sys.stdin?

Есть два способа сделать это.

Можно использовать sys.stdin, sys.stdout и sys.stderr в качестве значений специальных аргументов для _in, _out, _err соответственно, и в основном это должно работать:

import sh
import sys
sh.your_command(_in=sys.stdin, _out=sys.stdout)

Но иногда этот код не будет работать по нескольким причинам. Первая причина заключается в том, что sys.stdin, вероятно, является управляющим TTY (прикрепленным к оболочке, запустившей процесс python) и, вероятно, не установлен в необработанном режиме termios(3), это означает, что ввод буферизируется новыми строками.

Настоящее решение - использовать _fg=True:

import sh
sh.top(_fg=True)

В каком порядке указывать параметры команды?

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

bash-command --arg1=val1 arg2 --arg3=val3

Обычно это первая попытка, которую они делают:

sh.bash_command(arg1="val1", "arg2", arg3="val3")

Это не работает, потому что в Python, позиционные аргументы, такие как arg2, не могут идти после ключевых аргументов.

Более того, вполне возможно, что --arg3=val3 предшествует --arg1=val1. Причина этого в том, что ключевые аргументы **kwargs в функции являются неупорядоченным словарем, и поэтому пары ключ-значение не гарантируют разрешение на определенный порядок.

Итак, решение здесь состоит в том, чтобы отказаться от использования удобства ключевых аргументов и просто использовать необработанные упорядоченные аргументы:

sh.bash_command("--arg1=val1", "arg2", "--arg3=val3")