2013-03-13 2 views
0

명령을 실행하고 데이터를 stdin에 공급하며 stdout에서 읽으려고합니다. MacRuby를 통해 노출 된 NSTask뿐만 아니라 Ruby의 Open3 # popen3을 사용해 보았습니다. 필자가 작성한 프로그램의 소스는 here입니다. 나는 Xcode와 MacRuby에서 이것을하고있다.MacRuby에서 명령을 실행하고 데이터를 표준 입력으로 가져오고 표준 출력에서 ​​읽으려면 어떻게해야합니까?

진입 점, 단지 나를 쉽게 두 가지 방법을 전환 할 수 있도록 :

는 여기에 몇 가지 코드를 선택합니다.

def do_gpg_cmd cmd 
    do_gpg_cmd_nstask cmd 
end 

Open3 # popen3을 사용하는 루비 웨이. 이 방법에서

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 

는 응용 프로그램이 정지는 (내가 gpg --clearsign --local-user $key를 실행하는 앱에 로그인 버튼을 클릭하여 테스트하고 있습니다).

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 

코코아 방법, NSTask를 사용 : 나는 응용 프로그램을 죽일 때

은 엑스 코드가 자동으로 나타납니다 스레드 diagnosic이 표시됩니다.

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 

이 코드를 실행하면 Xcode 디버그 출력에이 오류가 발생합니다. 분명히 ARG 파트를 울트라 벙어리 로깅으로 출력하고 있습니다. 서브 프로세스가 실행되지 않습니다.

ARG: [--no-tty] 
ARG: [--clearsign] 
ARG: [--local-user] 
ARG: [0xC2808780] 
ARG: [] 
2013-03-12 23:27:39.305 GPGBoard[84924:3503] -[NSNull fileSystemRepresentation]: unrecognized selector sent to instance 0x7fff75b05310 
*** Dispatch block exited prematurely because of an uncaught exception: 
/Users/colin/Library/Developer/Xcode/DerivedData/GPGBoard-bradukgmaegxvmbukhwehepzyxcv/Build/Products/Debug/GPGBoard.app/Contents/Resources/AppDelegate.rb:81:in `block': NSInvalidArgumentException: -[NSNull fileSystemRepresentation]: unrecognized selector sent to instance 0x7fff75b05310 (RuntimeError) 

는 나도 접근 방식의 문제는 상호 배타적 인 것으로 의심하십시오 된 Open3 #의 popen3 문제는 NSTask에 문제가 파이프와 관련된 문제입니다 동안, 읽기를 차단 관련이있을 수 있습니다.

코드의이 작품은 나를 위해 작동하고 현재 디렉토리에있는 파일을 출력

답변

3

:

macruby[86209:707] -[NSNull fileSystemRepresentation]: unrecognized selector sent to instance 0x7fff77d6f310

: 지금은 task.arguments = "-l -a".split(" ") << niltask.arguments = ["-l", "-a"]를 교체 할 경우

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.setArguments(fcmd.split(" ") << nil) 인 것 같습니다. 이 번호를 task.setArguments(fcmd.split(" "))으로 변경하면 더 이상 NSNull 문제가 발생하지 않습니다.

+0

감사합니다.내가 그것을 제거하면, 나는 autozone에없는 것에 관한 메시지를 얻는다. 코드를 조금 더 읽고 NSString # initWithData의 인코딩 인수에 일반 스타일 해시 (: sym => : sym : : key 대신 키)로 다시 전환하기로 결정했습니다. 자,이 동작은 완전히 Ruby 버전과 동일합니다. 버튼을 클릭하면 멈 춥니 다. 종료하려면 강제 종료해야합니다. –

+0

사실, 아직 Ruby 버전을 사용하고 있습니다. 다시 전환 한 후 실제로 명령 (yay!)에서 출력을 얻을 수 있지만 즉시 중단됩니다. –

+0

작업이 실제로하는 일에 따라'terminate '를 호출 할 수 있습니다. – jtomschroeder

1

문제는 파이프가 버퍼 크기를 갖는다는 것입니다. 버퍼가 꽉 찼 으면 쓰기 명령은 다른 쪽에서 일부 데이터를 읽어 새 데이터를 저장할 공간이 생길 때까지 차단합니다.

코드는 먼저 모든 데이터를 명령의 stdin에 쓰려고 시도합니다. 명령이 일부 데이터를 읽는다고 가정하고 일부 출력을 stdout에 쓴 다음 stdin에서 계속 읽습니다. 많은 데이터가 입력되면 명령의 stdout 파이프의 버퍼가 가득 차게됩니다. 명령은 누군가 stdout 파이프에서 데이터를 읽을 때까지 차단합니다. 그러나 루비 코드는 stdin에 데이터를 쓰지 못하고 stdin 파이프가 가득 찰 때까지 그렇게합니다. 이제 데드 록 (dead-lock)이 있습니다.

용액 중 동시에 또는 단순히 표준 입력 데이터를 기록하는 동시에 표준 출력으로부터 데이터를 판독하는 블록 - 방식 (블록 크기는 파이프의 버퍼 크기보다 큰 없음.) 응답 용

+0

감사! 이것은 좋은 과거를 나에게 줬다. 그러나 나는 아직도 아주 거기에 없다. [내 코드를 업데이트하여 전략을 시험해보십시오.] (https://github.com/colindean/gpgboard/blob/master/)에서 [문제가 발생했습니다.] (https://github.com/MacRuby/MacRuby/issues/224) GPGBoard/AppDelegate.rb # L103). 추가 포인터가 있습니까? –

+0

좀 더 조사한 후에 [rb_thread_create()가 rev 542dc07c] (https://github.com/MacRuby/MacRuby/issues/225)에서 구현되지 않았기 때문에 차단 된 것처럼 보입니다. –