script - python chamar outro python
Chamando um comando externo em Python (20)
Chamando um comando externo em Python
Simples, use subprocess.run
, que retorna um objeto CompletedProcess
:
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
Por quê?
A partir do Python 3.5, a documentação recomenda subprocess.run :
A abordagem recomendada para invocar subprocessos é usar a função run () para todos os casos de uso que ela pode manipular. Para casos de uso mais avançados, a interface Popen subjacente pode ser usada diretamente.
Aqui está um exemplo do uso mais simples possível - e faz exatamente como solicitado:
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
run
aguarda que o comando seja concluído com êxito e retorna um objeto CompletedProcess
. Pode, em vez disso, aumentar TimeoutExpired
(se você der um timeout=
argumento) ou CalledProcessError
(se ele falhar e você passar check=True
).
Como você pode inferir do exemplo acima, stdout e stderr são canalizados para o seu próprio stdout e stderr por padrão.
Podemos inspecionar o objeto retornado e ver o comando que foi dado e o código de retorno:
>>> completed_process.args
'python --version'
>>> completed_process.returncode
0
Capturando saída
Se você deseja capturar a saída, você pode passar o subprocess.PIPE
para o stderr
ou stdout
apropriado:
>>> cp = subprocess.run('python --version',
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''
(Eu acho interessante e um pouco contra-intuitivo que as informações da versão sejam colocadas em stderr ao invés de stdout.)
Passar uma lista de comandos
Pode-se facilmente passar de manualmente fornecendo uma seqüência de comando (como a pergunta sugere) para fornecer uma seqüência de caracteres compilada programaticamente. Não crie strings programaticamente. Esse é um possível problema de segurança. É melhor assumir que você não confia na entrada.
>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n This is indented.\r\n'
Note, apenas args
devem ser passados posicionalmente.
Assinatura Completa
Aqui está a assinatura real na fonte e como mostrado pela help(run)
:
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
Os popenargs
e kwargs
são dados ao construtor Popen
. input
pode ser uma string de bytes (ou unicode, se especificar codificação ou universal_newlines=True
) que será canalizada para o stdin do subprocesso.
A documentação descreve timeout=
e check=True
better than I could:
O argumento timeout é passado para Popen.communicate (). Se o tempo limite expirar, o processo filho será eliminado e aguardado. A exceção TimeoutExpired será reelevada depois que o processo filho for finalizado.
Se a verificação for verdadeira e o processo sair com um código de saída diferente de zero, uma exceção CalledProcessError será gerada. Atributos dessa exceção contêm os argumentos, o código de saída e stdout e stderr, se eles foram capturados.
e este exemplo para check=True
é melhor do que um que eu poderia criar:
>>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Assinatura Expandida
Aqui está uma assinatura expandida, conforme indicado na documentação:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
Observe que isso indica que apenas a lista de argumentos deve ser passada posicionalmente. Portanto, passe os argumentos restantes como argumentos de palavra-chave.
Popen
Quando usar Popen
vez disso? Eu lutaria para encontrar casos de uso baseados apenas nos argumentos. O uso direto do Popen
, no entanto, lhe dará acesso a seus métodos, incluindo poll
, 'send_signal', 'terminate' e 'wait'.
Aqui está a assinatura Popen
como dada na fonte . Acho que este é o encapsulamento mais preciso da informação (em oposição à help(Popen)
):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None):
Mas mais informativo é a documentação do Popen
:
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
Execute um programa filho em um novo processo. No POSIX, a classe usa o comportamento de os.execvp () para executar o programa filho. No Windows, a classe usa a função Windows CreateProcess (). Os argumentos para o Popen são os seguintes.
Entender a documentação restante no Popen
será deixado como um exercício para o leitor.
Como posso chamar um comando externo (como se eu tivesse digitado no shell Unix ou no prompt de comando do Windows) de dentro de um script Python?
Atualizar:
subprocess.run
é a abordagem recomendada a partir do Python 3.5 se seu código não precisar manter a compatibilidade com versões anteriores do Python. É mais consistente e oferece uma facilidade de uso semelhante à do Envoy. (Tubulação não é tão simples. Veja esta pergunta para saber como ).
Aqui estão alguns exemplos dos documentos .
Execute um processo:
>>> subprocess.run(["ls", "-l"]) # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
Levantar na execução falhada:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Capturar saída:
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
Resposta original:
Eu recomendo tentar https://github.com/kennethreitz/envoy . É um invólucro para o subprocesso, que, por sua vez, visa substituir os módulos e funções mais antigos. Envoy é subprocesso para humanos.
Exemplo de uso do readme :
>>> r = envoy.run('git config', data='data to pipe in', timeout=2)
>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''
Material de tubulação ao redor também:
>>> r = envoy.run('uptime | pbcopy')
>>> r.command
'pbcopy'
>>> r.status_code
0
>>> r.history
[<Response 'uptime'>]
É assim que eu corro meus comandos. Este código tem tudo que você precisa
from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
Algumas dicas sobre como separar o processo filho do chamador (iniciando o processo filho em segundo plano).
Suponha que você queira iniciar uma tarefa longa a partir de um script CGI, ou seja, o processo filho deve viver mais do que o processo de execução do script CGI.
O exemplo clássico dos documentos do subprocesso é:
import subprocess
import sys
# some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess
# some more code here
A idéia aqui é que você não quer esperar na linha 'call subprocess' até que o longtask.py termine. Mas não está claro o que acontece depois da linha 'mais algum código aqui' do exemplo.
Minha plataforma de destino era freebsd, mas o desenvolvimento estava no Windows, então enfrentei o problema no Windows primeiro.
No windows (win xp), o processo pai não terminará até que o longtask.py termine seu trabalho. Não é o que você quer no script CGI. O problema não é específico do Python, na comunidade PHP os problemas são os mesmos.
A solução é passar o DETACHED_PROCESS Process Creation Flag para a função CreateProcess subjacente na API do win. Se acontecer de você ter instalado o pywin32, você pode importar o sinalizador do módulo win32process, caso contrário, você mesmo deve defini-lo:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
/ * UPD 2015.10.27 @eryksun em um comentário abaixo observa que o sinalizador semanticamente correto é CREATE_NEW_CONSOLE (0x00000010) * /
No freebsd, temos outro problema: quando o processo pai é concluído, ele também finaliza os processos filhos. E isso não é o que você quer no script CGI. Algumas experiências mostraram que o problema parecia estar em compartilhar sys.stdout. E a solução de trabalho foi a seguinte:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
Eu não verifiquei o código em outras plataformas e não sei as razões do comportamento no freebsd. Se alguém souber, por favor, compartilhe suas ideias. Pesquisando sobre o início de processos em segundo plano no Python não lança nenhuma luz ainda.
Eu normalmente uso:
import subprocess
p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
print line,
retval = p.wait()
Você é livre para fazer o que quiser com os dados stdout
no pipe. Na verdade, você pode simplesmente omitir esses parâmetros ( stdout=
e stderr=
) e ele se comportará como os.system()
.
Eu recomendaria usar o módulo de subprocesso em vez de os.system porque ele concede escape para você e, portanto, é muito mais seguro: http://docs.python.org/library/subprocess.html
subprocess.call(['ping', 'localhost'])
Existem várias bibliotecas diferentes que permitem chamar comandos externos com o Python. Para cada biblioteca eu dei uma descrição e mostrei um exemplo de chamar um comando externo. O comando que usei como exemplo é ls -l
(lista todos os arquivos). Se você quiser saber mais sobre qualquer uma das bibliotecas que listei e vinculou a documentação para cada uma delas.
Fontes:
- subprocesso: https://docs.python.org/3.5/library/subprocess.html
- shlex: https://docs.python.org/3/library/shlex.html
- os: https://docs.python.org/3.5/library/os.html
- sh: https://amoffat.github.io/sh/
- plumbum: https://plumbum.readthedocs.io/en/latest/
- pexpect: https://pexpect.readthedocs.io/en/stable/
- tecido: http://www.fabfile.org/
- enviado: https://github.com/kennethreitz/envoy
- comandos: https://docs.python.org/2/library/commands.html
Estas são todas as bibliotecas:
Espero que isso ajude você a tomar uma decisão sobre qual biblioteca usar :)
subprocesso
Subprocesso permite que você chame comandos externos e conecte-os a seus canais de entrada / saída / erro (stdin, stdout e stderr). O subprocesso é a opção padrão para executar comandos, mas às vezes outros módulos são melhores.
subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command
os
os é usado para "funcionalidade dependente do sistema operacional". Também pode ser usado para chamar comandos externos com os.system
e os.popen
(Nota: Há também um subprocesso.popen). O OS sempre executará o shell e é uma alternativa simples para pessoas que não precisam ou não sabem como usar o subprocess.run
.
os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output
sh
sh é uma interface de subprocesso que permite chamar programas como se fossem funções. Isso é útil se você quiser executar um comando várias vezes.
sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function
plumbum
plumbum é uma biblioteca para programas Python "script-like". Você pode chamar programas como funções como sh
. Plumbum é útil se você deseja executar um pipeline sem o shell.
ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command
pexpe
O pexpect permite gerar aplicativos filhos, controlá-los e encontrar padrões em suas saídas. Essa é uma alternativa melhor para o subprocesso de comandos que esperam um tty no Unix.
pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')
tecido
fabric é uma biblioteca 2.5 e 2.7 do Python. Ele permite que você execute comandos shell locais e remotos. Fabric é uma alternativa simples para executar comandos em um shell seguro (SSH)
fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output
enviado
O enviado é conhecido como "subprocesso para humanos". Ele é usado como um wrapper de conveniência em torno do módulo de subprocess
.
r = envoy.run("ls -l") # Run command
r.std_out # get output
comandos
commands
contém funções de wrapper para os.popen
, mas foi removido do Python 3, pois o subprocess
é uma alternativa melhor.
A edição foi baseada no comentário de JF Sebastian.
Há outra diferença aqui que não é mencionada anteriormente.
subprocess.Popen
executa o <command> como um subprocesso. No meu caso, eu preciso executar o arquivo <a> que precisa se comunicar com outro programa, <b>.
Eu tentei o subprocesso e a execução foi bem-sucedida. No entanto, <b> não pôde se comunicar com <a>. Tudo é normal quando eu corro ambos do terminal.
Mais uma coisa: (NOTA: o kwrite se comporta de maneira diferente de outros aplicativos. Se você tentar o seguinte com o Firefox, os resultados não serão os mesmos.)
Se você tentar os.system("kwrite")
, o fluxo do programa congelará até que o usuário feche o kwrite. Para superar isso, tentei, em vez disso, os.system(konsole -e kwrite)
. Este programa de tempo continuou a fluir, mas o kwrite se tornou o subprocesso do console.
Qualquer pessoa executa o kwrite não sendo um subprocesso (ou seja, no monitor do sistema ele deve aparecer na borda mais à esquerda da árvore).
Pode ser simples assim:
import os
cmd = "your command"
os.system(cmd)
Se você precisar da saída do comando que está chamando, poderá usar subprocess.check_output (Python 2.7+).
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
Observe também o parâmetro do shell .
Se shell for
True
, o comando especificado será executado através do shell. Isso pode ser útil se você estiver usando Python principalmente para o fluxo de controle aprimorado que ele oferece na maioria dos shells do sistema e ainda desejar acesso conveniente a outros recursos do shell, como caches de shell, curingas de nomes de arquivos, expansão de variáveis de ambiente e expansão de ~ diretório. No entanto, observe que o próprio Python oferece implementações de muitos recursos semelhantes a shell (em particular,glob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
eshutil
).
Usar:
import os
cmd = 'ls -al'
os.system(cmd)
os - Este módulo fornece uma maneira portátil de usar a funcionalidade dependente do sistema operacional.
Para as funções mais os
, here está a documentação.
Veja o módulo de subprocesso na biblioteca padrão:
from subprocess import call
call(["ls", "-l"])
A vantagem do subprocesso vs. sistema é que ele é mais flexível (você pode obter o stdout, stderr, o código de status "real", melhor tratamento de erros, etc ...).
A documentação oficial recomenda o módulo subprocess sobre a alternativa os.system ():
O módulo de subprocesso oferece recursos mais poderosos para gerar novos processos e recuperar seus resultados; usar esse módulo é preferível a usar essa função [
os.system()
].
A seção " Substituindo funções mais antigas com o módulo de subprocesso " na documentação do subprocesso pode ter algumas receitas úteis.
Documentação oficial no módulo de subprocesso :
https://docs.python.org/2/library/subprocess.html
... ou por um comando muito simples:
import os
os.system('cat testfile')
os.system
está OK, mas meio que datado. Também não é muito seguro. Em vez disso, tente subprocess
. subprocess
não chama sh diretamente e, portanto, é mais seguro que os.system
.
Obtenha mais informações here .
subprocess.check_call
é conveniente se você não quiser testar os valores de retorno. Ele lança uma exceção em qualquer erro.
Você pode usar o Popen e verificar o status do procedimento:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
Confira subprocess.Popen .
Eu gosto muito do shell_command por sua simplicidade. Ele é construído no topo do módulo de subprocesso.
Aqui está um exemplo dos documentos:
>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py shell_command.py test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
No Linux, caso você queira chamar um comando externo que será executado independentemente (continuará em execução após o término do script python), é possível usar uma fila simples como spooler de tarefas ou o comando at
Um exemplo com o spooler de tarefas:
#!/usr/bin/python
import os
netid= "nova net-list | awk '/ External / { print $2 }'"
temp=os.popen(netid).read() /* here temp also contains new line (\n) */
networkId=temp.rstrip()
print(networkId)
Notas sobre o spooler de tarefas ( ts
):
Você pode definir o número de processos concorrentes a serem executados ("slots") com:
ts -S <number-of-slots>
A instalação
ts
não requer privilégios de administrador. Você pode baixar e compilar a partir da fonte com um simplesmake
, adicioná-lo ao seu caminho e pronto.
Plugue sem vergonha, eu escrevi uma biblioteca para isso: P https://github.com/houqp/shell.py
É basicamente um invólucro para popen e shlex por enquanto. Ele também suporta comandos de tubulação para que você possa encadear comandos com mais facilidade no Python. Então você pode fazer coisas como:
ex('echo hello shell.py') | "awk '{print $2}'"
import os
cmd = 'ls -al'
os.system(cmd)
Se você quiser retornar os resultados do comando, você pode usar os.popen
. No entanto, isso está obsoleto desde a versão 2.6 em favor do módulo de subprocesso , que outras respostas abordaram bem.