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