類別 PTY

建立並管理偽終端機 (PTY)。另請參閱 en.wikipedia.org/wiki/Pseudo_terminal

PTY 讓您可以使用 ::open 分配新的終端機,或使用 ::spawn 搭配特定指令產生新的終端機。

範例

在此範例中,我們將變更 factor 指令的緩衝類型,假設 factor 使用 stdio 進行 stdout 緩衝。

如果使用 IO.pipe 取代 PTY.open,此程式碼會陷入僵局,因為 factor 的 stdout 已完全緩衝。

# 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

授權

© Copyright 1998 by Akinori Ito。

此軟體可為此目的自由重新散布,全部或部分,前提是此軟體和應用程式及其衍生品的任何副本都包含此完整的著作權聲明。

此軟體以「原樣」提供,不提供任何明示或暗示的擔保,包括但不限於適銷性、適售性或使用此軟體取得的結果的擔保。

公開類別方法

check(pid, raise = false) → Process::Status 或 nil 按一下以切換來源
check(pid, true) → nil 或引發 PTY::ChildExited

檢查 pid 指定的子處理程序狀態。如果處理程序仍然執行,則傳回 nil

如果處理程序未執行,且 raise 為 true,則會引發 PTY::ChildExited 例外。否則,它會傳回 Process::Status 實例。

pid

要檢查的處理程序的處理程序 ID

raise

如果 truepid 指定的處理程序已停止執行,則會引發 PTY::ChildExited

static VALUE
pty_check(int argc, VALUE *argv, VALUE self)
{
    VALUE pid, exc;
    rb_pid_t cpid;
    int status;
    const int flag =
#ifdef WNOHANG
        WNOHANG|
#endif
#ifdef WUNTRACED
        WUNTRACED|
#endif
        0;

    rb_scan_args(argc, argv, "11", &pid, &exc);
    cpid = rb_waitpid(NUM2PIDT(pid), &status, flag);
    if (cpid == -1 || cpid == 0) return Qnil;

    if (!RTEST(exc)) return rb_last_status_get();
    raise_from_check(cpid, status);

    UNREACHABLE_RETURN(Qnil);
}
getpty
別名:spawn
open → [master_io, slave_file] 按一下以切換來源
open {|(master_io, slave_file)| ... } → 區塊值

配置一個 pty(偽終端)。

在區塊形式中,會產生一個包含兩個元素(master_io、slave_file)的陣列,且區塊的值會從 open 傳回。

如果尚未關閉 IOFile,則在區塊完成後會將它們關閉。

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 的主控端,作為 IO

slave_file

pty 的從屬端,作為 File。終端裝置的路徑可透過 slave_file.path 取得

可以使用 IO#raw! 來停用換行轉換

require 'io/console'
PTY.open {|m, s|
  s.raw!
  # ...
}
static VALUE
pty_open(VALUE klass)
{
    int master_fd, slave_fd;
    char slavename[DEVICELEN];

    getDevice(&master_fd, &slave_fd, slavename, 1);

    VALUE master_path = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
    VALUE master_io = rb_io_open_descriptor(rb_cIO, master_fd, FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX, master_path, RUBY_IO_TIMEOUT_DEFAULT, NULL);

    VALUE slave_path = rb_obj_freeze(rb_str_new_cstr(slavename));
    VALUE slave_file = rb_io_open_descriptor(rb_cFile, slave_fd, FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY, slave_path, RUBY_IO_TIMEOUT_DEFAULT, NULL);

    VALUE assoc = rb_assoc_new(master_io, slave_file);

    if (rb_block_given_p()) {
        return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
    }

    return assoc;
}
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

指令的控制 tty 設定為 pty 的從屬端裝置,且其標準輸入/輸出/錯誤會重新導向到從屬端裝置。

env 是個選用的雜湊,會提供額外的環境變數給已執行的 pty。

# sets FOO to "bar"
PTY.spawn({"FOO"=>"bar"}, "printenv", "FOO") { |r,w,pid| p r.read } #=> "bar\r\n"
# unsets FOO
PTY.spawn({"FOO"=>nil}, "printenv", "FOO") { |r,w,pid| p r.read } #=> ""

commandcommand_line 是要執行的完整指令,給定一個 String。任何額外的 arguments 都會傳遞給指令。

傳回值

在非區塊形式中,這會傳回一個大小為 3 的陣列,[r, w, pid]

在區塊形式中,這些相同的值會產生到區塊

r

一個可讀的 IO,其中包含指令的標準輸出和標準錯誤

w

一個可寫的 IO,為指令的標準輸入

pid

指令的處理序識別碼。

static VALUE
pty_getpty(int argc, VALUE *argv, VALUE self)
{
    VALUE res;
    struct pty_info info;
    char SlaveName[DEVICELEN];

    establishShell(argc, argv, &info, SlaveName);

    VALUE pty_path = rb_obj_freeze(rb_str_new_cstr(SlaveName));
    VALUE rport = rb_io_open_descriptor(
        rb_cFile, info.fd, FMODE_READABLE, pty_path, RUBY_IO_TIMEOUT_DEFAULT, NULL
    );

    int wpty_fd = rb_cloexec_dup(info.fd);
    if (wpty_fd == -1) {
        rb_sys_fail("dup()");
    }
    VALUE wport = rb_io_open_descriptor(
        rb_cFile, wpty_fd, FMODE_WRITABLE | FMODE_TRUNC | FMODE_CREATE | FMODE_SYNC,
        pty_path, RUBY_IO_TIMEOUT_DEFAULT, NULL
    );

    res = rb_ary_new2(3);
    rb_ary_store(res, 0, rport);
    rb_ary_store(res, 1, wport);
    rb_ary_store(res,2,PIDT2NUM(info.child_pid));

    if (rb_block_given_p()) {
        rb_ensure(rb_yield, res, pty_detach_process, (VALUE)&info);
        return Qnil;
    }
    return res;
}
別名:getpty