2010-11-20 10 views
16

Czy istnieje zautomatyzowany sposób na wykonywanie rurociągów w powłoce Ruby? Próbuję przekonwertować następujący kod powłoki Ruby:Ruby pipes: Jak powiązać wyjście dwóch podprocesów?

a | b | c... > ... 

ale jedyne rozwiązanie znalazłem tak daleko jest do zrobienia zarządzanie buforami siebie (uproszczony, niesprawdzone, mam nadzieję, że dostanie mi chodzi po drugiej):

a = IO.popen('a') 
b = IO.popen('b', 'w+') 
Thread.new(a, b) { |in, out| 
    out.write(in.readpartial(4096)) until in.eof? 
    out.close_write 
} 
# deal with b.read... 

Sądzę, że to, czego szukam, jest sposobem na to, aby popen użył istniejącego strumienia, zamiast tworzyć nowy? Lub alternatywnie metoda scalania IO # do podłączenia wyjścia A do wejścia b? Moje obecne podejście staje się raczej nieporęczne, gdy rośnie liczba filtrów.

Wiem oczywiście o Kernel#system('a | b'), ale muszę mieszać filtry Ruby z zewnętrznymi filtrami programowymi w sposób ogólny.

+0

Istnieje jedno rozwiązanie z poleceniem spawn w ruby. Powinieneś otworzyć kilka potoków, a następnie użyć opcji odradzania, aby przekierować Stdin i Stdout procesów potomnych. Możesz znaleźć bardziej szczegółowy przykład w mojej odpowiedzi tutaj: http://stackoverflow.com/questions/11898528/marshal-ruby-pipes-sending-serialized-object-to-child-processes/13258047#13258047 –

+0

Rurociągi Open3 wyglądają ładnie idealny https://ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html#method-c-pipeline_rw – kch

Odpowiedz

3

Jeśli a, b, a c polecenia są normalnie dostępne z poziomu wiersza poleceń, a następnie można użyć:

captured_output = `a | b | c` 

Ruby będzie uruchomić polecenia w podpowłoce i uchwycić standardowe wyjście.

Jeśli z jakiegoś powodu chcesz przekierować dane wyjściowe do pliku, możesz dodać przekierowanie również do tego polecenia. STDOUT nie będą zwracane do Ciebie w tej sprawie, ale można otworzyć plik i ręcznie przetworzyć go:

`a | b | c > captured_output` 
File.foreach('captured_output') do |li| 
    print li 
end 

Nie oferuje większą kontrolę jak za pomocą system lub popen3 ale to jest wygodne:

>> sin, sout, serr = Open3.popen3('ls -al | tail -1') #=> [#<IO:fd 4>, #<IO:fd 5>, #<IO:fd 7>, #<Thread:0x00000100bb8798 run>] 
>> sout.read #=> "drwxr-xr-x 3 greg staff 102 Nov 2 21:01 python\n" 
+0

Chciałbym również zamiast używać Threads zrobiłem to za pomocą dużej pętli na wszystko ... nieprzyjemne rozwiązanie dla nieprzyjemnego problemu – mpapis

0

Niestety, orurowanie w powłoce jest poważną sprawą i rzeczywiście wymagałoby to sporych ilości kodu. Nie jest konieczne tworzenie wątków z pętlami do odczytu/zapisu, ale wymagałoby to jeszcze wiele pracy.

Najprostszym przykładem, jaki znalazłem, jest redirect implementation in dash (Debian Almquist Shell). Ogólnie rzecz biorąc, jeśli chcesz zrobić to samo w Ruby, będziesz musiał zreplikować te sztuczki manipulacyjne przy użyciu Ruby'ego, używając Rubiego:, IO#fileno, IO#pipe, IO#reopen itd. Łatwiej będzie ponownie użyć powłoki (takiej jak kreska) kod w bibliotece C .so dla interpretera Ruby niż próba połączenia tego samego przy użyciu prostych prymitywów Ruby.

Nie wiem o istniejącym uogólnionym interfejsie API Ruby dla złożonych połączeń/przekierowań międzyprocesorowych. Jeśli mógłbyś zaproponować dobry interfejs API, którego chciałbyś użyć, być może mógłbym wziąć udział w implementacji.

9

Stare pytanie, ale ponieważ jest to jeden z pierwszy wynik w Google, oto odpowiedź: http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3/ (metoda 8)

W skrócie:

sh = Shell.new 
sh.system("a") | sh.system("b") | sh.system("c") 

I można robić bardziej skomplikowane rzeczy jak

sh.echo(my_string) | sh.system("wc") > "file_path" 
xml = (sh.echo(html) | sh.system("tidy", "-q")).to_s 
1

Korzystanie zwykły rubin, spawn ma opcji przekierowania, które można wykorzystać do połączenia z rurami procesów.

1) Utwórz rurę

r,w = IO.pipe 

2) Użyj go, aby połączyć dwa procesów potomnych

spawn(*%w[echo hello world], out: w) 
spawn(*%w[tr a-z A-Z], in: r) 
# => HELLO WORLD 

Oczywiście, można ująć to w czymś jak sh.system ze wspomnianej biblioteki Shell i utwórz metodę |() do wykonywania połączeń.

Moduł open3 biblioteki standardowej ma kilka naprawdę fajnych narzędzi do tego typu rzeczy, w tym do tworzenia kompletnych potoków.

Powiązane problemy