Pergunta

Como faço para executar o seguinte comando shell usando o Python subprocess módulo?

echo "input data" | awk -f script.awk | sort > outfile.txt

Os dados de entrada virá de uma corda, então eu realmente não precisa echo. Eu tenho até aqui, alguém pode explicar como eu obtê-lo a tubulação através sort também?

p_awk = subprocess.Popen(["awk","-f","script.awk"],
                          stdin=subprocess.PIPE,
                          stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )

Atualizar : Nota que, enquanto a resposta aceite abaixo na verdade não responder à pergunta como pediu, eu acredito S.Lott é certo eo que é melhor para evitar ter de resolver esse problema em primeiro lugar !

Foi útil?

Solução

Você seria um pouco mais feliz com o seguinte.

import subprocess

awk_sort = subprocess.Popen( "awk -f script.awk | sort > outfile.txt",
    stdin=subprocess.PIPE, shell=True )
awk_sort.communicate( b"input data\n" )

delegar parte do trabalho para o shell. Deixá-lo conectar dois processos com um pipeline.

Você ficaria muito mais feliz reescrita 'script.awk' em Python, eliminando awk e do gasoduto.

Editar . Algumas das razões para sugerir que awk não está ajudando.

[Há muitas razões para responder através de comentários.]

  1. Awk está adicionando um passo de nenhum valor significativo. Não há nada especial sobre o processamento do awk que Python não controla.

  2. tempo de processamento

    O pipeline de awk para classificar, para grandes conjuntos de dados, pode melhorar decorrido. Para conjuntos curtos de dados, ele não tem nenhum benefício significativo. Uma medição rápida de awk >file ; sort file e awk | sort irá revelar de simultaneidade ajuda. Com sorte, ele raramente ajuda porque espécie não é um filtro, uma vez-through.

  3. A simplicidade de "Python para ordenar" o processamento (em vez de "Python para awk à sorte") impede que o exato tipo de perguntas feitas aqui.

  4. Python - enquanto verborrágico de awk -. Também é explícita, onde awk tem certas regras implícitas que são opacos para iniciantes, e confuso para os não-especialistas

  5. Awk (como o shell script em si), adiciona uma nova linguagem de programação. Se tudo isso pode ser feito em uma linguagem (Python), eliminando o reservatório eo awk programação elimina duas linguagens de programação, permitindo que alguém para se concentrar nas partes produtoras de valor da tarefa.

A linha inferior: awk não pode adicionar um valor significativo. Neste caso, awk é um custo líquido; acrescentou o suficiente complexidade que era necessário para fazer esta pergunta. Removendo awk será um ganho líquido.

Sidebar Por que a construção de um pipeline (a | b) é tão difícil.

Quando o shell é confrontado com a | b ele tem que fazer o seguinte.

  1. Fork um processo filho do shell originais. Isto acabará por se tornar b.

  2. Construir uma tubulação de os. (Não um subprocess.PIPE Python), mas os.pipe() chamada que retorna dois novos descritores de arquivos que estão conectados via tampão comum. Neste ponto, o processo tem stdin, stdout, stderr de seu pai, além de um arquivo que será "stdout de um" e "stdin do b".

  3. Fork uma criança. A criança substitui seu stdout com stdout do novo um. Exec o processo a.

  4. A criança fecha b substitui seu stdin com stdin do novo b. Exec o processo b.

  5. Os b criança aguarda uma para completar.

  6. O pai está esperando por b para ser concluído.

Eu acho que o acima pode ser usado de forma recursiva para a | b | c desova, mas você tem que entre parênteses implicitamente pipelines longos, tratando-os como se eles estão a | (b | c).

Desde Python tem os.pipe(), os.exec() e os.fork(), e você pode substituir sys.stdin e sys.stdout, há uma maneira de fazer o acima em puro Python. Na verdade, você pode ser capaz de trabalhar fora alguns atalhos usando os.pipe() e subprocess.Popen.

No entanto, é mais fácil delegar essa operação para o shell.

Outras dicas

import subprocess

some_string = b'input_data'

sort_out = open('outfile.txt', 'wb', 0)
sort_in = subprocess.Popen('sort', stdin=subprocess.PIPE, stdout=sort_out).stdin
subprocess.Popen(['awk', '-f', 'script.awk'], stdout=sort_in, 
                 stdin=subprocess.PIPE).communicate(some_string)

Para emular um gasoduto shell:

from subprocess import check_call

check_call('echo "input data" | a | b > outfile.txt', shell=True)

sem invocar o shell (ver 17.1.4.2. Substituir shell gasoduto):

#!/usr/bin/env python
from subprocess import Popen, PIPE

a = Popen(["a"], stdin=PIPE, stdout=PIPE)
with a.stdin:
    with a.stdout, open("outfile.txt", "wb") as outfile:
        b = Popen(["b"], stdin=a.stdout, stdout=outfile)
    a.stdin.write(b"input data")
statuses = [a.wait(), b.wait()] # both a.stdin/stdout are closed already

plumbum fornece um pouco de açúcar sintaxe:

#!/usr/bin/env python
from plumbum.cmd import a, b # magic

(a << "input data" | b > "outfile.txt")()

O análogo de:

#!/bin/sh
echo "input data" | awk -f script.awk | sort > outfile.txt

é:

#!/usr/bin/env python
from plumbum.cmd import awk, sort

(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()

http://www.python.org/doc/2.5.2/lib/node535.html cobriu isso muito bem. Existe alguma parte deste você não entendeu?

Seu programa seria muito semelhante, mas o segundo Popen teria stdout = para um arquivo, e você não precisa a saída de seu .communicate().

Inspirado pela resposta da @ Cristian. Conheci apenas o mesmo problema, mas com um comando diferente. Então, eu estou colocando meu exemplo testado, o que eu acredito que poderia ser útil:

grep_proc = subprocess.Popen(["grep", "rabbitmq"],
                             stdin=subprocess.PIPE, 
                             stdout=subprocess.PIPE)
subprocess.Popen(["ps", "aux"], stdout=grep_proc.stdin)
out, err = grep_proc.communicate()

Esta é testada.

O que foi feito

  • Declarado execução grep preguiçoso com stdin da tubulação. Este comando será executado na execução do comando ps quando o tubo será preenchido com o stdout de ps.
  • Chamado de ps comando primário com stdout direcionado para o tubo utilizado pelo comando grep.
  • Grep comunicado para obter stdout do tubo.

Eu gosto desta maneira porque é a concepção tubo naturais delicadamente enrolado com interfaces subprocess.

A resposta aceita é contornar a questão. Aqui está um trecho que as cadeias a saída de vários processos: Note que também imprime o (um pouco) o comando equivalente shell para que você possa executá-lo e certifique-se a saída está correta.

#!/usr/bin/env python3

from subprocess import Popen, PIPE

# cmd1 : dd if=/dev/zero bs=1m count=100
# cmd2 : gzip
# cmd3 : wc -c
cmd1 = ['dd', 'if=/dev/zero', 'bs=1M', 'count=100']
cmd2 = ['tee']
cmd3 = ['wc', '-c']
print(f"Shell style : {' '.join(cmd1)} | {' '.join(cmd2)} | {' '.join(cmd3)}")

p1 = Popen(cmd1, stdout=PIPE, stderr=PIPE) # stderr=PIPE optional, dd is chatty
p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd3, stdin=p2.stdout, stdout=PIPE)

print("Output from last process : " + (p3.communicate()[0]).decode())

# thoretically p1 and p2 may still be running, this ensures we are collecting their return codes
p1.wait()
p2.wait()
print("p1 return: ", p1.returncode)
print("p2 return: ", p2.returncode)
print("p3 return: ", p3.returncode)

EDIT: pipes está disponível no Windows, mas, crucialmente, não parece realmente trabalho no Windows. Ver comentários abaixo.

O Python biblioteca padrão agora inclui o módulo pipes para manusear este:

https://docs.python.org/2/library/pipes.html , https://docs.python.org/3.4/library/pipes.html

Eu não sei quanto tempo este módulo tem sido em torno, mas esta abordagem parece ser muito mais simples do que mucking com subprocess.

As respostas anteriores perdeu um ponto importante. Substituindo gasoduto shell é basicamente correta, como fora pontas por geocar. É quase suficiente para executar communicate no último elemento do tubo.

O problema restante é passar os dados de entrada para o pipeline. Com vários subprocessos, um simples communicate(input_data) no último elemento não funciona - ele trava para sempre. Você precisa criar um um gasoduto e uma criança manualmente assim:

import os
import subprocess

input = """\
input data
more input
""" * 10

rd, wr = os.pipe()
if os.fork() != 0: # parent
    os.close(wr)
else:              # child
    os.close(rd)
    os.write(wr, input)
    os.close(wr)
    exit()

p_awk = subprocess.Popen(["awk", "{ print $2; }"],
                         stdin=rd,
                         stdout=subprocess.PIPE)
p_sort = subprocess.Popen(["sort"], 
                          stdin=p_awk.stdout,
                          stdout=subprocess.PIPE)
p_awk.stdout.close()
out, err = p_sort.communicate()
print (out.rstrip())

Agora, a criança fornece a entrada através do tubo, e as chamadas de pais comunicar (), que funciona como o esperado. Com esta abordagem, você pode criar pipelines longos arbitrárias, sem recorrer a "parte delegar parte do trabalho para o shell". Infelizmente, a documentação subprocess não menciona isso.

Existem maneiras de conseguir o mesmo efeito sem tubos:

from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)

Agora uso stdin=tf para p_awk. É uma questão de gosto que você preferir.

O acima ainda não é 100% em equivalentes de condutas Bash porque o manuseamento de sinal é diferente. Você pode ver isso se você adicionar outro elemento tubo que trunca a saída do sort, por exemplo, head -n 10. Com o código acima, sort irá imprimir uma mensagem de erro "cano quebrado" para stderr. Você não verá essa mensagem quando você executa o mesmo oleoduto no shell. (Essa é a única diferença, porém, o resultado em stdout é o mesmo). A razão parece ser sets Popen que de python SIG_IGN para SIGPIPE, enquanto que as folhas de casca de TI em SIG_DFL e manipulação de sinais de sort é diferente nestes dois casos.

Para mim, a seguir abordagem é a mais limpa e mais fácil de ler

from subprocess import Popen, PIPE

def string_to_2_procs_to_file(input_s, first_cmd, second_cmd, output_filename):
    with open(output_filename, 'wb') as out_f:
        p2 = Popen(second_cmd, stdin=PIPE, stdout=out_f)
        p1 = Popen(first_cmd, stdout=p2.stdin, stdin=PIPE)
        p1.communicate(input=bytes(input_s))
        p1.wait()
        p2.stdin.close()
        p2.wait()

que pode ser chamado assim:

string_to_2_procs_to_file('input data', ['awk', '-f', 'script.awk'], ['sort'], 'output.txt')
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top