2009-08-07 7 views
13

मैं के बराबर करना चाहते हैं:आप लुआ के साथ एक पठन-लेखन पाइप कैसे बनाते हैं?

foo=$(echo "$foo"|someprogram) 
lua भीतर

- यानी, मैं एक चर पाठ का एक गुच्छा युक्त मिल गया है, और मैं एक फिल्टर के माध्यम से इसे चलाने के लिए चाहते हैं (कार्यान्वित पाइथन में ऐसा होता है)।

कोई संकेत?

जोड़ा गया: वास्तव में एक अस्थायी फ़ाइल

उत्तर

3

अहा, एक संभवतः बेहतर समाधान:

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

मुझे 'आवश्यकता' के बारे में त्रुटि क्यों मिलती है? "अप्रत्याशित वैश्विक चर 'की आवश्यकता है' –

4

लुआ मानक पुस्तकालय में कुछ भी नहीं है इस अनुमति देने के लिए नहीं है का उपयोग किए बिना यह करने के लिए करना चाहते हैं।

Here is an in-depth exploration of the difficulties of doing bidirectional communication properly, और एक प्रस्तावित समाधान:

यदि संभव हो तो, एक फाइल करने के लिए धारा (इनपुट या आउटपुट) के एक छोर पर रीडायरेक्ट करे। यानी .:

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

आप this extension जो एक उपप्रक्रिया संभव के साथ द्विदिश संचार बनाने के लिए os और io नामस्थान के लिए काम करता है कहते हैं में रुचि हो सकती।

+0

वहाँ किसी तरह एक lua उपप्रक्रिया (कांटा() या समान), फिल्टर करने के लिए डेटा पास करने के कवच की तरह करता है बनाने के लिए है? वह डेडलॉक से बच जाएगा ... –

-1

एक बहुत अच्छा नहीं समाधान है कि एक अस्थायी फ़ाइल से बचा जाता है ...

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

x="hello\nworld" 

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

जब तक आपके लुआ io.popen का समर्थन करता है, इस समस्या के लिए आसान है ।

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 

इसके बाद आप कॉल कर सकते हैं

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

मैं हर समय इस तरह सामान कार्य करें: समाधान बिल्कुल के रूप में आप, रेखांकित किया है $(...) के बजाय छोड़कर आप इस तरह एक समारोह की जरूरत है। यहाँ के गठन आदेश के लिए एक संबंधित उपयोगी समारोह है:

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

यह stderr पर कब्जा नहीं करता है। क्या इसे कैप्चर करने का कोई तरीका है? –

+0

@ विलास, अगर आपको स्टीडर और स्टडआउट मिश्रण करने में कोई फर्क नहीं पड़ता है, तो बस कमांड लाइन में '2> और 1' जोड़ें। या डेबियन लिपि 'एनोटेट-आउटपुट' में देखें। –

+0

इस दृष्टिकोण से बहुत सावधान रहें --- आप कमांड लाइन पर 'foo' की संपूर्ण सामग्री को पार करते हैं, और आप गारंटी नहीं दे सकते कि कमांड लाइन कितनी बड़ी है। आपका डेटा भी सार्वजनिक होने का अंत होगा, क्योंकि यह प्रक्रिया सूची में दिखाई देगा। –

0

यहाँ कैसे मैं समस्या हल है, यह lua posix आवश्यकता होती है।

  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 निष्पादन के दौरान, you'l पढ़ सकते हैं और सामान्य कब से लिख सकते हैं और कोड के इस भाग के बाद आप सिर्फ लिखने/बच्चे कार्यक्रम के साथ comunicate करने के लिए IN और OUT पर पढ़ने के लिए की है।

2

मैं एक ही काम करने की कोशिश करते हुए इस पोस्ट पर ठोकर खाई और कभी भी अच्छा समाधान नहीं मिला, मैंने अपने मुद्दों को हल करने के लिए नीचे दिए गए कोड को देखें। यह कार्यान्वयन उपयोगकर्ताओं को stdin, stdout, stderr तक पहुंचने और वापसी स्थिति कोड प्राप्त करने की अनुमति देता है। साधारण पाइप कॉल के लिए एक साधारण रैपर कहा जाता है।

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

यह काफी अच्छा है लेकिन क्या आप वाकई यह प्रस्तुत करते हैं? फ़ाइल डिस्क्रिप्टर संख्याएं एक-एक-एक हैं (0,1 और 2 होना चाहिए) और कांटा और निष्पादन त्रुटियों के परीक्षण मेरे लिए उलटा दिखते हैं। – jpc

+0

ज्यादातर मेरे लिए लिखे गए कार्यों के रूप में काम करता है, 'स्थानीय posix = आवश्यकता ("posix")' जोड़ना था। – robm

+0

मुझे ps.pipe_simple() में उन्हें निकालने के लिए loops के बाद 'posix.close (r)' और 'posix.close (e) 'जोड़ना पड़ा। लिनक्स पर 500 कॉल के बाद अन्यथा 'बहुत खुली फ़ाइलें' मिलीं। – robm

0

यह आसान है, कोई आवश्यक एक्सटेंशन (lua 5.3 के साथ परीक्षण किया)।

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

for line in stdin do 
    stdout (line) 
end 
inout.lua के रूप में सहेजें

और कर chmod +x /tmp/inout.lua

20:30 $ foo=$(echo "bla"| /tmp/inout.lua) 
20:30 $ echo $foo 
bla 
संबंधित मुद्दे