Cocoa 如何在MacRuby中执行命令、将数据馈送到其stdin并从其stdout读取数据?

Cocoa 如何在MacRuby中执行命令、将数据馈送到其stdin并从其stdout读取数据?,cocoa,pipe,stdout,stdin,macruby,Cocoa,Pipe,Stdout,Stdin,Macruby,我试图执行一个命令,将数据馈送到它的stdin,并从它的stdout读取数据。我尝试过使用Ruby的Open3Open3以及通过MacRuby公开的NSTask。我正在编写的程序的源代码是可用的。我用Xcode和MacRuby来做这个 以下是一些选择代码: 入口点,只允许我在两种方法之间轻松切换 def do_gpg_cmd cmd do_gpg_cmd_nstask cmd end ruby方式,使用Open3Open3 def do_gpg_cmd_ruby cmd gpg = "

我试图执行一个命令,将数据馈送到它的stdin,并从它的stdout读取数据。我尝试过使用Ruby的Open3Open3以及通过MacRuby公开的NSTask。我正在编写的程序的源代码是可用的。我用Xcode和MacRuby来做这个

以下是一些选择代码:

入口点,只允许我在两种方法之间轻松切换

def do_gpg_cmd cmd
  do_gpg_cmd_nstask cmd
end
ruby方式,使用Open3Open3

def do_gpg_cmd_ruby cmd
  gpg = "#{@gpg_path} --no-tty "
  cmd_output = ''
  logg "executing [#{cmd}]"
  Dispatch::Queue.concurrent.async do
    logg "new thread starting"
    Open3.popen3(gpg + cmd) do |stdin, stdout, stderr|
      stdin.write input_text
      stdin.close
      cmd_output = stdout.read
      output_text cmd_output
      stdout.close
      logg stderr.read
      stderr.close
    end
  end
  return cmd_output
end
在这种方法中,我正在测试的应用程序通过单击应用程序中的Sign按钮冻结,该按钮运行gpg-clearsign-localuser$key

当我终止应用程序时,Xcode会在自动出现的线程诊断中显示:

libsystem_kernel.dylib`__psynch_cvwait:
0x7fff84b390f0:  movl   $33554737, %eax
0x7fff84b390f5:  movq   %rcx, %r10
0x7fff84b390f8:  syscall
0x7fff84b390fa:  jae    0x7fff84b39101            ; __psynch_cvwait + 17 ; THIS LINE IS HIGHLIGHTED
0x7fff84b390fc:  jmpq   0x7fff84b3a4d4            ; cerror_nocancel
0x7fff84b39101:  ret    
0x7fff84b39102:  nop    
0x7fff84b39103:  nop  
Cocoa方法,使用NSTask

def do_gpg_cmd_nstask cmd
  Dispatch::Queue.concurrent.async do
    fcmd = "--no-tty " + cmd
    task = NSTask.alloc.init
    task.setLaunchPath(@gpg_path)
    task.setArguments(fcmd.split(" ") << nil)

    task.arguments.each {|a| puts "ARG: [#{a}]" }

    inpipe = NSPipe.pipe
    outpipe = NSPipe.pipe
    errpipe = NSPipe.pipe

    task.setStandardOutput(outpipe)
    task.setStandardInput(inpipe)
    task.setStandardError(errpipe)

    output = outpipe.fileHandleForReading
    errput = errpipe.fileHandleForReading
    input = inpipe.fileHandleForWriting

    task.launch

    input.writeData input_text.dataUsingEncoding(NSUTF8StringEncoding)
    input.closeFile

    outdata = output.readDataToEndOfFile
    errdata = errput.readDataToEndOfFile
    output.closeFile
    errput.closeFile
    outstring = NSString.alloc.initWithData(outdata, encoding: NSUTF8StringEncoding)
    errstring = NSString.alloc.initWithData(errdata, encoding: NSUTF8StringEncoding)

    output_text outstring
    logg errstring
  end
end

我怀疑这两种方法的问题是相互排斥的:Open3Open3问题可能与阻塞读取有关,而NSTask的问题与管道问题有关。

这段代码对我有用,并打印出当前目录中的文件:

framework "Cocoa"
task = NSTask.new
task.launchPath = "/bin/ls"
task.arguments = ["-l", "-a"]
stdoutPipe = NSPipe.pipe
task.standardOutput = stdoutPipe
task.launch
data = stdoutPipe.fileHandleForReading.readDataToEndOfFile
puts NSString.alloc.initWithData data, :encoding => NSASCIIStringEncoding

现在,如果我将task.arguments=[-l,-a]替换为task.arguments=-l-a.split这段代码适用于我并打印出当前目录中的文件:

framework "Cocoa"
task = NSTask.new
task.launchPath = "/bin/ls"
task.arguments = ["-l", "-a"]
stdoutPipe = NSPipe.pipe
task.standardOutput = stdoutPipe
task.launch
data = stdoutPipe.fileHandleForReading.readDataToEndOfFile
puts NSString.alloc.initWithData data, :encoding => NSASCIIStringEncoding

现在,如果我将task.arguments=[-l,-a]替换为task.arguments=-l-a.split则问题在于管道具有缓冲区大小。当缓冲区已满时,写入命令将阻塞,直到另一端读取了一些数据,以便为新数据腾出空间

代码首先尝试将所有数据写入命令的stdin。假设该命令确实读取了一些数据,将一些输出写入stdout,然后继续从其stdin读取。如果有大量数据被输入,那么在某个时候,命令的标准输出管道的缓冲区将变满。命令会一直阻塞,直到有人从标准输出管道读取数据。但是,您的ruby代码还没有完成向stdin写入数据的工作,并且会一直这样做,直到stdin管道也满了为止。现在有一个死锁


解决方案是将数据写入stdin,同时从stdout读取数据,可以是并行的,也可以是简单的按块读取。块大小不大于管道的缓冲区大小。

问题是管道具有缓冲区大小。当缓冲区已满时,写入命令将阻塞,直到另一端读取了一些数据,以便为新数据腾出空间

代码首先尝试将所有数据写入命令的stdin。假设该命令确实读取了一些数据,将一些输出写入stdout,然后继续从其stdin读取。如果有大量数据被输入,那么在某个时候,命令的标准输出管道的缓冲区将变满。命令会一直阻塞,直到有人从标准输出管道读取数据。但是,您的ruby代码还没有完成向stdin写入数据的工作,并且会一直这样做,直到stdin管道也满了为止。现在有一个死锁


解决方案是将数据写入stdin,同时从stdout读取数据,可以是并行的,也可以是简单的分块方式。块大小不大于管道的缓冲区大小。

感谢您的回答。当我删除它时,我会收到一条消息,说明某些东西不在自动区域中。我进一步阅读了代码,并决定将NSStringinitWithData的编码参数切换回常规样式的hash:sym=>:key,而不是sym::key。现在,行为与完全Ruby版本相同。我点击按钮,它就挂了。我不得不被迫退出。事实上,我仍然使用Ruby版本。切换回来后,我实际上可以从命令yay获得输出!但它在读取后立即挂起。根据任务实际执行的操作,您可以尝试调用Terminateh如果程序在读取后看起来挂起,我将如何调用terminate?我在读取前将其放入,并在读取后放置5秒钟睡眠,以确保程序完成对管道的写入。没有骰子。编辑:签名操作现在通常可以工作,但输出量大得多的加密操作似乎仍然无法工作。谢谢回答。当我删除它时,我会收到一条消息,说明某些东西不在自动区域中。我进一步阅读了代码,并决定将NSStringinitWithData的编码参数切换回常规样式的hash:sym=>:key,而不是sym::key。现在,行为与完全Ruby版本相同。我点击按钮,它就挂了。我不得不被迫退出。事实上,我仍然使用Ruby版本。切换回来后,我实际上可以从命令yay获得输出!但它在读取后立即挂起。根据任务实际执行的操作,您可以尝试调用Terminateh如果程序在读取后看起来挂起,我将如何调用terminate?我在读取前将其放入,并在读取后放置5秒钟睡眠,以确保程序完成对管道的写入。没有骰子。编辑:签名操作现在通常可以工作,但加密操作的输出要大得多
t、 看来还是不行,谢谢!这让我度过了一段美好的时光,但我仍然没有完全做到这一点。我在后面。还有其他的建议吗?经过更多的研究,我似乎被阻止了,因为。谢谢!这让我度过了一段美好的时光,但我仍然没有完全做到这一点。我在后面。还有什么其他的建议吗?经过进一步的研究,我似乎对此束手无策,因为。