2009-08-07 12 views
13

Ben eşdeğer yapmak istiyorum: Lua içindeLuo ile nasıl bir okuma-yazma borusu inşa ediyorsunuz?

foo=$(echo "$foo"|someprogram) 

- yani ben metin bir demet içeren bir değişken var ve ben bir filtre aracılığıyla çalıştırmak istiyorum (uygulanan python'da olduğu gibi).

Herhangi bir ipucu?

Eklendi: gerçekten geçici bir dosya Buna izin vermek Lua standart kütüphanede şey yok

cevap

3

Aha, bir olasılıkla daha iyi bir çözüm : Sürece Lua io.popen destekler olarak

require('posix') 
require('os') 
require('io') 

function splat_popen(data,cmd) 
    rd,wr = posix.pipe() 
    io.flush() 
    child = posix.fork() 
    if child == 0 then 
     rd:close() 
     wr:write(data) 
     io.flush() 
     os.exit(1) 
    end 
    wr:close() 

    rd2,wr2 = posix.pipe() 
    io.flush() 
    child2 = posix.fork() 
    if child2 == 0 then 
     rd2:close() 
     posix.dup(rd,io.stdin) 
     posix.dup(wr2,io.stdout) 
     posix.exec(cmd) 
     os.exit(2) 
    end 
    wr2:close() 
    rd:close() 

    y = rd2:read("*a") 
    rd2:close() 

    posix.wait(child2) 
    posix.wait(child) 

    return y 
end 

munged=splat_popen("hello, world","/usr/games/rot13") 
print("munged: "..munged.." !") 
+0

neden 'talep' ile ilgili bir hata alıyorum? "var olmayan global değişken" gerektirir "" –

4

kullanmadan bunu istiyorum.

Here is an in-depth exploration of the difficulties of doing bidirectional communication properly

, ve önerilen bir çözüm:

mümkünse, bir dosyaya akımı (giriş ya da çıkış) bir ucunu yönlendirmek. Yani .:

fp = io.popen("foo >/tmp/unique", "w") 
fp:write(anything) 
fp:close() 
fp = io.open("/tmp/unique") 
x = read("*a") 
fp:close() 

Mümkün bir alt süreci ile çift yönlü iletişimi yapmak os ve io ad işlevler ekleyen this extension ilginizi çekebilir. geçici bir dosya önler

+0

kabuk yaptığı gibi, filtreye veri aktarmak için (fork() veya benzeri) bir lua alt süreç oluşturmak için bir yol var mı? Bu, çıkmazdan kaçınacak ... –

-1

Çok güzel değil çözüm ...

require("io") 
require("posix") 

x="hello\nworld" 

posix.setenv("LUA_X",x) 
i=popen('echo "$LUA_X" | myfilter') 
x=i.read("*a") 
3

bu sorun kolay . Daha sonra

local foo = ... 
local cmd = ("echo $foo | someprogram"):gsub('$foo', foo) 
foo = os.capture(cmd) 

böyle hep şeyler arayabilir

function os.capture(cmd, raw) 
    local f = assert(io.popen(cmd, 'r')) 
    local s = assert(f:read('*a')) 
    f:close() 
    if raw then return s end 
    s = string.gsub(s, '^%s+', '') 
    s = string.gsub(s, '%s+$', '') 
    s = string.gsub(s, '[\n\r]+', ' ') 
    return s 
end 

: Eğer böyle bir işlev gerekir yerine $(...) hariç, ana hatlarıyla gibi çözüm tam olarak budur. İşte oluşturan komutlar için bir ilgili kullanışlı fonksiyon bulunuyor:

İşte
local quote_me = '[^%w%+%-%=%@%_%/]' -- complement (needn't quote) 
local strfind = string.find 

function os.quote(s) 
    if strfind(s, quote_me) or s == '' then 
    return "'" .. string.gsub(s, "'", [['"'"']]) .. "'" 
    else 
    return s 
    end 
end 
+0

Bu, stderr'i yakalamıyor. Onu da yakalamanın bir yolu var mı? –

+0

@Vilas, stderr ve stdout'u karıştırmanın sakıncası yoksa, komut satırına '2> & 1' ekleyin. Ya da Debian komut dosyasına "not ek açıklaması" yazın. –

+0

Bu yaklaşıma çok dikkat edin --- komut satırındaki tüm foo komutlarının tamamını kapatabilir ve komut satırının ne kadar büyük olduğunu garanti edemezsiniz. Verileriniz, işlem listesinde görüneceği gibi herkese açık olacak. –

0

Ben problemi nasıl çözdüğünü, bu lua posix gerektirir.

  p = require 'posix' 
      local r,w = p.pipe() 
      local r1,w1 = p.pipe() 
      local cpid = p.fork() 
      if cpid == 0 then -- child reads from pipe          
      w:close() 
      r1:close() 
      p.dup(r, io.stdin) 
      p.dup(w1 ,io.stdout) 
      p.exec('./myProgram') 
      r:close() 
      w1:close() 
      p._exit(0) 
      else -- parent writes to pipe             
      IN = r1 
      OUT = w 
      end 

myProgram yürütülmesi sırasında, you'l okumak ve normal io dan yazıp bu kod parçası sonra sadece çocuk programı ile comunicate IN ve OUT üzerinde okuma/yazma gerekir.

2

Aynı şeyi yapmaya çalışıyorum ve hiçbir zaman iyi bir çözüm bulamadım bu yazıya tökezledim, sorunlarımı nasıl çözdüğümü öğrenmek için aşağıdaki kodu inceleyin. Bu uygulama, kullanıcıların stdin, stdout, stderr öğelerine erişmesine ve dönüş durumu kodunu almasına olanak tanır. Basit boru çağrıları için basit bir sarıcı çağrılır.

require("posix") 

-- 
-- Simple popen3() implementation 
-- 
function popen3(path, ...) 
    local r1, w1 = posix.pipe() 
    local r2, w2 = posix.pipe() 
    local r3, w3 = posix.pipe() 

    assert((w1 ~= nil or r2 ~= nil or r3 ~= nil), "pipe() failed") 

    local pid, err = posix.fork() 
    assert(pid ~= nil, "fork() failed") 
    if pid == 0 then 
     posix.close(w1) 
     posix.close(r2) 
     posix.dup2(r1, posix.fileno(io.stdin)) 
     posix.dup2(w2, posix.fileno(io.stdout)) 
     posix.dup2(w3, posix.fileno(io.stderr)) 
     posix.close(r1) 
     posix.close(w2) 
     posix.close(w3) 

     local ret, err = posix.execp(path, unpack({...})) 
     assert(ret ~= nil, "execp() failed") 

     posix._exit(1) 
     return 
    end 

    posix.close(r1) 
    posix.close(w2) 
    posix.close(w3) 

    return pid, w1, r2, r3 
end 

-- 
-- Pipe input into cmd + optional arguments and wait for completion 
-- and then return status code, stdout and stderr from cmd. 
-- 
function pipe_simple(input, cmd, ...) 
    -- 
    -- Launch child process 
    -- 
    local pid, w, r, e = popen3(cmd, unpack({...})) 
    assert(pid ~= nil, "filter() unable to popen3()") 

    -- 
    -- Write to popen3's stdin, important to close it as some (most?) proccess 
    -- block until the stdin pipe is closed 
    -- 
    posix.write(w, input) 
    posix.close(w) 

    local bufsize = 4096 
    -- 
    -- Read popen3's stdout via Posix file handle 
    -- 
    local stdout = {} 
    local i = 1 
    while true do 
     buf = posix.read(r, bufsize) 
     if buf == nil or #buf == 0 then break end 
     stdout[i] = buf 
     i = i + 1 
    end 

    -- 
    -- Read popen3's stderr via Posix file handle 
    -- 
    local stderr = {} 
    local i = 1 
    while true do 
     buf = posix.read(e, bufsize) 
     if buf == nil or #buf == 0 then break end 
     stderr[i] = buf 
     i = i + 1 
    end 

    -- 
    -- Clean-up child (no zombies) and get return status 
    -- 
    local wait_pid, wait_cause, wait_status = posix.wait(pid) 

    return wait_status, table.concat(stdout), table.concat(stderr) 
end 

-- 
-- Example usage 
-- 
local my_in = io.stdin:read("*all") 
--local my_cmd = "wc" 
--local my_args = {"-l"} 
local my_cmd = "spamc" 
local my_args = {} -- no arguments 
local my_status, my_out, my_err = pipe_simple(my_in, my_cmd, unpack(my_args)) 

-- Obviously not interleaved as they would have been if printed in realtime 
io.stdout:write(my_out) 
io.stderr:write(my_err) 

os.exit(my_status) 
+0

Bu çok hoş ama bu şekilde sunulduğundan emin misin? Dosya tanımlayıcı numaraları birer birer (0,1 ve 2 olmalıdır) ve çatal ve yürütme hataları için yapılan testler tersine döndü. – jpc

+0

çoğunlukla benim için yazılmış gibi çalışır, 'yerel posix = require ("posix")' eklemek zorunda kaldı. – robm

+0

Döngüleri ps.pipe_simple() içine akıtıldıktan sonra 'posix.close (r)' ve 'posix.close (e) 'eklemek zorunda kaldım. Aksi takdirde 500'den fazla arama yaptıktan sonra 'çok açık dosya var'. – robm

0

Kolaydır, uzantı gerekmez (lua 5.3 ile test edilmiştir).inout.lua olarak kaydetme

#!/usr/bin/lua 
-- use always locals 
local stdin = io.stdin:lines() 
local stdout = io.write 

for line in stdin do 
    stdout (line) 
end 

ve yapılacak chmod +x /tmp/inout.lua

20:30 $ foo=$(echo "bla"| /tmp/inout.lua) 
20:30 $ echo $foo 
bla