script - python chamar outro python




Chamando um comando externo em Python (20)

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?


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.


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