Quero escrever uma função que execute um comando shell e retorne sua saída como uma string , não importa se é uma mensagem de erro ou sucesso. Eu só quero obter o mesmo resultado que eu teria obtido com a linha de comando.
O que seria um exemplo de código que faria uma coisa dessas?
Por exemplo:
def run_command(cmd):
# ??????
print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
python
shell
subprocess
Luz cinza
fonte
fonte
Respostas:
A resposta a esta pergunta depende da versão do Python que você está usando. A abordagem mais simples é usar a
subprocess.check_output
função:check_output
executa um único programa que recebe apenas argumentos como entrada. 1 Retorna o resultado exatamente como impressostdout
. Se você precisar escrever uma entradastdin
, pule para as seçõesrun
ouPopen
. Se você deseja executar comandos shell complexos, consulte a notashell=True
no final desta resposta.A
check_output
função funciona em quase todas as versões do Python ainda em uso amplo (2.7+). 2 Mas para versões mais recentes, não é mais a abordagem recomendada.Versões modernas do Python (3.5 ou superior):
run
Se você estiver usando o Python 3.5 ou superior e não precisar de compatibilidade com versões anteriores , a nova
run
função é recomendada. Ele fornece uma API de alto nível muito geral para osubprocess
módulo. Para capturar a saída de um programa, passe osubprocess.PIPE
sinalizador para ostdout
argumento de palavra - chave. Em seguida, acesse ostdout
atributo doCompletedProcess
objeto retornado :O valor de retorno é um
bytes
objeto; portanto, se você deseja uma sequência adequada, precisarádecode
disso. Supondo que o processo chamado retorne uma sequência codificada em UTF-8:Tudo isso pode ser compactado em uma linha:
Se você deseja passar a entrada para o processo
stdin
, passe umbytes
objeto para oinput
argumento de palavra - chave:Você pode capturar erros passando
stderr=subprocess.PIPE
(capturar pararesult.stderr
) oustderr=subprocess.STDOUT
(capturar pararesult.stdout
junto com a saída regular). Quando a segurança não é uma preocupação, você também pode executar comandos shell mais complexos, passandoshell=True
conforme descrito nas notas abaixo.Isso adiciona um pouco de complexidade, comparado à maneira antiga de fazer as coisas. Mas acho que vale a pena: agora você pode fazer quase tudo o que precisa fazer apenas com a
run
função.Versões anteriores do Python (2.7-3.4):
check_output
Se você estiver usando uma versão mais antiga do Python, ou precisar de uma modesta compatibilidade com versões anteriores, provavelmente poderá usar a
check_output
função conforme descrito brevemente acima. Está disponível desde o Python 2.7.Ele usa os mesmos argumentos que
Popen
(veja abaixo) e retorna uma string contendo a saída do programa. O início desta resposta tem um exemplo de uso mais detalhado. No Python 3.5 e superior,check_output
é equivalente a executarrun
comcheck=True
estdout=PIPE
, e retornar apenas ostdout
atributo.Você pode transmitir
stderr=subprocess.STDOUT
para garantir que as mensagens de erro sejam incluídas na saída retornada - mas, em algumas versões do Python, a passagemstderr=subprocess.PIPE
paracheck_output
pode causar conflitos . Quando a segurança não é uma preocupação, você também pode executar comandos shell mais complexos, passandoshell=True
conforme descrito nas notas abaixo.Se você precisar canalizar
stderr
ou passar a entrada para o processo,check_output
não estará à altura da tarefa. Veja osPopen
exemplos abaixo nesse caso.Aplicativos complexos e versões herdadas do Python (2.6 e abaixo):
Popen
Se você precisar de profunda compatibilidade com versões anteriores ou se precisar de funcionalidades mais sofisticadas do que as
check_output
fornecidas, precisará trabalhar diretamente comPopen
objetos, que encapsulam a API de baixo nível para subprocessos.O
Popen
construtor aceita um único comando sem argumentos ou uma lista contendo um comando como seu primeiro item, seguido por qualquer número de argumentos, cada um como um item separado na lista.shlex.split
pode ajudar a analisar seqüências de caracteres em listas formatadas adequadamente.Popen
os objetos também aceitam vários argumentos diferentes para gerenciamento de E / S de processo e configuração de baixo nível.Enviar entrada e capturar saída
communicate
é quase sempre o método preferido. Como em:Ou
Se você definir
stdin=PIPE
,communicate
também permitirá que você transmita dados para o processo viastdin
:Nota resposta de Aaron Hall , o que indica que em alguns sistemas, você pode precisar de conjunto
stdout
,stderr
estdin
todosPIPE
(ouDEVNULL
) para chegarcommunicate
ao trabalho em tudo.Em alguns casos raros, você pode precisar de captura de saída complexa em tempo real. A resposta da Vartec sugere um caminho a seguir, mas outros métodos que não
communicate
sejam propensos a conflitos se não forem usados com cuidado.Como em todas as funções acima, quando a segurança não é uma preocupação, você pode executar comandos shell mais complexos passando
shell=True
.Notas
1. Executando comandos do shell: o
shell=True
argumentoNormalmente, cada chamada para
run
,check_output
ou oPopen
construtor executa um único programa . Isso significa que não há tubos sofisticados no estilo bash. Se você deseja executar comandos complexos do shell, pode passarshell=True
, que todas as três funções suportam.No entanto, isso gera preocupações de segurança . Se estiver fazendo algo além de scripts leves, convém chamar cada processo separadamente e passar a saída de cada um como entrada para a próxima, via
Ou
A tentação de conectar tubos diretamente é forte; resista. Caso contrário, você provavelmente verá impasses ou precisará fazer coisas hacky como essa .
2. Considerações Unicode
check_output
retorna uma string no Python 2, mas umbytes
objeto no Python 3. Vale a pena dedicar um momento para aprender sobre o unicode, se você ainda não o fez.fonte
check_output()
ecommunicate()
você tem que esperar até que o processo é feito, compoll()
que você está recebendo a saída como ela vem. Realmente depende do que você precisa.out
era do tipo<class 'bytes'>
para mim. A fim de obter o resultado como uma string que eu tinha para decodificá-lo antes de imprimir assim:out.decode("utf-8")
shell=True
? Funciona para mim. Você não precisashlex.split
quando passashell=True
.shlex.split
é para comandos não-shell. Acho que vou tirar isso porque isso está turvando as águas.universal_newlines=True
que permite transmitir e extrair cadeias Unicode na codificação padrão do sistema. Em 3.7, isso foi renomeado para o mais sensatotext=True
.encoding
parâmetro, emsubprocess.run
vez de usarresult.stdout.decode('utf-8')
, você pode usarsubprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
.Isso é bem mais fácil, mas funciona apenas no Unix (incluindo Cygwin) e Python2.7.
Retorna uma tupla com o (return_value, output).
Para uma solução que funcione no Python2 e Python3, use o
subprocess
módulo:fonte
Algo parecido:
Observe que estou redirecionando o stderr para o stdout, pode não ser exatamente o que você deseja, mas também quero mensagens de erro.
Essa função gera linha por linha conforme elas chegam (normalmente você teria que esperar o subprocesso terminar para obter a saída como um todo).
Para o seu caso, o uso seria:
fonte
wait
ecall
funções.PIPE
valorstdin
e fechar esse arquivo assim quePopen
retornar.retcode
for0
. A verificação deve serif retcode is not None
. Não se deve produzir cadeias vazias (mesmo uma linha de vazio é, pelo menos, um símbolo de '\ n'):if line: yield line
. Liguep.stdout.close()
no final..readlines()
não retornará até que toda a saída seja lida e, portanto, será interrompida para uma saída grande que não cabe na memória. Também para evitar a falta de dados em buffer após a saída do subprocesso, deve haver um análogo deif retcode is not None: yield from p.stdout.readlines(); break
A resposta da Vartec não lê todas as linhas, então fiz uma versão que:
O uso é o mesmo que a resposta aceita:
fonte
return iter(p.stdout.readline, b'')
em vez do loop whilep.stdout.readline()
pode retornar a saída em buffer anteriormente não vazia, mesmo que o processo filho já tenha sido encerrado (p.poll()
não estáNone
).Esta é uma solução complicada, mas super simples , que funciona em várias situações:
Um arquivo temporário (aqui é tmp) é criado com a saída do comando e você pode ler a saída desejada.
Nota extra dos comentários: Você pode remover o arquivo tmp no caso de trabalho único. Se você precisar fazer isso várias vezes, não será necessário excluir o tmp.
fonte
mktemp
a fazê-lo funcionar em situações de rosca, eu achoos.remove('tmp')
para torná-lo "sem arquivo".Eu tive o mesmo problema, mas descobri uma maneira muito simples de fazer isso:
Espero que ajude
Nota: Esta solução é específica do Python3, pois
subprocess.getoutput()
não funciona no Python2fonte
Você pode usar os seguintes comandos para executar qualquer comando do shell. Eu os usei no ubuntu.
Nota: Isso está obsoleto desde o python 2.6. Agora você deve usar
subprocess.Popen
. Abaixo está o exemplofonte
os.popen()
está obsoleta em Python 2.6, mas é não depreciado em Python 3.x, uma vez que em 3.x ele é implementado usandosubprocess.Popen()
.Sua milhagem pode variar, tentei a rotação de @ senderle na solução da Vartec no Windows no Python 2.6.5, mas estava recebendo erros e nenhuma outra solução funcionou. Meu erro foi:
WindowsError: [Error 6] The handle is invalid
.Eu descobri que tinha que atribuir o PIPE a cada identificador para que ele retornasse a saída que eu esperava - o seguinte funcionou para mim.
e chame assim, (
[0]
obtém o primeiro elemento da tuplastdout
):Depois de aprender mais, acredito que preciso desses argumentos de pipe, porque estou trabalhando em um sistema personalizado que usa identificadores diferentes, então tive que controlar diretamente todos os padrões.
Para parar os pop-ups do console (com Windows), faça o seguinte:
fonte
DEVNULL
vez desubprocess.PIPE
se você não escrever / ler de um tubo, caso contrário, poderá interromper o processo filho.Eu tinha um sabor ligeiramente diferente do mesmo problema com os seguintes requisitos:
palavra-chave 'yield' acima
Combinei e aprimorei as respostas anteriores para criar o seguinte:
Este código seria executado da mesma forma que as respostas anteriores:
fonte
p.poll()
sem dormir entre as chamadas, desperdiçaríamos ciclos de CPU chamando essa função milhões de vezes. Em vez disso, "estrangulamos" nosso loop dizendo ao sistema operacional que não precisamos nos incomodar pelo próximo 1/10 de segundo, para que ele possa realizar outras tarefas. (É possível que p.poll () também durma, tornando nossa declaração de sono redundante).Dividir o comando inicial para o
subprocess
pode ser complicado e complicado.Use
shlex.split()
para se ajudar.Comando de amostra
git log -n 5 --since "5 years ago" --until "2 year ago"
O código
Sem
shlex.split()
o código ficaria da seguinte maneirafonte
shlex.split()
é uma conveniência, especialmente se você não sabe exatamente como funciona a citação no shell; mas converter manualmente essa sequência na lista['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']
não é difícil, se você entender as aspas.Se você precisar executar um comando shell em vários arquivos, isso fez o truque para mim.
Edit: Acabei de ver a solução de Max Persson com a sugestão de JF Sebastian. Fui em frente e incorporou isso.
fonte
Popen
aceita uma string, mas você precisashell=True
, ou uma lista de argumentos; nesse caso, você deve passar em['nm', filename]
vez de uma string. O último é preferível porque o shell adiciona complexidade sem fornecer nenhum valor aqui. Passar uma string semshell=True
aparentemente funcionar no Windows, mas isso pode mudar em qualquer próxima versão do Python.De acordo com @senderle, se você usa python3.6 como eu:
Agirá exatamente como você executa o comando no bash
fonte
check=True, universal_newlines=True
. Em outras palavras,subprocess.run()
já faz tudo o que seu código faz.Se você usar o
subprocess
módulo python, poderá lidar com o STDOUT, STDERR e o código de comando de retorno separadamente. Você pode ver um exemplo para a implementação completa do chamador de comando. Claro que você pode estendê-lotry..except
se quiser.A função abaixo retorna os códigos STDOUT, STDERR e Return, para que você possa manipulá-los no outro script.
fonte
subprocess.run()
. Não reinvente a roda.por exemplo, execute ('ls -ahl') diferenciou três / quatro retornos possíveis e plataformas de SO:
função abaixo
fonte
A saída pode ser redirecionada para um arquivo de texto e depois lida novamente.
fonte
stdout=temp_file
?ecls.exe
parece implantar outra ferramenta de linha de comando; portanto, a maneira simples não funcionava às vezes.acabou de escrever um pequeno script bash para fazer isso usando curl
https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5
fonte