Como redirecionar a saída com subprocesso em Python?

97

O que eu faço na linha de comando:

cat file1 file2 file3 > myfile

O que eu quero fazer com o python:

import subprocess, shlex
my_cmd = 'cat file1 file2 file3 > myfile'
args = shlex.split(my_cmd)
subprocess.call(args) # spits the output in the window i call my python program
catatemypythoncode
fonte
A execução de tal comando no subprocesso não forneceria nenhuma saída. Pode ser que você queira executá-lo sem > myfile redirecionando a saída de cat file1 file2 file3 para python?
PoltoS
@PoltoS Quero juntar alguns arquivos e depois processar o arquivo resultante. Achei que usar o gato era a alternativa mais fácil. Existe uma maneira melhor / pythônica de fazer isso?
catatemypythoncode
os.sendfile()solução baseada é possível, ver reproduzir o comando cat unix em python
jfs
1
Eu acho que o redirecionamento de saída ('>' ou '>>') não funciona em subprocess.Popen (pelo menos em Python 2.7) (em shell = modo True) Neste exemplo, como outros apontam, você pode contornar isso por não usar o redirecionamento, mas em outros casos o redirecionamento é útil. Se o redirecionamento ou tubulação não for suportado no subprocess.Popen deve ser documentado (e / ou os.system () não deve ser descontinuado até que seja corrigido)
Ribo

Respostas:

20

ATUALIZAÇÃO: os.system não é recomendado, embora ainda esteja disponível no Python 3.


Use os.system:

os.system(my_cmd)

Se você realmente deseja usar o subprocesso, aqui está a solução (principalmente retirada da documentação do subprocesso):

p = subprocess.Popen(my_cmd, shell=True)
os.waitpid(p.pid, 0)

OTOH, você pode evitar chamadas de sistema completamente:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)
Marcelo Cantos
fonte
1
Funciona, mas deixe-me perguntar então: qual é o objetivo da biblioteca de subprocesso se os.system já faz o trabalho? Tenho a sensação de que deveria ter usado o subprocesso em vez disso, pois é uma biblioteca dedicada a essa tarefa, embora, como estou fazendo isso só para mim, estarei bem usando os.system desta vez.
catatemypythoncode
A biblioteca de subprocessos é muito mais flexível os.systeme pode modelar com os.systemprecisão, mas também é mais complexa de se trabalhar.
Marcelo Cantos
13
os.systemveio antes subprocess. O primeiro é uma API legada que o último pretende substituir.
Santa
5
@catatemypythoncode: você não deve usar os.system()ou shell=True. Para redirecionar a saída de um subprocesso, use o stdoutparâmetro mostrado na resposta de Ryan Thompson . Embora você não precise de um subprocesso ( cat) no seu caso, você pode concatenar arquivos usando Python puro.
jfs
4
OTOH = Por outro lado
Cephlin
271

No Python 3.5+, para redirecionar a saída, basta passar um identificador de arquivo aberto para o stdoutargumento para subprocess.run:

# Use a list of args instead of a string
input_files = ['file1', 'file2', 'file3']
my_cmd = ['cat'] + input_files
with open('myfile', "w") as outfile:
    subprocess.run(my_cmd, stdout=outfile)

Como outros apontaram, o uso de um comando externo como catpara este propósito é completamente estranho.

Ryan C. Thompson
fonte
9
Esta deve ser a resposta para a questão geral de tubulação ao usar o shell do Python
Kaushik Ghose
46
Esta é a resposta correta, não aquela marcada como correta.
Justin Blake,
7
Para Python 3.5+ use subprocess.run(my_cmd, stdout=outfile)que está substituindosubprocess.call(...)
Austin Yates
1
Observe que isso não funciona com objetos de arquivo personalizados, se eles não tiverem um campo fileno (se não forem um arquivo real).
Eliezer Miron
1
Como o Python <3.5 está obsoleto a partir de agora, atualizei a resposta com seu comentário, @AustinYates.
Greg Dubicki
5

@PoltoS Quero juntar alguns arquivos e depois processar o arquivo resultante. Achei que usar o gato era a alternativa mais fácil. Existe uma maneira melhor / pythônica de fazer isso?

Claro:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())
SingleNegationElimination
fonte
1
size = 'ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 dump.mp4 > file'
proc = subprocess.Popen(shlex.split(size), shell=True)
time.sleep(1)
proc.terminate() #proc.kill() modify it by a suggestion
size = ""
with open('file', 'r') as infile:
    for line in infile.readlines():
        size += line.strip()

print(size)
os.remove('file')

Quando você usa um subprocesso , o processo deve ser eliminado. Este é um exemplo. Se você não encerrar o processo, o arquivo ficará vazio e você não poderá ler nada . Ele pode ser executado no Windows . Não posso ter certeza de que pode executado em Unix.

wyx
fonte
1
É um exemplo de código ruim (que não vai funcionar em Unix, que demonstra más práticas for line in .readlines():, s +=) e proc.kill()pode levar à perda de informação em geral (não permitir que o subprocesso de encerrar graciosamente (no Unix) - conteúdo unflushed está perdido ) De qualquer forma, a nota sobre o buffer é mais apropriada como um comentário.
jfs
Eu executo no Windows está OK (porque kill é igual a encerrar no Windows). No Unix, você deve usar proc.terminate (). @ JF Sebastian Não tenho Sistema Unix no meu computador.
wyx de
Se você estiver no Windows, em seguida, deixar cair shlex.split(), gota shell=True, gota >file, gota open(), etc e uso stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]em vez de. Aqui está um exemplo completo . Mais soluções: Parar de ler a saída do processo em Python sem travar? Nota: não há nenhum requisito na questão de que você precise encerrar o processo filho manualmente - você pode resolver outros problemas, por exemplo, um processo pode se comportar de maneira diferente se seu stdout for um tty, mas estiver fora do tópico.
jfs de
0

Um caso interessante seria atualizar um arquivo anexando um arquivo semelhante a ele. Assim, não seria necessário criar um novo arquivo no processo. É particularmente útil no caso em que um arquivo grande precisa ser anexado. Aqui está uma possibilidade usando a linha de comando final diretamente do python.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
DJJ
fonte