В разделе рассмотрены частые вопросы, задаваемые начинающими пользователями модуля sh
, которые по каким-то причинам небыли освещены в предыдущих разделах.
*
не работает в качестве аргумента команды терминала?$PATH
?sys.stdout
и sys.stdin
?Нет никаких планов по поддержке Windows.
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")