Estou tentando entender qual é a motivação por trás do uso das funções de biblioteca do Python para executar tarefas específicas do SO, como criar arquivos / diretórios, alterar atributos de arquivo etc. em vez de apenas executar esses comandos via os.system()
ou subprocess.call()
?
Por exemplo, por que eu gostaria de usar em os.chmod
vez de usar os.system("chmod...")
?
Entendo que é mais "pitônico" usar os métodos de biblioteca disponíveis do Python, tanto quanto possível, em vez de apenas executar comandos shell diretamente. Mas, existe alguma outra motivação por trás disso, do ponto de vista da funcionalidade?
Estou apenas falando sobre a execução de comandos simples de uma linha aqui. Quando precisamos de mais controle sobre a execução da tarefa, entendo que usarsubprocess
módulo faz mais sentido, por exemplo.
fonte
print
quando você podeos.system("echo Hello world!")
?os.path
para manipular caminhos em vez de manipulá-los manualmente: ele funciona em todos os sistemas operacionais em que é executado.os.chmod
não chama ochmod
programa que o shell chamaria. Usandoos.system('chmod ...')
lança um shell de interpretar uma string para chamar outro executável para fazer uma chamada para o Cchmod
função, enquantoos.chmod(...)
vai muito mais diretamente ao Cchmod
.Respostas:
É mais rápido ,
os.system
esubprocess.call
criar novos processos que é desnecessário para algo tão simples. De fato,os.system
esubprocess.call
com oshell
argumento geralmente crie pelo menos dois novos processos: o primeiro sendo o shell e o segundo o comando que você está executando (se não for um shell embutidotest
).Alguns comandos são inúteis em um processo separado . Por exemplo, se você executar
os.spawn("cd dir/")
, ele mudará o diretório de trabalho atual do processo filho, mas não do processo Python. Você precisa usaros.chdir
para isso.Você não precisa se preocupar com caracteres especiais interpretados pelo shell.
os.chmod(path, mode)
funcionará independentemente do nome do arquivo, enquantoos.spawn("chmod 777 " + path)
falhará horrivelmente se o nome do arquivo for algo parecido; rm -rf ~
. (Observe que você pode solucionar isso se usarsubprocess.call
sem oshell
argumento.)Você não precisa se preocupar com nomes de arquivos que começam com um traço .
os.chmod("--quiet", mode)
alterará as permissões do arquivo nomeado--quiet
, masos.spawn("chmod 777 --quiet")
falhará, como--quiet
é interpretado como um argumento. Isso é verdade mesmo parasubprocess.call(["chmod", "777", "--quiet"])
.Você tem menos preocupações entre plataformas e shell, pois a biblioteca padrão do Python deve lidar com isso para você. Seu sistema possui
chmod
comando? Está instalado? Ele suporta os parâmetros que você espera? Oos
módulo tentará ser o mais multiplataforma possível e documentará quando isso não for possível.Se o comando que você está executando tiver uma saída com a qual você se preocupa, é necessário analisá-lo, o que é mais complicado do que parece, pois você pode esquecer as maiúsculas (nomes de arquivos com espaços, guias e novas linhas), mesmo quando você não se importa com portabilidade.
fonte
ls
oudir
é bastante alto para certos tipos de desenvolvedores, exatamente comobash
oucmd
ouksh
ou qualquer outro shell que você preferir.ls
é de nível superior a esse, pois não é uma chamada direta à API do kernel do seu sistema operacional. É uma aplicação (pequena).ls
oudir
masopendir()/readdir()
(api linux) ouFindFirstFile()/FindNextFile()
(windows api) ouFile.listFiles
(API Java) ouDirectory.GetFiles()
(C #). Tudo isso está intimamente ligado a uma chamada direta ao sistema operacional. Alguns podem ser tão simples quanto inserir um número em um registro e chamarint 13h
para ativar o modo kernel.É mais seguro. Para lhe dar uma idéia, aqui está um exemplo de script
Se a entrada do usuário foi
test; rm -rf ~
essa, excluiria o diretório inicial.É por isso que é mais seguro usar a função incorporada.
Por isso, você deve usar o subprocesso em vez do sistema também.
fonte
Existem quatro casos fortes para preferir os métodos mais específicos do Python no
os
módulo ao invés de usaros.system
ou nosubprocess
módulo ao executar um comando:os
módulo estão disponíveis em várias plataformas, enquanto muitos comandos do shell são específicos do sistema operacional.os
módulo.Redundância (consulte código redundante ):
Na verdade, você está executando um "intermediário" redundante no caminho para as eventuais chamadas do sistema (
chmod
no seu exemplo). Esse intermediário é um novo processo ou sub-shell.De
os.system
:E
subprocess
é apenas um módulo para gerar novos processos.Você pode fazer o que precisa sem gerar esses processos.
Portabilidade (consulte portabilidade do código fonte ):
O
os
objetivo do módulo é fornecer serviços genéricos de sistema operacional e sua descrição começa com:Você pode usar
os.listdir
no Windows e no Unix. Tentar usaros.system
/subprocess
para esta funcionalidade forçará você a manter duas chamadas (parals
/dir
) e verificar em qual sistema operacional você está. Isto não é tão portátil e vai causar ainda mais frustração mais tarde (ver saída Handling ).Compreendendo os resultados do comando:
Suponha que você queira listar os arquivos em um diretório.
Se você estiver usando
os.system("ls")
/subprocess.call(['ls'])
, poderá recuperar a saída do processo, que é basicamente uma grande string com os nomes dos arquivos.Como você pode distinguir um arquivo com um espaço no nome de dois arquivos?
E se você não tiver permissão para listar os arquivos?
Como você deve mapear os dados para objetos python?
Isso está fora da minha cabeça e, embora haja soluções para esses problemas - por que resolver novamente um problema que foi resolvido para você?
Este é um exemplo de seguir o princípio Não se repita (geralmente chamado de "SECO") por não repetir uma implementação que já existe e está disponível gratuitamente para você.
Segurança:
os.system
esubprocess
são poderosos. É bom quando você precisa desse poder, mas é perigoso quando não precisa. Quando você usaos.listdir
, sabe que não pode fazer mais nada além de listar arquivos ou gerar um erro. Quando você usaos.system
ousubprocess
obtém o mesmo comportamento, pode acabar fazendo algo que não pretendia fazer.Segurança de injeção (veja exemplos de injeção de casca ) :
Se você usa a entrada do usuário como um novo comando, basicamente dá a ele um shell. É muito parecido com a injeção de SQL, fornecendo um shell no banco de dados para o usuário.
Um exemplo seria um comando do formulário:
Isso pode ser facilmente explorado para executar qualquer código arbitrário usando a entrada:
NASTY COMMAND;#
para criar o eventual:Existem muitos desses comandos que podem colocar seu sistema em risco.
fonte
Por uma razão simples - quando você chama uma função de shell, ela cria uma sub-shell que é destruída após a existência do seu comando; portanto, se você alterar o diretório em um shell - isso não afeta seu ambiente no Python.
Além disso, a criação de sub shell é demorada, portanto, o uso direto de comandos do SO afetará seu desempenho
EDITAR
Eu tive alguns testes de tempo em execução:
Função interna é executada mais de 10 vezes mais rápido
EDIT2
Pode haver casos em que invocar executável externo possa produzir melhores resultados do que pacotes Python - acabei de lembrar de um email enviado por um colega meu que o desempenho do gzip chamado através do subprocesso era muito maior do que o desempenho de um pacote Python que ele usava. Mas certamente não quando estamos falando de pacotes padrão de SO que emulam comandos padrão do SO
fonte
%
o intérprete normal.time <path to script>
terminal e ele informará o tempo real, o usuário e o processo gasto. Ou seja, se você não possui o iPython e tem acesso à linha de comando do Unix.As chamadas de shell são específicas do SO, enquanto as funções do módulo OS do Python não são, na maioria dos casos. E evita gerar um subprocesso.
fonte
É muito mais eficiente. O "shell" é apenas outro binário do SO que contém muitas chamadas de sistema. Por que incorrer na sobrecarga de criação de todo o processo de shell apenas para essa chamada de sistema única?
A situação é ainda pior quando você usa
os.system
algo que não é um shell embutido. Você inicia um processo de shell que, por sua vez, inicia um executável que, a dois processos de distância, faz a chamada do sistema. Pelo menossubprocess
teria removido a necessidade de um processo intermediário de shell.Não é específico para Python, isso.
systemd
é uma melhoria nos tempos de inicialização do Linux pelo mesmo motivo: ele faz o sistema necessário se autodenominar em vez de gerar milhares de conchas.fonte