2009-08-07 8 views
13

私は相当やってみたい:LUA内はどのようにしてLUAと読み書きのパイプを構築するのですか?

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

- つまり、私はテキストの束を含む変数を持っている、と私はフィルタを介してそれを実行したいと思います(実装しますそれが起こると、Pythonで)。

ヒント

追加されました:本当にこれを可能にする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

なぜ 'require'についてのエラーが出ますか? "存在しないグローバル変数 'require'" –

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() 

あなたは可能サブプロセスとの双方向通信を行うためにosio名前空間に機能を追加しthis extensionに興味があるかもしれません。

+0

シェルが行うよう、フィルタにデータを渡すために(フォーク()または同様の)LUAのサブプロセスを作成するためのいくつかの方法がありますか?それはデッドロックを避けるでしょう... –

-1

Aの一時ファイルを避ける非常に良いではない解決策...

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

x="hello\nworld" 

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

限り、あなたのLuaがio.popenをサポートして、この問題は簡単です。 。あなたはその後、

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

私はこのようなすべての時間をものを行う呼び出すことができます

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 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

@Vilas、stderrとstdoutを混在させたくないのであれば、 '2>&1'をコマンドラインに追加してください。あるいは、Debianスクリプト 'annotate-output'を調べてみてください。 –

+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が読み込まれ、通常のIOからの書き込みと、コードのこの部分の後にあなただけの子プログラムでcomunicateしINOUTで読む/書く必要があります。

2

私は私の問題を解決する方法については、以下のコードを参照して、同じことをやろうとしながら、この記事につまずいと良い解決策を見つけたことはありません。この実装は、ユーザーが標準入力、標準出力、標準エラー出力にアクセスし、リターンステータスコードを取得することができます。単純なラッパーは、単純なパイプコールで呼ばれています。

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

これはかなりいいですが、これは提示されているように動作するのですか?ファイルディスクリプタの番号は1つずつオフになります(0,1,2)。forkとexecpエラーのテストは私に逆らって見えます。 – jpc

+0

は主に私のために書かれたように動作し、 'local posix = require( "posix")'を追加する必要がありました。 – robm

+0

私は、ps.pipe_simple()でそれらを流すためにループの後に 'posix.close(r)'と 'posix.close(e)'を追加しなければなりませんでした。さもなければ、Linux上でこれを500回コールした後、「あまりにも多くのファイルを開く」ことがあります。 – robm

0

拡張は不要です(lua 5.3でテスト済み)。inout.luaとして保存

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

for line in stdin do 
    stdout (line) 
end 

とやるchmod +x /tmp/inout.lua

20:30 $ foo=$(echo "bla"| /tmp/inout.lua) 
20:30 $ echo $foo 
bla 
関連する問題