script - Chamando um comando externo em Python




python chamar outro 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.

https://code.i-harness.com

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() .



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:

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() e shutil ).


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 :



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):

  1. Você pode definir o número de processos concorrentes a serem executados ("slots") com:

    ts -S <number-of-slots>

  2. A instalação tsnão requer privilégios de administrador. Você pode baixar e compilar a partir da fonte com um simples make, 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.





external