PTY
PTY
PTY는 의사 터미널(pseudo terminal, PTY)을 만들고 관리하는 모듈이에요. 의사 터미널 개념에 대한 배경은 en.wikipedia.org/wiki/Pseudo_terminal에서 자세히 볼 수 있어요.
PTY는 ::open으로 새 터미널을 할당받거나, ::spawn으로 특정 명령과 함께 새 터미널을 띄울 수 있게 해 줘요.
출처: Ruby 4.0 API
본문
예제
이 예제에서 factor 명령이 stdout 버퍼링에 stdio를 쓴다고 가정하고, 그 버퍼링 방식을 바꿔볼게요. 만약 PTY.open 대신 IO.pipe를 쓴다면, factor의 stdout이 완전 버퍼링(full buffering)이라 이 코드는 교착 상태(deadlock)에 빠져요.
# start by requiring the standard library PTY
require 'pty'
master, slave = PTY.open
read, write = IO.pipe
pid = spawn("factor", :in=>read, :out=>slave)
read.close # we dont need the read
slave.close # or the slave
# pipe "42" to the factor command
write.puts "42"
# output the response from factor
p master.gets #=> "42: 2 3 7\n"
# pipe "144" to factor and print out the response
write.puts "144"
p master.gets #=> "144: 2 2 2 2 3 3\n"
write.close # close the pipe
# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
master.gets # FreeBSD returns nil.
rescue Errno::EIO # GNU/Linux raises EIO.
nil
end
p ret #=> nil
License
© Copyright 1998 by Akinori Ito.
This software may be redistributed freely for this purpose, in full or in part, provided that this entire copyright notice is included on any copies of this software and applications and derivations thereof.
This software is provided on an "as is" basis, without warranty of any kind, either expressed or implied, as to any matter including, but not limited to warranty of fitness of purpose, or merchantability, or results obtained from use of this software.
Public Class Methods
check(pid, raise = false) → Process::Status or nil / check(pid, true) → nil or raises PTY::ChildExited
pid로 지정된 자식 프로세스의 상태를 확인해요. 프로세스가 아직 살아 있으면 nil을 돌려줘요. 프로세스가 살아 있지 않은데 raise가 true이면 PTY::ChildExited 예외를 발생시키고, 그렇지 않으면 Process::Status 인스턴스를 돌려줘요.
pid
확인할 프로세스의 프로세스 ID
raise
true이고 pid로 식별된 프로세스가 더 이상 살아 있지 않으면 PTY::ChildExited가 발생해요.
getpty
spawn의 별칭이에요.
open → [master_io, slave_file] / open {|(master_io, slave_file)| ... } → block value
pty(의사 터미널)를 할당해요. 블록 형식에서는 (master_io, slave_file) 두 요소의 배열을 yield 하고, open은 블록의 값을 돌려줘요. IO와 File은 블록이 끝난 뒤, 아직 닫히지 않았다면 둘 다 닫혀요.
PTY.open {|master, slave|
p master #=> #<IO:masterpty:/dev/pts/1>
p slave #=> #<File:/dev/pts/1>
p slave.path #=> "/dev/pts/1"
}
블록 형식이 아닌 경우에는 [master_io, slave_file] 두 요소 배열을 돌려줘요.
master, slave = PTY.open
# do something with master for IO, or the slave file
두 가지 형식 공통의 인자는 다음과 같아요.
master_io
pty의 마스터(master). IO예요.
slave_file
pty의 슬레이브(slave). File이에요. 터미널 장치의 경로는 slave_file.path로 얻을 수 있어요.
IO#raw!을 쓰면 newline 변환을 끌 수 있어요.
require 'io/console'
PTY.open {|m, s|
s.raw!
# ...
}
spawn([env,] command_line) { |r, w, pid| ... } / spawn([env,] command_line) → [r, w, pid] / spawn([env,] command, arguments, ...) { |r, w, pid| ... } / spawn([env,] command, arguments, ...) → [r, w, pid]
지정된 명령을 새로 할당된 pty에서 실행해요. 별칭 ::getpty도 쓸 수 있어요.
명령의 controlling tty는 pty의 슬레이브 장치로 설정되고, 표준 입력/출력/오류는 슬레이브 장치로 리다이렉트돼요.
env는 spawned pty에 추가 환경 변수를 제공하는 선택적 해시예요.
# sets FOO to "bar"
PTY.spawn({"FOO"=>"bar"}, "printenv", "FOO") do |r, w, pid|
p r.read #=> "bar\r\n"
ensure
r.close; w.close; Process.wait(pid)
end
# unsets FOO
PTY.spawn({"FOO"=>nil}, "printenv", "FOO") do |r, w, pid|
p r.read #=> ""
ensure
r.close; w.close; Process.wait(pid)
end
command와 command_line은 실행할 전체 명령이며 문자열로 주어져요. 추가 인자는 명령에 그대로 전달돼요.
반환 값
블록 형식이 아니면 크기 3의 배열 [r, w, pid]을 돌려줘요. 블록 형식에서는 이 값들이 블록으로 yield 돼요.
r
명령의 표준 출력과 표준 오류를 담은 읽기 가능한 IO
w
명령의 표준 입력인 쓰기 가능한 IO
pid
명령의 프로세스 식별자
정리(Clean up)
이 메서드는 IO를 닫거나 자식 프로세스를 기다리는 등의 정리를 하지 않아요. 다만 블록 형식에서는 프로세스가 좀비(zombie)가 되지 않도록 detach 돼요(Process.detach 참고). 그 외의 정리는 호출자 책임이에요. pid를 기다릴 때는 그 전에 r과 w를 모두 닫아야 해요. 반대 순서로 하면 어떤 OS에서는 교착 상태가 발생할 수 있어요.