shell como - Chamando um comando externo em Python



linux programa (25)

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?


Answers

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

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.


subprocess.check_call é conveniente se você não quiser testar os valores de retorno. Ele lança uma exceção em qualquer erro.


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}'"


Com biblioteca padrão

O módulo Use subprocess (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

É o caminho padrão recomendado. No entanto, tarefas mais complicadas (canais, saída, entrada, etc.) podem ser tediosas para construir e escrever.

Nota sobre a versão em Python: Se você ainda estiver usando o Python 2, o subprocess.call funcionará de maneira semelhante.

ProTip: docs.python.org/2/library/shlex.html#shlex.split pode ajudá-lo a analisar o comando para run , call e outras funções de subprocess , caso você não queira (ou você não pode!) Fornecê-las em forma de listas:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

Com dependências externas

Se você não se importa com dependências externas, use plumbum :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

É o melhor wrapper de subprocess . É multi-plataforma, ou seja, funciona em sistemas Windows e Unix-like. Instale por pip install plumbum .

Outra biblioteca popular é sh :

from sh import ifconfig
print(ifconfig('wlan0'))

No entanto, sh caiu o suporte do Windows, por isso não é tão impressionante como costumava ser. Instalar por pip install sh .


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.


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.


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.


os.systemnão permite armazenar resultados, portanto, se você deseja armazenar resultados em alguma lista ou algo assim subprocess.callfunciona.


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

Aqui está um resumo das maneiras de chamar programas externos e as vantagens e desvantagens de cada um:

  1. os.system("some_command with args") passa o comando e os argumentos para o shell do seu sistema. Isso é bom porque você pode executar vários comandos de uma só vez dessa maneira e configurar canais e redirecionamento de entrada / saída. Por exemplo:

    os.system("some_command < input_file | another_command > output_file")  
    

    No entanto, embora isso seja conveniente, você precisa manipular manualmente o escape de caracteres de shell, como espaços, etc. Por outro lado, isso também permite executar comandos que são simplesmente comandos de shell e não programas realmente externos. Veja a documentação .

  2. stream = os.popen("some_command with args") fará a mesma coisa que os.system exceto que lhe dá um objeto semelhante a um arquivo que você pode usar para acessar entrada / saída padrão para aquele processo. Existem 3 outras variantes do popen que lidam com o i / o de forma ligeiramente diferente. Se você passar tudo como uma string, então seu comando é passado para o shell; Se você passá-los como uma lista, então você não precisa se preocupar em escapar de nada. Veja a documentação .

  3. A classe Popen do módulo subprocess . Isto é pretendido como um substituto para os.popen mas tem o lado negativo de ser um pouco mais complicado em virtude de ser tão abrangente. Por exemplo, você diria:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    ao invés de:

    print os.popen("echo Hello World").read()
    

    mas é bom ter todas as opções lá em uma classe unificada em vez de 4 diferentes funções popen. Veja a documentação .

  4. A função de call do módulo de subprocess . Isto é basicamente como a classe Popen e pega todos os mesmos argumentos, mas simplesmente espera até que o comando seja completado e lhe dê o código de retorno. Por exemplo:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Veja a documentação .

  5. Se você estiver no Python 3.5 ou posterior, você pode usar a nova função subprocess.run , que é muito parecida com a anterior, mas ainda mais flexível e retorna um objeto CompletedProcess quando o comando termina de ser executado.

  6. O módulo os também tem todas as funções fork / exec / spawn que você tem em um programa em C, mas eu não recomendo usá-las diretamente.

O módulo de subprocess provavelmente deve ser o que você usa.

Finalmente, por favor, esteja ciente que para todos os métodos onde você passa o comando final para ser executado pelo shell como uma string e você é responsável por escapar dele. Existem sérias implicações de segurança se qualquer parte da string que você passar não puder ser totalmente confiável. Por exemplo, se um usuário estiver inserindo alguma parte da string. Se não tiver certeza, use apenas esses métodos com constantes. Para lhe dar uma dica das implicações, considere este código:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

e imagine que o usuário entre "minha mãe não me amou && rm -rf /".


No Windows você pode simplesmente importar o subprocessmódulo e executar comandos externos, chamando subprocess.Popen(), subprocess.Popen().communicate()e subprocess.Popen().wait()como abaixo:

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

Saída:

27a74fcd-37c0-4789-9414-9531b7e3f126

Eu sempre uso fabric para coisas como:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Mas esta parece ser uma boa ferramenta: sh (interface do subprocesso Python) .

Veja um exemplo:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

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 .


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 .


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


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


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 :


Verifique também a biblioteca Python "pexpect".

Ele permite o controle interativo de programas / comandos externos, até mesmo ssh, ftp, telnet, etc. Você pode simplesmente digitar algo como:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

Sem a saída do resultado:

import os
os.system("your command here")

Com saída do resultado:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

import os
os.system("your command")

Observe que isso é perigoso, já que o comando não está limpo. Deixo a seu critério o google para a documentação relevante nos módulos 'os' e 'sys'. Há um monte de funções (exec * e spawn *) que fazem coisas semelhantes.


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


Nota: O título desta pergunta costumava ser algo como "Como imprimir em python?"

Como as pessoas podem vir procurando por ele com base no título, o Python também suporta a substituição no estilo printf:

>>> strings = [ "one", "two", "three" ]
>>>
>>> for i in xrange(3):
...     print "Item %d: %s" % (i, strings[i])
...
Item 0: one
Item 1: two
Item 2: three

E você pode multiplicar facilmente os valores de string:

>>> print "." * 10
..........




python shell command subprocess external