STDIN
;При передаче нескольких аргументов команде терминала, обернутой модулем sh
, каждый аргумент должен быть отдельной строкой. Передача списка строк, так же будет ошибкой.
from sh import tar # правильная передача аргументов tar("cvf", "/tmp/test.tar", "/my/home/directory/")
При передаче аргументов, как указано ниже, будет возникать ошибка:
from sh import tar # неправильная передача аргументов tar("cvf /tmp/test.tar /my/home/directory") # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # ... # raise exc # sh.ErrorReturnCode_2: # RAN: /bin/tar 'cvf /tmp/test.tar /my/home/directory'
Это сбивает с толку многих новых пользователей модуля sh
. Они хотят сделать что-то подобное tar("cvf /tmp/test.tar /my/home/directory")
и ожидают, что это просто сработает, Но вместо этого они получат сбивающее с толку сообщение об ошибке (как показано выше).
Причина, по которой они ожидают, что это сработает, заключается в том, что оболочки, такие как Bash
, автоматически анализируют gthtlfyye. командную строку и разбивают ее на аргументы, прежде чем отправлять их в исполняемый файл. У них есть сложный набор правил, некоторые из которых представлены Python модулем shlex
.
Даже если бы разработчики модуля хотели реализовать это в sh
, то это уменьшило бы возможности пользователей параметризовать части аргументов. Им пришлось бы использовать строковую интерполяцию, которая по сути уродлива и подвержена ошибкам:
from sh import tar tar("cvf {1} {2}".format("/tmp/tar1.tar", "/home/oh no a space")
В приведенном выше примере "/home/oh"
, "no"
, "a"
и "space"
будут отдельными аргументами для команды терминала tar
, что приведет к неожиданному поведению программы. В принципе, каждая команда с позиционными параметрами должна была бы ожидать символов, которые могли бы сломать синтаксический анализатор.
Модуль sh
поддерживает ключи короткой формы (например -a
) и длинной формы (например --arg
) в качестве параметров командной строки:
# вызывает "curl http://duckduckgo.com/ -o page.html --silent" curl("http://duckduckgo.com/", o="page.html", silent=True) # или если не использовать ключевые параметры curl("http://duckduckgo.com/", "-o", "page.html", "--silent") # вызывает "adduser amoffat --system --shell=/bin/bash --no-create-home" adduser("amoffat", system=True, shell="/bin/bash", no_create_home=True) # без использования ключевых параметров adduser("amoffat", "--system", "--shell", "/bin/bash", "--no-create-home")
Обычно этот вопрос задают, когда пользователь пытается выполнить что-то вроде следующей командной строки:
my-command --arg1=val1 arg2 --arg3=val3
Первая попытка, которую пользователь пытается сделать это :
import sh sh.bash_command(arg1="val1", "arg2", arg3="val3")
Это не работает, потому что в Python позиционные аргументы, такие как arg2
, не могут идти после ключевых аргументов.
Кроме того, вполне возможно, что --arg3=val3
предшествует --arg1=val1
. Причина этого заключается в том, что **kwargs
в функции является неупорядоченным отображением (словарем), и поэтому пары ключ-значение не гарантированно разрешаются в определенном порядке.
Таким образом, в данном случае, решение состоит в том, чтобы отказаться от использования ключевых аргументов и просто использовать необработанные позиционные аргументы:
sh.bash_command("--arg1=val1", "arg2", "--arg3=val3")
Часто встает необходимость переопределить параметры по умолчанию для всех команд терминала, запускаемых через модуль sh
. Например, предположим, что надо объединять вывод всех команд в буфер io.StringIO
.
import sh from io import StringIO buf = StringIO() sh.ls("/", _out=buf) sh.whoami(_out=buf) sh.ps("auxwf", _out=buf)
Из кода видно, что постоянная передача _out=buf
во все вызовы быстро надоедает. К счастью, можно создавать контексты выполнения, которые позволяют устанавливать аргументы по умолчанию для всех команд, порожденных из этого контекста:
import sh from io import StringIO buf = StringIO() sh_buf = sh(_out=buf) sh_buf.ls("/") sh_buf.whoami() sh_buf.ps("auxwf")
Теперь все, что запускается из sh_buf, будет посылать свои выходные данные в экземпляр буфера StringIO
.
Контексты выполнения также могут быть импортированы, например, из модуля нижнего уровня buf.py
:
# buf.py import sh from io import StringIO buf = StringIO() sh_buf = sh(_out=buf) # cmd.py from sh_buf import ls, whoami, ps ls("/") whoami() ps("auxwf")
STDIN
Поток STDIN отправляется процессу непосредственно с помощью специального ключевого аргументы _in
:
print(cat(_in="test")) # test
Любая команда терминала, принимающая входные данные от STDIN
, может быть использована следующим образом:
print(tr("[:lower:]", "[:upper:]", _in="sh is awesome")) # SHISAWESOME
Следовательно пользователи не ограничены использованием только строк в качестве входных данных (но не параметров). Можно использовать файловый объект, очередь Queue
или любую итерацию (список, словарь и т. д.):
stdin = ["sh", "is", "awesome"] out = tr("[:lower:]", "[:upper:]", _in=stdin)
Многие программы терминала имеют свои собственные подмножества команд, такие как git
(например branch
или checkout
), svn
(например update
или status
) и sudo
(где любая команда, следующая за sudo
, считается ее подкомандой). Модуль sh
обрабатывает подкоманды через синтаксис Python доступа к атрибутам:
from sh import git, sudo # выполнит "git branch -v" print(git.branch("-v")) # та же команда print(git("branch", "-v")) # выполнит "sudo /bin/ls /root" print(sudo.ls("/root")) # та же команда print(sudo("/bin/ls", "/root"))
Обращение к подкомандам через синтаксис доступа к атрибутам - это в основном синтаксический сахар, который делает вызов некоторых программ концептуально более приятным.
Примечание. Если будете использовать sudo
в качестве подкоманды, обязательно посмотрите раздел "Использование sudo в модуле sh
".