Na minha máquina local, eu executo um script python que contém esta linha
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
Isso funciona bem.
Em seguida, executo o mesmo código em um servidor e recebo a seguinte mensagem de erro
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
Então, o que fiz foi inserir um print bashCommand
que me imprime além do comando no terminal antes de executá-lo os.system()
.
Obviamente, recebo novamente o erro (causado por os.system(bashCommand)
), mas antes desse erro ele imprime o comando no terminal. Então eu apenas copiei essa saída e fiz uma cópia colar no terminal e apertei enter e funciona ...
Alguém tem idéia do que está acontecendo?
cwm
. Talvez você tenha alguma configuração.bashrc
que configure o ambiente para o uso do bash interativo?cwm
. Ou talvez exista uma diferença no PATH, e uma versão diferente docwm
seja chamada. Ou versões diferentes do Python. É realmente difícil de descobrir isso sem acesso à máquina ...Respostas:
Não use
os.system
. Foi descontinuado em favor do subprocesso . A partir dos documentos : "Este módulo pretende substituir vários mais velhos módulos e funções:os.system
,os.spawn
".Como no seu caso:
fonte
cd 'path\to\somewhere'
seguido por outro comando bash que precisava ser executado em algum lugar. @ user225312cwd
argumento para Popen:subprocess.Popen(..., cwd='path\to\somewhere')
stdout=file
redireciona a saída para um arquivo neste caso. Implementa> file
). Seria errado para passar..., '>', 'file']
no último comando esperando o redirecionamento (não vai funcionar sem um shell e se você usar um shell, você deve passar o comando como uma string)Para expandir um pouco as respostas anteriores aqui, há vários detalhes que são geralmente ignorados.
subprocess.run()
maissubprocess.check_call()
e amigos maissubprocess.call()
sobresubprocess.Popen()
maisos.system()
sobreos.popen()
text=True
, akauniversal_newlines=True
.shell=True
oushell=False
e como ele muda entre aspas e a disponibilidade de conveniências de shell.sh
e BashEsses tópicos são abordados com mais detalhes abaixo.
Preferir
subprocess.run()
ousubprocess.check_call()
o
subprocess.Popen()
função é uma força de trabalho de baixo nível, mas é difícil de usar corretamente e você acaba copiando / colando várias linhas de código ... que já existem convenientemente na biblioteca padrão como um conjunto de funções de wrapper de nível superior para várias finalidades, que são apresentados com mais detalhes a seguir.Aqui está um parágrafo da documentação :
Infelizmente, a disponibilidade dessas funções do wrapper difere entre as versões do Python.
subprocess.run()
foi introduzido oficialmente no Python 3.5. Ele pretende substituir todos os itens a seguir.subprocess.check_output()
foi introduzido no Python 2.7 / 3.1. É basicamente equivalente asubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
foi introduzido no Python 2.5. É basicamente equivalente asubprocess.run(..., check=True)
subprocess.call()
foi introduzido no Python 2.4 nosubprocess
módulo original ( PEP-324 ). É basicamente equivalente asubprocess.run(...).returncode
API de alto nível vs
subprocess.Popen()
O refatorado e estendido
subprocess.run()
é mais lógico e mais versátil do que as funções herdadas mais antigas que ele substitui. Ele retorna umCompletedProcess
objeto que possui vários métodos que permitem recuperar o status de saída, a saída padrão e alguns outros resultados e indicadores de status do subprocesso concluído.subprocess.run()
é o caminho a percorrer se você simplesmente precisar de um programa para executar e retornar o controle ao Python. Para cenários mais envolvidos (processos em segundo plano, talvez com E / S interativa com o programa pai Python), você ainda precisa usarsubprocess.Popen()
e cuidar de todo o encanamento. Isso requer uma compreensão bastante complexa de todas as partes móveis e não deve ser realizada de ânimo leve. OPopen
objeto mais simples representa o processo (possivelmente ainda em execução) que precisa ser gerenciado a partir do seu código pelo restante da vida útil do subprocesso.Talvez deva ser enfatizado que apenas
subprocess.Popen()
cria um processo. Se você deixar assim, você tem um subprocesso em execução simultaneamente com o Python, portanto, um processo "em segundo plano". Se ele não precisar fazer entrada ou saída ou coordenar-se com você, poderá fazer um trabalho útil em paralelo com o seu programa Python.Evite
os.system()
eos.popen()
Desde o tempo eterno (bem, desde Python 2.5) a
os
documentação do módulo tem contido a recomendação de preferênciasubprocess
sobreos.system()
:O problema
system()
é que, obviamente, depende do sistema e não oferece maneiras de interagir com o subprocesso. Ele simplesmente roda, com saída padrão e erro padrão fora do alcance do Python. A única informação que Python recebe de volta é o status de saída do comando (zero significa sucesso, embora o significado de valores diferentes de zero também dependa um pouco do sistema).O PEP-324 (que já foi mencionado acima) contém uma justificativa mais detalhada sobre por que
os.system
é problemático e comosubprocess
tenta resolver esses problemas.os.popen()
costumava ser ainda mais fortemente desencorajado :No entanto, desde algum momento no Python 3, ele foi reimplementado para simplesmente usar
subprocess
e redireciona para asubprocess.Popen()
documentação para obter detalhes.Entenda e use normalmente
check=True
Você também notará que
subprocess.call()
possui muitas das mesmas limitações queos.system()
. Em uso regular, você geralmente deve verificar se o processo foi concluído com êxito, qualsubprocess.check_call()
e o quesubprocess.check_output()
faz (onde o último também retorna a saída padrão do subprocesso concluído). Da mesma forma, você deve usar normalmente,check=True
asubprocess.run()
menos que precise especificamente permitir que o subprocesso retorne um status de erro.Na prática, com
check=True
ousubprocess.check_*
, Python lançará umaCalledProcessError
exceção se o subprocesso retornar um status de saída diferente de zero.Um erro comum
subprocess.run()
é omitircheck=True
e surpreender-se quando o código downstream falhar se o subprocesso falhar.Por outro lado, um problema comum
check_call()
echeck_output()
era que os usuários que usavam cegamente essas funções ficaram surpresos quando a exceção foi levantada, por exemplo, quandogrep
não encontraram uma correspondência. (Você provavelmente deve substituir ogrep
código Python nativo de qualquer maneira, conforme descrito abaixo.)Tudo contado, você precisa entender como os comandos do shell retornam um código de saída e sob quais condições eles retornarão um código de saída diferente de zero (erro) e tomarão uma decisão consciente de como exatamente deve ser tratado.
Entenda e provavelmente use
text=True
akauniversal_newlines=True
Desde Python 3, cadeias internas ao Python são cadeias Unicode. Mas não há garantia de que um subprocesso gere saída Unicode ou seqüências de caracteres.
(Se as diferenças não forem imediatamente óbvias, recomenda-se a leitura pragmática do Node Batchelder Unicode , se não for totalmente obrigatória. Há uma apresentação em vídeo de 36 minutos por trás do link, se você preferir, embora a leitura da página provavelmente leve muito menos tempo. )
No fundo, o Python precisa buscar um
bytes
buffer e interpretá-lo de alguma forma. Se ele contém um blob de dados binários, não deve ser decodificado em uma seqüência de caracteres Unicode, porque é um comportamento propenso a erros e indutor de erros - precisamente o tipo de comportamento irritante que envolveu muitos scripts Python 2, antes que houvesse uma maneira de distinguir adequadamente entre texto codificado e dados binários.Com
text=True
, você diz ao Python que, na verdade, espera dados de texto de volta na codificação padrão do sistema e que eles devem ser decodificados em uma string Python (Unicode) da melhor maneira possível (geralmente UTF-8 em qualquer moderadamente) sistema de datas, exceto talvez o Windows?)Se isso é não o que você pedir de volta, Python vai apenas dar-lhe
bytes
cordas nosstdout
estderr
cordas. Talvez em algum depois apontar-lhe que sei que eles eram cadeias de texto depois de tudo, e você sabe sua codificação. Então, você pode decodificá-los.O Python 3.7 introduziu o alias mais curto, descritivo e compreensível
text
para o argumento de palavra-chave, que antes era chamado de maneira enganosauniversal_newlines
.Understand
shell=True
vsshell=False
Com
shell=True
você, você passa uma única string para o seu shell, e o shell o leva a partir daí.Com
shell=False
você, passe uma lista de argumentos para o sistema operacional, ignorando o shell.Quando você não possui um shell, salva um processo e se livra de uma quantidade bastante substancial de complexidade oculta, que pode ou não abrigar bugs ou mesmo problemas de segurança.
Por outro lado, quando você não possui um shell, não possui redirecionamento, expansão de curinga, controle de tarefas e um grande número de outros recursos do shell.
Um erro comum é usar
shell=True
e ainda transmitir ao Python uma lista de tokens, ou vice-versa. Isso acontece em alguns casos, mas é realmente mal definido e pode ser interrompido de maneiras interessantes.A réplica comum "mas funciona para mim" não é uma refutação útil, a menos que você entenda exatamente sob quais circunstâncias ela poderia parar de funcionar.
Exemplo de refatoração
Muitas vezes, os recursos do shell podem ser substituídos pelo código Python nativo. O Awk ou
sed
scripts simples provavelmente devem ser traduzidos simplesmente para Python.Para ilustrar isso parcialmente, aqui está um exemplo típico, mas um pouco tolo, que envolve muitos recursos de shell.
Algumas coisas a serem observadas aqui:
shell=False
você, não é necessário citar que o shell requer em torno de strings. Colocar aspas de qualquer maneira provavelmente é um erro.O código refatorado também ilustra o quanto o shell realmente faz por você com uma sintaxe muito concisa - para melhor ou para pior. Python diz que explícito é melhor que implícito, mas o código Python é bastante detalhado e sem dúvida parece mais complexo do que isso realmente é. Por outro lado, oferece vários pontos onde você pode obter o controle no meio de outra coisa, como exemplifica trivialmente o aprimoramento de que podemos incluir facilmente o nome do host junto com a saída do comando shell. (Isso também não é difícil de fazer no shell, mas às custas de mais um desvio e talvez outro processo.)
Construções comuns do shell
Para completar, aqui estão breves explicações sobre alguns desses recursos do shell e algumas notas sobre como eles podem ser substituídos por recursos nativos do Python.
glob.glob()
ou frequentemente por comparações simples de strings do Python, comofor file in os.listdir('.'): if not file.endswith('.png'): continue
. O Bash possui vários outros recursos de expansão, como.{png,jpg}
expansão de chaves e{1..100}
também a expansão de til (~
expande para o diretório inicial e, geralmente,~account
para o diretório inicial de outro usuário)$SHELL
ou$my_exported_var
às vezes podem simplesmente ser substituídas por variáveis Python. Variáveis do shell exportados estão disponíveis como por exemploos.environ['SHELL']
(o significadoexport
é fazer com que a variável disponível para subprocessos -. Uma variável que não está disponível para subprocessos, obviamente, não estará disponível para Python executado como um subprocesso da casca, ou vice-versa Aenv=
palavra-chave O argumento parasubprocess
métodos permite definir o ambiente do subprocesso como um dicionário; portanto, é uma maneira de tornar uma variável Python visível para um subprocesso). Comshell=False
você precisará entender como remover quaisquer aspas; por exemplo,cd "$HOME"
é equivalente aos.chdir(os.environ['HOME'])
sem aspas ao redor do nome do diretório. (Muitas vezescd
de qualquer maneira, não é útil ou necessário, e muitos iniciantes omitem as aspas duplas em torno da variável e ficam impunes até um dia ... )grep 'foo' <inputfile >outputfile
abreoutputfile
para escrever einputfile
para ler e passa seu conteúdo como entrada padrão paragrep
, cuja saída padrão então chegaoutputfile
. Isso geralmente não é difícil de substituir pelo código Python nativo.echo foo | nl
executa dois subprocessos, em que a saída padrão deecho
é a entrada padrão denl
(no nível do SO, em sistemas semelhantes ao Unix, esse é um identificador de arquivo único). Se você não pode substituir uma ou as duas extremidades do pipeline pelo código Python nativo, talvez pense em usar um shell, afinal, especialmente se o pipeline tiver mais de dois ou três processos (embora veja opipes
módulo na biblioteca padrão do Python ou vários de concorrentes de terceiros mais modernos e versáteis).ls -l /
é equivalente a'ls' '-l' '/'
, mas os citando torno literais é completamente opcional. Seqüências de caracteres não citadas que contêm metacaracteres de shell sofrem expansão de parâmetro, tokenização de espaço em branco e expansão de curinga; aspas duplas impedem a tokenização de espaço em branco e a expansão de curingas, mas permitem expansões de parâmetros (substituição de variável, substituição de comando e processamento de barra invertida). Isso é simples em teoria, mas pode ser desconcertante, especialmente quando há várias camadas de interpretação (um comando de shell remoto, por exemplo).Entenda as diferenças entre
sh
e Bashsubprocess
executa seus comandos shell, a/bin/sh
menos que você solicite especificamente o contrário (exceto, é claro, no Windows, onde ele usa o valor daCOMSPEC
variável). Isso significa que vários recursos exclusivos do Bash, como matrizes,[[
etc. não estão disponíveis.Se você precisar usar a sintaxe apenas do Bash, poderá passar o caminho para o shell como
executable='/bin/bash'
(onde, é claro, se o seu Bash estiver instalado em outro lugar, será necessário ajustar o caminho).A
subprocess
é separado de seu pai e não pode alterá-loUm erro um tanto comum é fazer algo como
que, além da falta de elegância, também trai uma falta fundamental de compreensão da parte "sub" do nome "subprocesso".
Um processo filho é executado completamente separado do Python e, quando termina, o Python não tem idéia do que fez (além dos indicadores vagos que podem inferir do status de saída e saída do processo filho). Uma criança geralmente não pode mudar o ambiente dos pais; não pode definir uma variável, alterar o diretório de trabalho ou, em muitas palavras, se comunicar com seu pai sem a cooperação do pai.
A correção imediata nesse caso específico é executar os dois comandos em um único subprocesso;
embora obviamente esse caso de uso específico não exija o shell. Lembre-se, você pode manipular o ambiente do processo atual (e, portanto, também seus filhos) via
ou passar uma configuração de ambiente para um processo filho com
(para não mencionar a refatoração óbvia
subprocess.run(['echo', 'bar'])
; masecho
é um mau exemplo de algo para executar em um subprocesso, é claro).Não execute o Python a partir do Python
Este é um conselho um pouco dúbio; Certamente, há situações em que faz sentido ou é mesmo um requisito absoluto executar o interpretador Python como um subprocesso de um script Python. Mas com muita frequência, a abordagem correta é simplesmente para
import
o outro módulo Python no seu script de chamada e chamar suas funções diretamente.Se o outro script Python estiver sob seu controle e não for um módulo, considere transformá-lo em um . (Essa resposta já é muito longa, portanto não vou me aprofundar nos detalhes aqui.)
Se você precisar de paralelismo, poderá executar funções Python em subprocessos com o
multiprocessing
módulo. Também existe athreading
execução de várias tarefas em um único processo (que é mais leve e oferece mais controle, mas também mais restrito, pois os segmentos de um processo são fortemente acoplados e vinculados a um único GIL .)fonte
run()
às cegas. A faltacheck=True
causou um erro que seria evitado se check_call fosse usado - "check" está no nome, você não pode perdê-lo - é o padrão correto: não ignore os erros silenciosamente. Eu não li mais.sh
mas você me venceu. Estou tentando explicar detalhadamente os detalhes para ajudar os iniciantes, para os quais essas armadilhas não são óbvias, e isso fica um pouco demorado. O seu deve ser suficiente o suficiente; +1stderr/stdout = subprocess.PIPE
uma sobrecarga de desempenho superior às configurações padrão?os.system()
.Chame-o com subprocesso
O erro que você está recebendo parece ser porque não há módulo de troca no servidor, você deve instalar a troca no servidor e executar o script novamente
fonte
swap
módulo está obviamente lá, porque a execução do comando a partir do shell funciona.shell=True
, deve usar uma lista para passar vários argumentos, ou seja, use em['a', 'b', 'c']
vez de'a b c'
. Embora uma divisão ingênua não funcione devido a> file
(redirecionamento de shell) no comando. Mais detalhesÉ possível usar o programa bash, com o parâmetro -c para executar os comandos:
fonte
subprocess.check_output(bashCommand, shell=True)
faz a mesma coisa. Se o seu comando for uma sequência estática, tente analisá-lo em uma lista e evite oshell=True
; embora neste caso você precisa do shell para o redirecionamento de qualquer maneira, ou então você terá que refatorar-lo para pura Python -with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
/bin/sh
(usado pelo subprocesso) não é necessariamentebash
(você não pode usar basismos). Embora se possa usar,executable='/bin/bash
se desejar. Aqui está um exemplo de códigocheck_output()
é inútil aqui (a saída está sempre vazia devido ao> file
redirecionamento; use emcheck_call()
vez disso.Você pode usar
subprocess
, mas sempre achei que não era uma maneira 'pitônica' de fazê-lo. Então, criei o Sultan (plugue descarado) que facilita a execução das funções da linha de comando.https://github.com/aeroxis/sultan
fonte
De acordo com o erro, você está perdendo um pacote chamado swap no servidor. Isso
/usr/bin/cwm
requer isso. Se você estiver no Ubuntu / Debian, instalepython-swap
usando o aptitude.fonte
swap
ou não deveria ter importado em primeiro lugar. você podeimport swap
manualmente? funciona?sys.path
onde está funcionando e onde não está. Em seguida, tente procurar a pasta swap ou swap.py nas pastas impressas. Como disse Sven, pode haver um problema com esses caminhos, e isso ajudará você a descobrir.Também você pode usar 'os.popen'. Exemplo:
Resultado:
fonte
subprocess
módulo."os.popen
não tem mais esse aviso e é simplesmente um invólucro finosubprocess.Popen()
agora.Para executar o comando sem um shell, passe o comando como uma lista e implemente o redirecionamento no Python usando
[subprocess]
:Nota: não
> test.nt
no final.stdout=file
implementa o redirecionamento.Para executar o comando usando o shell no Python, passe o comando como uma string e ative
shell=True
:Aqui está o shell é responsável pelo redirecionamento de saída (
> test.nt
está no comando).Para executar um comando bash que usa bashisms, especifique explicitamente o executável do bash, por exemplo, para emular a substituição do processo do bash :
fonte
.split()
não é adequado quando há seqüências de caracteres entre aspas etc. Há uma rotina separadashlex.split()
que lida com sintaxe de shell arbitrariamente complexa..split()
trabalhos neste caso.shlex.split()
às vezes pode ser útil, mas também pode falhar em alguns casos. Há muitas coisas que poderiam ser mencionadas. Você pode começar com o link para a descrição da tag do subprocesso fornecida acima.A maneira pitônica de fazer isso é usar
subprocess.Popen
subprocess.Popen
faz uma lista em que o primeiro elemento é o comando a ser executado, seguido por qualquer argumento de linha de comando.Como um exemplo:
fonte
echo -v '"Hello Again!"'
aspas simples entre aspas duplas.subprocesss.Popen
, você precisa gerenciar o objeto de processo resultante (no mínimo, execute await()
para impedir que ele se transforme em um processo zumbi).