Comment rediriger la sortie avec un sous-processus en Python?

97

Ce que je fais dans la ligne de commande:

cat file1 file2 file3 > myfile

Ce que je veux faire avec 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
la source
L'exécution d'une telle commande dans un sous-processus ne vous donnerait aucune sortie. Peut-être voulez-vous l'exécuter sans > myfile redirigeant la sortie de cat file1 file2 file3 vers python?
PoltoS
@PoltoS Je souhaite joindre certains fichiers, puis traiter le fichier résultant. Je pensais qu'utiliser le chat était l'alternative la plus simple. Y a-t-il une meilleure façon / pythonique de le faire?
catatemypythoncode
os.sendfile()-une solution basée sur est possible, voir Reproduire la commande unix cat en python
jfs
1
Je pense que la redirection de sortie ('>' ou '>>') ne fonctionne pas dans subprocess.Popen (au moins en Python 2.7) (en mode shell = True) Dans cet exemple, comme d'autres le soulignent, vous pouvez contourner ceci en n'utilisant pas la redirection, mais dans d'autres cas, la redirection est utile. Si la redirection ou la tuyauterie n'est pas prise en charge dans le sous-processus, l'ouverture doit être documentée (et / ou os.system () ne doit pas être obsolète jusqu'à ce que cela soit corrigé)
Ribo

Réponses:

20

MISE À JOUR: os.system est déconseillé, bien qu'il soit toujours disponible en Python 3.


Utilisez os.system:

os.system(my_cmd)

Si vous voulez vraiment utiliser un sous-processus, voici la solution (principalement tirée de la documentation pour le sous-processus):

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

OTOH, vous pouvez éviter complètement les appels système:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)
Marcelo Cantos
la source
1
Cela fonctionne, mais permettez-moi de vous demander alors: à quoi sert la bibliothèque de sous-processus si os.system fait déjà le travail? J'ai l'impression que j'aurais dû utiliser un sous-processus à la place car c'est une bibliothèque dédiée à cette tâche, bien que puisque je le fais juste pour moi, je serai bien en utilisant os.system cette fois.
catatemypythoncode
La bibliothèque de sous-processus est beaucoup plus flexible os.systemet peut modéliser os.systemavec précision, mais elle est également plus complexe à utiliser.
Marcelo Cantos
13
os.systemest venu avant subprocess. Le premier est une ancienne API que le second a l'intention de remplacer.
Santa
5
@catatemypythoncode: vous ne devez pas utiliser os.system()ou shell=True. Pour rediriger la sortie d'un sous-processus, utilisez le stdoutparamètre comme indiqué dans la réponse de Ryan Thompson . Bien que vous n'ayez pas besoin d'un sous-processus ( cat) dans votre cas, vous pouvez concaténer des fichiers en utilisant du Python pur.
jfs
4
OTOH = D'autre part
Cephlin
272

Dans Python 3.5+ pour rediriger la sortie, passez simplement un handle de fichier ouvert pour l' stdoutargument à 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)

Comme d'autres l'ont souligné, l'utilisation d'une commande externe comme catcelle-ci est totalement étrangère.

Ryan C. Thompson
la source
9
Cela devrait être la réponse à la question générale de la tuyauterie lors de l'utilisation du shell de Python
Kaushik Ghose
46
C'est la bonne réponse, pas celle marquée comme correcte.
Justin Blake
7
Pour Python 3.5+, utilisez subprocess.run(my_cmd, stdout=outfile)qui remplacesubprocess.call(...)
Austin Yates
1
À noter, cela ne fonctionne pas avec les objets de fichier personnalisés, s'ils n'ont pas de champ fileno (s'ils ne sont pas un vrai fichier.)
Eliezer Miron
1
Comme Python <3.5 est obsolète pour le moment, j'ai mis à jour la réponse avec votre commentaire, @AustinYates.
Greg Dubicki
5

@PoltoS Je souhaite joindre certains fichiers, puis traiter le fichier résultant. Je pensais qu'utiliser le chat était l'alternative la plus simple. Y a-t-il une meilleure façon / pythonique de le faire?

Bien sûr:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())
SingleNegationElimination
la source
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')

Lorsque vous utilisez un sous - processus , le processus doit être tué.C'est un exemple.Si vous ne tuez pas le processus, le fichier sera vide et vous ne pourrez rien lire.Il peut fonctionner sous Windows.Je ne peux pas m'assurer qu'il peut fonctionne sous Unix.

wyx
la source
1
Il est un mauvais exemple de code (il ne fonctionnera pas sur Unix, il démontre les mauvaises pratiques for line in .readlines():, s +=) et proc.kill()peut entraîner une perte d'information en général (il ne permet pas de mettre fin à la sous - processus gracieusement (sous Unix) - contenu unflushed est perdu ). Quoi qu'il en soit, la note sur la mise en mémoire tampon est plus appropriée comme commentaire.
jfs
Je l'exécute sous Windows, c'est OK (car tuer est égal à se terminer sous Windows). Sous Unix, vous devriez peut-être utiliser proc.terminate (). @ JF Sebastian Je n'ai pas de système Unix sur mon ordinateur.
wyx
Si vous êtes sous Windows puis déposez shlex.split(), goutte shell=True, goutte >file, goutte open(), etc et de l' utilisation stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]au lieu. Voici un exemple complet . Plus de solutions: arrêter de lire la sortie du processus en Python sans se bloquer? Remarque: il n'y a aucune exigence dans la question que vous deviez terminer le processus enfant manuellement - vous pouvez résoudre d'autres problèmes, par exemple, un processus peut se comporter différemment si son stdout est un tty mais il est hors sujet.
jfs
0

Un cas intéressant serait de mettre à jour un fichier en y ajoutant un fichier similaire. Ensuite, on n'aurait pas à créer un nouveau fichier dans le processus. Il est particulièrement utile dans le cas où un fichier volumineux doit être ajouté. Voici une possibilité d'utiliser la ligne de commande teminal directement à partir de python.

import subprocess32 as sub

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