ruby – IO.popen ne fonctionne pas boiteux encodage stdin et stdout

J’ai travaillé avec des pipes et IO.popen spécifiquement dans Ruby et j’ai rencontré un problème que je ne peux pas comprendre. J’essaie d’écrire des données binarys du processus flac processus lame dans un fichier. La structure de code que j’utilise est ci-dessous.

 # file paths file = Pathname.new('example.flac').realpath dest = Pathname.new('example.mp3') # execute the process and return the IO object wav = IO.popen("flac --decode --stdout \"#{file}\"", 'rb') lame = IO.popen("lame -V0 --vbr-new - -", 'r+b') # write output from wav to the lame IO object lame << wav.read # close pipe for writing (should indicate to the # process that input for stdin is finished). lame.close_write # open up destiniation file and write from lame stdout dest.open('wb'){|out| out << lame.read } # close all pipes wav.close lame.close 

Cependant, cela ne fonctionne pas. Après l’exécution de flac , le script se bloque et rest inactif (pas d’utilisation du processeur). Aucune erreur ou exception ne se produit .

J’utilise cygwin sur Windows 7, avec le package cygwin ruby ​​( 1.9.3p429 (2013-05-15) [i386-cygwin] ).

Je dois faire quelque chose de mal, toute aide est très appréciée. Merci!

EXTRA # 1

Je veux insérer et extraire les données binarys du processus lame car j’essaie de créer une plate-forme indépendante ( support Ruby limité bien sûr ) pour transcoder les fichiers audio, et le binary Windows de lame ne supporte que les noms de chemin Windows. pas cygwin.

EDIT # 1

J’ai lu à certains endroits (je n’ai pas enregistré les URL, je vais essayer de les rechercher dans l’historique de mon navigateur) que IO.popen a connu des problèmes avec les processus de blocage dans Windows et que cela pourrait être le cas.

J’ai joué avec d’autres bibliothèques, y compris Open3.popen3 et Open4 , mais en suivant une structure de code très similaire à celle ci-dessus, le processus lame est toujours bloqué et rest insensible.

EDIT # 2

J’ai trouvé cet article qui parlait des limitations de cmd.exe de Windows et de la manière dont il empêche l’utilisation de données en streaming depuis des fichiers vers stdin.

J’ai refait mon code pour ressembler à celui illustré ci-dessous pour le tester, et au fur et à mesure, il se bloque sur stdin write. Si je retire (commente) cette ligne, le processus lame s’exécute ( avec un avertissement de format audio non pris en charge ). Peut-être que ce que l’article a dit pourrait expliquer mon problème ici.

 # file paths file = Pathname.new('example.flac').realpath dest = Pathname.new('example.mp3') # some local variables read_wav = nil read_lame = nil # the flac process, which exits succesfully IO.popen("flac --decode --stdout \"#{file}\"", 'rb'){|wav| until wav.eof do read_wav = wav.read end } # the lame process, which fails IO.popen("lame -V0 --vbr-new --verbose - -", 'r+b'){|lame| lame << read_wav # if I comment out this, the process exits, instead of hanging lame.close_write until lame.eof do read_lame << lame.read end } 

EDIT # 3

J’ai trouvé ce stackoverflow qui (dans la première réponse) mentionnait que la mise en œuvre de cygwin pipe cygwin pas fiable. Cela ne serait peut-être pas lié à Windows (du moins pas directement) mais à cygwin et à son émulation. J’ai plutôt choisi d’utiliser le code suivant, basé sur la réponse de icy , qui fonctionne !

 flac = "flac --decode --stdout \"#{file}\"" lame = "lame -V0 --vbr-new --verbose - \"#{dest}\"" system(flac + ' | ' + lame) 

Avez-vous essayé le tuyau | personnage?
Testé sur Windows avec l’installateur Ruby

 require 'open3' command = 'dir /B | sort /R' # a windows example command Open3.popen3(command) {|stdin, stdout, stderr, wait_thr| pid = wait_thr.pid puts stdout.read # } 

Autres moyens: Ruby pipes: comment associer la sortie de deux sous-processus?

EDIT: utiliser IO::pipe

 require 'open3' command1 = 'dir /B' command2 = 'sort /R' reader,writer = IO.pipe Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr| writer.write stdout.read } writer.close stdout, stderr, status = Open3.capture3(command2, :stdin_data => reader.read) reader.close puts "status: #{status}" #pid and exit code puts "stderr: #{stderr}" #use this to debug command2 errors puts stdout 

L’incorporation des deux semble également fonctionner, mais, comme le blog que vous avez mentionné, il faut attendre la fin de la première commande (pas en temps réel – test avec une commande ping)

 stdout2 = '' Open3.popen3(command1) {|stdin, stdout, stderr, wait_thr| stdout2, stderr2, status2 = Open3.capture3(command2, :stdin_data => stdout.read) } puts stdout2