9
votes

Comment concaténer plusieurs fichiers pour stdin de popen

Je porte un script Bash à Python 2.6 et je veux remplacer certains code:

p0 = Popen(["cat", "file1", "file2"...], stdout=PIPE)
p1 = ... stdin=p0.stdout ...


2 commentaires

Qu'est-ce que filtre fait? Avez-vous besoin d'appeler un programme externe pour cette fonctionnalité?


@Sven: Le filtre est un programme de cent ligne C ++ Traitement de l'entrée ~ 10 Go ... C'est un peu beaucoup à réécrire, et il est pratique l'avoir à des vitesses C ++ pour obtenir un retournement rapide d'édition / test. Donc, cela dit, un supplément popen / cat dans le contexte d'un travail non trivial n'est pas un problème significatif, il se sent simplement bâclé: -}.


4 Réponses :


1
votes

Cela devrait être facile. Tout d'abord, créez un tuyau à l'aide de OS.Pipe , puis populaire Le filtre avec la fin de lecture du tuyau en entrée standard. Ensuite, pour chaque fichier dans le répertoire avec nom correspondant au modèle, passez simplement son contenu à l'extrémité d'écriture du tuyau. Cela devrait être exactement le même que la commande shell chat ..._ *. LOG | Filtre args fait.

mise à jour: désolé, tuyau à partir de OS.Pipe n'est pas nécessaire, j'ai oublié que subprocess.popen (..., stdin = subprocess.pipe) actuellement crée un pour vous. De plus, un tuyau ne peut pas être bourré avec trop de données, plus de données ne peuvent être écrites sur un tuyau qu'après lecture des données précédentes.

donc la solution (par exemple avec wc -l ) serait: xxx

Utilisation Exemple: xxx


6 commentaires

Merci Messa - Ça va essayer, bien que je suis inquiet que si j'essaie d'écrire toutes les entrées ~ 10 Go sur le tuyau avant d'appeler p2.communicate () [0] il bloquera ou échouer après 64 kilo-octets ou quelle que soit la taille du tampon. Peut-être que j'aurai besoin d'un autre fil pour alimenter le tuyau pendant que communiquer fonctionne? Nous verrons comment ça se passe. Merci pour l'avenue d'exploration.


Je joue avec ceci: mes observations semblent confirmées par la réponse acceptée à Stackoverflow.com/questions/163542/... - que le filtre doit avoir STDIN = SUBPROCESSE.PIPEZ plutôt qu'un FD lisible renvoyé par OS.Pipe () . Une chose étrange est que je ne peux que communiquer () entre filtre_pipe.stdin.write (...) et ... . Fermer () , lequel suggère communiquer () n'est pas bloqué. Cela semble exclure l'approche du thread - communiquer () n'attendra pas que l'autre thread finit l'écriture. Trop de données pour mem ....


Vous pouvez raccourcir que pour CHUK IN ITER (Lambda: F.Read (8192), ''): p.stdin.write (chunk) ou p.stdin.writelines (ITER ( Lambda: F.Read (8192), '')) .


@MESSA, @ROSH: Vos solutions ont l'air très prometteuse - je vais les vérifier le matin et rapporter si elles fonctionnent bien avec les volumes d'entrée en question. Merci à vous deux pour votre temps et vos efforts.


@MESSA, ce n'est pas si facile et il ne bloquera certainement que si le sous-processus que vous ouvrez crée une importante sortie qui ne se termine pas avec un descripteur de fichier qui "écrit" automatiquement (E.G. est un tuyau). Preuve: Gist.github.com/979549 . De tels cas nécessitent une machinerie plus complexe avec des filets ou quelque chose du genre à vider l'autre extrémité du tuyau de sortie simultanément avec l'écriture sur le tuyau d'entrée. Vérifiez communiquer () implémentation dans subprocess.py .


@MESSA: Merci encore pour vos efforts et vos idées. Je pense que Abbot a raison à ce sujet et une approche comme Rosh's est nécessaire ... peu plus que cela ne vaut pour mes besoins actuels, mais bon à connaître. À votre santé.



2
votes

Si vous regardez à l'intérieur de la mise en œuvre du module sous-processus , vous verrez que STD {IN, OUT OUT, ERR} devrait être FileObjects prenant en charge Fileno () méthode, donc Un objet de type cas de concaturation simple avec une interface Python (ou même un objet StringIO) n'est pas approprié ici.

S'il s'agissait d'itérateurs, pas d'objets de fichier, vous pouvez utiliser ithertools.chain .

Bien sûr, sacrifier la consommation de mémoire que vous pouvez faire quelque chose comme ceci: xxx


4 commentaires

Merci pour l'explication RE FILENO () . Avec tant d'apport (~ 10 Go), une jointure en mémoire n'est probablement pas appropriée mais merci d'illustrer la technique. Donc, j'en sais plus sur ce que ne pas essayer! À votre santé.


@Tony, mon avis: Si vous avez un grand dossier, allez simplement avec popen (chat). Bien sûr, vous pouvez réimplément ceci en python, mais quoi? Ce sera un processus distinct de toute façon (si vous souhaitez autoriser sa sortie à l'objet popen), alors pourquoi ne pas utiliser d'outils standard?


Intéressant ... Je ne peux pas voir un moyen de le faire pour le moment, il n'est donc pas clair pour moi que s'il y a un moyen, cela impliquerait un processus séparé. Mais, je me sens de plus en plus à l'aise avec le popen (["chat" ...]) de toute façon ... pas une grosse affaire pour mon application spécifique, mais je viens de trouver difficile d'imaginer que En général, un processus externe serait la meilleure pratique pour cela. Sur le côté plus, popen (["chat" ...) est concis et facile compris ....


@Tony, ce n'est pas la méthode la plus efficace, mais subprocess.popen ne fournit pas une API commode pour une solution efficace pour votre cas. Cochez les sources de modules sous-processus , implémentation de la méthode communiquer () . Pour une solution efficace, vous devez le répliquer en remplacement de son argument d'entrée avec un itérateur qui retourne des agglomérations.



1
votes

Lorsque vous utilisez Subprocess, vous devez envisager le fait que Popen interne utilisera le descripteur de fichier (gestionnaire) et appelera OS.dup2 () pour STDIN, STDOUT et STDERR avant de les transmettre au processus d'enfant créé.

si si Vous ne voulez pas utiliser de tuyau de coque système avec popen: xxx

Je pense que votre autre option consiste à écrire une fonction CAT en Python et à générer un fichier en forme de chat et passez ce fichier à P1 STDIN, ne pensez pas à une classe qui implémente l'API de l'IO car elle ne fonctionnera pas comme je l'ai dit, car à l'interne, le processus de l'enfant n'aura pas Les descripteurs de fichier.

avec qui dit, je pense que votre meilleure option est d'utiliser UNIX Tipy Way comme dans Subprocess Doc .


4 commentaires

Je suis avec vous jusqu'à la dernière ligne ... par "Première solution" Signifiez-vous la population de chat que vous montrez au-dessus de «la solution triviale»? Le lien que vous avez fourni discute de l'importance de la fermeture du stdout pour des tuyaux antérieurs ... J'aurais manqué que tellement merci! Je commence à penser que cela pourrait être aussi bon que possible, mais laissera la question ouverte un moment plus longtemps ....


@Tony: Oui par "première solution", je faisais référence à la "solution triviale" :), mais après votre commentaire, je ne pense pas que c'était bon d'appeler "solution triviale" pour la solution de conduite de la conduite :). édité


BTW / Je ne devrais pas vraiment créer un fichier temporaire avec la sortie concaténée, car je veux vous assurer que le programme fonctionne même lorsqu'il y a un espace libre minimal sur les disques (tous trop courants, semble que les grandes banques ont toutes des personnes dont le travail est de contraindre le disque Utilisation à des quantités de manière frustrante, même sur des machines de produit: - /).


@Tony: Oui, une autre raison d'utiliser le chat et la pipe :)



6
votes

Vous pouvez remplacer tout ce que vous faites avec le code Python, à l'exception de votre utilitaire externe. De cette façon, votre programme restera portable tant que votre util extérieur est portable. Vous pouvez également envisager de tourner le programme C ++ dans une bibliothèque et d'utiliser Cyron pour interfacer avec elle. Comme Messa a montré, date code> est remplacé par TIME.StrfTime code>, globbing est effectué avec glob.glob code> et chat code> peut Soyez remplacé par la lecture de tous les fichiers de la liste et les écrire à l'entrée de votre programme. L'appel à bzip2 code> peut être remplacé par le module bz2 code>, mais cela compliquera votre programme car vous devriez lire et écrire simultanément. Pour ce faire, vous devez soit utiliser p.communicate code> ou un thread si les données sont énormes ( Select.Select.Sélectionnez code> serait un meilleur choix, mais cela ne fonctionnera pas sur Windows).

import os
import gzip
import bz2
try:
    import magic
except ImportError:
    has_magic = False
else:
    has_magic = True


mime_openers = {
    'application/x-bzip2': bz2.BZ2File,
    'application/x-gzip': gzip.GzipFile,
}

ext_openers = {
    '.bz2': bz2.BZ2File,
    '.gz': gzip.GzipFile,
}


def open_autodecompress(filename, mode='r'):
    if has_magic:
        ms = magic.open(magic.MAGIC_MIME_TYPE)
        ms.load()
        mimetype = ms.file(filename)
        opener = mime_openers.get(mimetype, open)
    else:
        basepart, ext = os.path.splitext(filename)
        opener = ext_openers.get(ext, open)
    return opener(filename, mode)


2 commentaires

Salut Rosh. Merci encore pour cela - code élégant et concis. Néanmoins, j'ai trouvé que certaines de mes intrants sur certains hôtes sont compressées (GZIP ou BZIP2) plus tôt dans la journée. Il est donc réellement concis d'utiliser bzcat --force dans le pipeline (bien que je ' m Assurez-vous avec quelques modifications supplémentaires La bibliothèque BZ2 que vous utilisez ci-dessus pourrait également satisfaire à la fin de l'entrée). Mais - paille qui a brisé le dos du chameau et tout ça. Je vais essayer cela plus tard pour mon intérêt, mais je préfère la réassurance de quelque chose de mort-simple pour cette utilisation de la production. En espérant que d'autres trouveront cette question / réponse utile. À votre santé.


J'ai ajouté un exemple de la manière dont vous pouvez détecter le type de fichier des fichiers d'entrée lors de la lecture, si vous le trouvez utile.