Spec-Zone.ru › Ruby 3.4

модуль Open3

Модуль Open3 поддерживает создание дочерних процессов с доступом к их потокам $stdin, $stdout и $stderr.

Что здесь

Каждый из этих методов выполняет указанную команду в новом процессе или подоболочке либо несколько команд в новых процессах и/или подоболочках:

  • Каждый из этих методов выполняет одну команду в процессе или подоболочке, принимает строку для передачи в $stdin и возвращает строковый вывод из $stdout, $stderr или обоих потоков:

    • Open3.capture2: выполняет команду; возвращает строку из $stdout.

    • Open3.capture2e: выполняет команду; возвращает строку из объединённых потоков $stdout и $stderr.

    • Open3.capture3: выполняет команду; возвращает строки из $stdout и $stderr.

  • Каждый из этих методов выполняет одну команду в процессе или подоболочке и возвращает каналы для $stdin, $stdout и/или $stderr:

    • Open3.popen2: выполняет команду; возвращает каналы для $stdin и $stdout.

    • Open3.popen2e: выполняет команду; возвращает каналы для $stdin и объединённых потоков $stdout и $stderr.

    • Open3.popen3: выполняет команду; возвращает каналы для $stdin, $stdout и $stderr.

  • Каждый из этих методов выполняет одну или несколько команд в процессах и/или подоболочках и возвращает каналы для первого $stdin, последнего $stdout или обоих потоков:

    • Open3.pipeline_r: возвращает канал для последнего $stdout.

    • Open3.pipeline_rw: возвращает каналы для первого $stdin и последнего $stdout.

    • Open3.pipeline_w: возвращает канал для первого $stdin.

    • Open3.pipeline_start: не ожидает завершения процессов.

    • Open3.pipeline: ожидает завершения процессов.

Каждый из приведённых выше методов принимает:

  • Необязательный хеш имён и значений переменных окружения; см. раздел Среда выполнения.

  • Обязательный строковый аргумент, являющийся command_line или exe_path; см. раздел Аргумент command_line или exe_path.

  • Необязательный хеш параметров выполнения; см. раздел Параметры выполнения.

Константы

VERSION

Общедоступные методы класса

capture2([env, ] command_line, options = {}) → [stdout_s, status] Показать исходный код
capture2([env, ] exe_path, *args, options = {}) → [stdout_s, status]
# File lib/open3.rb, line 775
def capture2(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data)
  binmode = opts.delete(:binmode)

  popen2(*cmd, opts) {|i, o, t|
    if binmode
      i.binmode
      o.binmode
    end
    out_reader = Thread.new { o.read }
    if stdin_data
      begin
        if stdin_data.respond_to? :readpartial
          IO.copy_stream(stdin_data, i)
        else
          i.write stdin_data
        end
      rescue Errno::EPIPE
      end
    end
    i.close
    [out_reader.value, t.value]
  }
end

По сути, это обёртка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает в виде строки stdout_s стандартный вывод дочернего процесса.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_s, status]:

stdout_s, status = Open3.capture2('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326047 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Open3.popen3; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Open3.popen3; см. раздел Параметры выполнения.

Передаётся хеш options; два параметра локально действуют в методе Open3.capture2:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся в стандартный ввод команды:

    Open3.capture2('tee', stdin_data: 'Foo')
    
    # => ["Foo", #<Process::Status: pid 2326087 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а для внутренних потоков устанавливается двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.capture2('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", #<Process::Status: pid 2326131 exit 0>]
Open3.capture2('echo')                         # Built-in.
# => ["\n", #<Process::Status: pid 2326139 exit 0>]
Open3.capture2('date > date.tmp')              # Contains meta character.
# => ["", #<Process::Status: pid 2326174 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture2('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из 2 элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени выполняющегося процесса.

Пример:

Open3.capture2('/usr/bin/date')
# => ["Fri Sep 29 01:00:39 PM CDT 2023\n", #<Process::Status: pid 2326222 exit 0>]

Ruby вызывает исполняемый файл напрямую, без оболочки и раскрытия выражений оболочкой:

Open3.capture2('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture2('echo', 'C #')
# => ["C #\n", #<Process::Status: pid 2326267 exit 0>]
Open3.capture2('echo', 'hello', 'world')
# => ["hello world\n", #<Process::Status: pid 2326299 exit 0>]
capture2e([env, ] command_line, options = {}) → [stdout_and_stderr_s, status] Показать исходный код
capture2e([env, ] exe_path, *args, options = {}) → [stdout_and_stderr_s, status]
# File lib/open3.rb, line 902
def capture2e(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data)
  binmode = opts.delete(:binmode)

  popen2e(*cmd, opts) {|i, oe, t|
    if binmode
      i.binmode
      oe.binmode
    end
    outerr_reader = Thread.new { oe.read }
    if stdin_data
      begin
        if stdin_data.respond_to? :readpartial
          IO.copy_stream(stdin_data, i)
        else
          i.write stdin_data
        end
      rescue Errno::EPIPE
      end
    end
    i.close
    [outerr_reader.value, t.value]
  }
end

По сути, это обёртка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает в виде строки stdout_and_stderr_s объединённые стандартный вывод и стандартный вывод ошибок дочернего процесса.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_and_stderr_s, status]:

stdout_and_stderr_s, status = Open3.capture2e('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2371692 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Open3.popen3; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Open3.popen3; см. раздел Параметры выполнения.

Передаётся хеш options; два параметра локально действуют в методе Open3.capture2e:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся в стандартный ввод команды:

    Open3.capture2e('tee', stdin_data: 'Foo')
    # => ["Foo", #<Process::Status: pid 2371732 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а для внутренних потоков устанавливается двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.capture2e('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", #<Process::Status: pid 2371740 exit 0>]
Open3.capture2e('echo')                         # Built-in.
# => ["\n", #<Process::Status: pid 2371774 exit 0>]
Open3.capture2e('date > date.tmp')              # Contains meta character.
# => ["", #<Process::Status: pid 2371812 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture2e('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из 2 элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени выполняющегося процесса.

Пример:

Open3.capture2e('/usr/bin/date')
# => ["Sat Sep 30 09:01:46 AM CDT 2023\n", #<Process::Status: pid 2371820 exit 0>]

Ruby вызывает исполняемый файл напрямую, без оболочки и раскрытия выражений оболочкой:

Open3.capture2e('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture2e('echo', 'C #')
# => ["C #\n", #<Process::Status: pid 2371856 exit 0>]
Open3.capture2e('echo', 'hello', 'world')
# => ["hello world\n", #<Process::Status: pid 2371894 exit 0>]
capture3([env, ] command_line, options = {}) → [stdout_s, stderr_s, status] Показать исходный код
capture3([env, ] exe_path, *args, options = {}) → [stdout_s, stderr_s, status]
# File lib/open3.rb, line 648
def capture3(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data) || ''
  binmode = opts.delete(:binmode)

  popen3(*cmd, opts) {|i, o, e, t|
    if binmode
      i.binmode
      o.binmode
      e.binmode
    end
    out_reader = Thread.new { o.read }
    err_reader = Thread.new { e.read }
    begin
      if stdin_data.respond_to? :readpartial
        IO.copy_stream(stdin_data, i)
      else
        i.write stdin_data
      end
    rescue Errno::EPIPE
    end
    i.close
    [out_reader.value, err_reader.value, t.value]
  }
end

По сути, это обёртка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает в виде строк stdout_s и stderr_s стандартный вывод и стандартный вывод ошибок дочернего процесса.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_s, stderr_s, status]:

stdout_s, stderr_s, status = Open3.capture3('echo "Foo"')
# => ["Foo\n", "", #<Process::Status: pid 2281954 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Open3.popen3; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Open3.popen3; см. раздел Параметры выполнения.

Передаётся хеш options; два параметра локально действуют в методе Open3.capture3:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся в стандартный ввод команды:

    Open3.capture3('tee', stdin_data: 'Foo')
    # => ["Foo", "", #<Process::Status: pid 2319575 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а для внутренних потоков устанавливается двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.capture3('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", "", #<Process::Status: pid 2282025 exit 0>]
Open3.capture3('echo')                         # Built-in.
# => ["\n", "", #<Process::Status: pid 2282092 exit 0>]
Open3.capture3('date > date.tmp')              # Contains meta character.
# => ["", "", #<Process::Status: pid 2282110 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture3('echo "Foo"')
# => ["Foo\n", "", #<Process::Status: pid 2282092 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из 2 элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени выполняющегося процесса.

Пример:

Open3.capture3('/usr/bin/date')
# => ["Thu Sep 28 05:03:51 PM CDT 2023\n", "", #<Process::Status: pid 2282300 exit 0>]

Ruby вызывает исполняемый файл напрямую, без оболочки и раскрытия выражений оболочкой:

Open3.capture3('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture3('echo', 'C #')
# => ["C #\n", "", #<Process::Status: pid 2282368 exit 0>]
Open3.capture3('echo', 'hello', 'world')
# => ["hello world\n", "", #<Process::Status: pid 2282372 exit 0>]
pipeline([env, ] *cmds, options = {}) → array_of_statuses Показать исходный код
# File lib/open3.rb, line 1334
def pipeline(*cmds)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  pipeline_run(cmds, opts, [], []) {|ts|
    ts.map(&:value)
  }
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Направляет stdout каждого дочернего процесса в stdin следующего дочернего процесса, а для последнего дочернего процесса — в stdout вызывающего кода.

  • Ожидает завершения дочерних процессов.

  • Возвращает массив объектов Process::Status (по одному для каждого дочернего процесса).

Пример:

wait_threads = Open3.pipeline('ls', 'grep R')
# => [#<Process::Status: pid 2139200 exit 0>, #<Process::Status: pid 2139202 exit 0>]

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

Если первый аргумент является хешем, он становится начальным аргументом env при каждом вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при каждом вызове Process.spawn‘ см. раздел Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к вызываемому исполняемому файлу.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. раздел Аргумент command_line или exe_path.

pipeline_r([env, ] *cmds, options = {}) → [last_stdout, wait_threads] Показать исходный код
# File lib/open3.rb, line 1104
def pipeline_r(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  pipeline_run(cmds, opts, [out_w], [out_r], &block)
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Направляет stdout каждого дочернего процесса в stdin следующего дочернего процесса, а для последнего дочернего процесса — в stdout вызывающего кода.

Метод не ожидает завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не задан, возвращает массив из 2 элементов, содержащий:

  • Поток stdout последнего дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

last_stdout, wait_threads = Open3.pipeline_r('ls', 'grep R')
# => [#<IO:fd 5>, [#<Process::Waiter:0x000055e8de2f9898 dead>, #<Process::Waiter:0x000055e8de2f94b0 sleep>]]
puts last_stdout.read
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

Rakefile
README.md

Если блок задан, вызывает его с потоком stdout последнего дочернего процесса и массивом процессов ожидания:

Open3.pipeline_r('ls', 'grep R') do |last_stdout, wait_threads|
  puts last_stdout.read
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

Если первый аргумент является хешем, он становится начальным аргументом env при каждом вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при каждом вызове Process.spawn; см. раздел Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к вызываемому исполняемому файлу.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. раздел Аргумент command_line или exe_path.

pipeline_rw([env, ] *cmds, options = {}) → [first_stdin, last_stdout, wait_threads] Показать исходный код
# File lib/open3.rb, line 1014
def pipeline_rw(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block)
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Направляет stdout каждого дочернего процесса в stdin следующего дочернего процесса, а для первого дочернего процесса получает данные из stdin вызывающего кода, а для последнего дочернего процесса направляет данные в stdout вызывающего кода.

Метод не ожидает завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не задан, возвращает массив из 3 элементов, содержащий:

  • Поток stdin первого дочернего процесса.

  • Поток stdout последнего дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

first_stdin, last_stdout, wait_threads = Open3.pipeline_rw('sort', 'cat -n')
# => [#<IO:fd 20>, #<IO:fd 21>, [#<Process::Waiter:0x000055e8de29ab40 sleep>, #<Process::Waiter:0x000055e8de29a690 sleep>]]
first_stdin.puts("foo\nbar\nbaz")
first_stdin.close # Send EOF to sort.
puts last_stdout.read
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

1 bar
2 baz
3 foo

Если блок задан, вызывает его с потоком stdin первого дочернего процесса, потоком stdout последнего дочернего процесса и массивом процессов ожидания:

Open3.pipeline_rw('sort', 'cat -n') do |first_stdin, last_stdout, wait_threads|
  first_stdin.puts "foo\nbar\nbaz"
  first_stdin.close # send EOF to sort.
  puts last_stdout.read
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

1 bar
2 baz
3 foo

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

Если первый аргумент является хешем, он становится начальным аргументом env при каждом вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при каждом вызове Process.spawn; см. раздел Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к вызываемому исполняемому файлу.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. раздел Аргумент command_line или exe_path.

pipeline_start([env, ] *cmds, options = {}) → [wait_threads] Показать исходный код
# File lib/open3.rb, line 1272
def pipeline_start(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  if block
    pipeline_run(cmds, opts, [], [], &block)
  else
    ts, = pipeline_run(cmds, opts, [], [])
    ts
  end
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Не ожидает завершения дочерних процессов.

Если блок не задан, возвращает массив потоков ожидания для всех дочерних процессов.

Пример:

wait_threads = Open3.pipeline_start('ls', 'grep R')
# => [#<Process::Waiter:0x000055e8de9d2bb0 run>, #<Process::Waiter:0x000055e8de9d2890 run>]
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

Rakefile
README.md

Если блок задан, вызывает его с массивом процессов ожидания:

Open3.pipeline_start('ls', 'grep R') do |wait_threads|
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

Если первый аргумент является хешем, он становится начальным аргументом env при каждом вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при каждом вызове Process.spawn; см. раздел Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к вызываемому исполняемому файлу.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. раздел Аргумент command_line или exe_path.

pipeline_w([env, ] *cmds, options = {}) → [first_stdin, wait_threads] Показать исходный код
# File lib/open3.rb, line 1195
def pipeline_w(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  pipeline_run(cmds, opts, [in_r], [in_w], &block)
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Направляет stdout каждого дочернего процесса в stdin следующего дочернего процесса, а для первого дочернего процесса направляет stdout вызывающего кода в stdin дочернего процесса.

Метод не ожидает завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не задан, возвращает массив из 2 элементов, содержащий:

  • Поток stdin первого дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

first_stdin, wait_threads = Open3.pipeline_w('sort', 'cat -n')
# => [#<IO:fd 7>, [#<Process::Waiter:0x000055e8de928278 run>, #<Process::Waiter:0x000055e8de923e80 run>]]
first_stdin.puts("foo\nbar\nbaz")
first_stdin.close # Send EOF to sort.
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

1 bar
2 baz
3 foo

Если блок задан, вызывает его с потоком stdin первого дочернего процесса и массивом процессов ожидания:

Open3.pipeline_w('sort', 'cat -n') do |first_stdin, wait_threads|
  first_stdin.puts("foo\nbar\nbaz")
  first_stdin.close # Send EOF to sort.
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

1 bar
2 baz
3 foo

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Инъекция команд.

Если первый аргумент является хешем, он становится начальным аргументом env при каждом вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при каждом вызове Process.spawn; см. раздел Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к вызываемому исполняемому файлу.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. раздел Аргумент command_line или exe_path.

popen2([env, ] command_line, options = {}) → [stdin, stdout, wait_thread] Показать исходный код
popen2([env, ] exe_path, *args, options = {}) → [stdin, stdout, wait_thread]
popen2([env, ] command_line, options = {}) {|stdin, stdout, wait_thread| ... } → object
popen2([env, ] exe_path, *args, options = {}) {|stdin, stdout, wait_thread| ... } → object
# File lib/open3.rb, line 365
def popen2(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создаёт потоки stdin и stdout, которые являются потоками стандартного ввода и стандартного вывода дочернего процесса.

  • Создаёт поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout, wait_thread]. Вызывающий код должен закрыть каждый из двух возвращённых потоков.

stdin, stdout, wait_thread = Open3.popen2('echo')
# => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f58d52dbe98 run>]
stdin.close
stdout.close
wait_thread.pid   # => 2263572
wait_thread.value # => #<Process::Status: pid 2263572 exit 0>

Если блок задан, вызывает его с тремя переменными (двумя потоками и потоком ожидания) и возвращает значение, возвращённое блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen2('echo') do |stdin, stdout, wait_thread|
  p stdin
  p stdout
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<Process::Waiter:0x00007f58d59a34b0 sleep>
2263636
#<Process::Status: pid 2263636 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Process.spawn; см. раздел Окружение выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.popen2('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen2('echo') {|*args| p args }                         # Built-in.
Open3.popen2('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведённых выше вызовов):

# => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577dfe410 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen2('echo "Foo"') { |i, o, t| o.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path — это одно из следующего:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени запускаемого процесса.

Пример:

Open3.popen2('/usr/bin/date') { |i, o, t| o.gets }
# => "Thu Sep 28 09:41:06 AM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и без раскрытия оболочкой:

Open3.popen2('doesnt_exist') { |i, o, t| o.gets } # Raises Errno::ENOENT

Если задан один или несколько аргументов args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen2('echo', 'C #') { |i, o, t| o.gets }
# => "C #\n"
Open3.popen2('echo', 'hello', 'world') { |i, o, t| o.gets }
# => "hello world\n"

Связанные методы:

  • Open3.popen2e: Предоставляет отдельные потоки для стандартного ввода и объединённых потоков стандартного вывода и стандартного вывода ошибок дочернего процесса.

  • Open3.popen3: Предоставляет отдельные потоки для стандартного ввода, стандартного вывода и стандартного вывода ошибок дочернего процесса.

popen2e([env, ] command_line, options = {}) → [stdin, stdout_and_stderr, wait_thread] Показать исходный код
popen2e([env, ] exe_path, *args, options = {}) → [stdin, stdout_and_stderr, wait_thread]
popen2e([env, ] command_line, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } → object
popen2e([env, ] exe_path, *args, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } → object
# File lib/open3.rb, line 508
def popen2e(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[[:out, :err]] = out_w

  popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
ensure
  if block
    in_r.close
    in_w.close
    out_r.close
    out_w.close
  end
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создаёт потоки stdin, stdout_and_stderr, которые являются потоками стандартного ввода и объединёнными потоками стандартного вывода и стандартного вывода ошибок дочернего процесса.

  • Создаёт поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout_and_stderr, wait_thread]. Вызывающий код должен закрыть каждый из двух возвращённых потоков.

stdin, stdout_and_stderr, wait_thread = Open3.popen2e('echo')
# => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f7577da4398 run>]
stdin.close
stdout_and_stderr.close
wait_thread.pid   # => 2274600
wait_thread.value # => #<Process::Status: pid 2274600 exit 0>

Если блок задан, вызывает его с тремя переменными (двумя потоками и потоком ожидания) и возвращает значение, возвращённое блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen2e('echo') do |stdin, stdout_and_stderr, wait_thread|
  p stdin
  p stdout_and_stderr
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<Process::Waiter:0x00007f75777578c8 sleep>
2274763
#<Process::Status: pid 2274763 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Process.spawn; см. раздел Окружение выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.popen2e('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen2e('echo') {|*args| p args }                         # Built-in.
Open3.popen2e('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведённых выше вызовов):

# => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577d8a1f0 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen2e('echo "Foo"') { |i, o_and_e, t| o_and_e.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path — это одно из следующего:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени запускаемого процесса.

Пример:

Open3.popen2e('/usr/bin/date') { |i, o_and_e, t| o_and_e.gets }
# => "Thu Sep 28 01:58:45 PM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и без раскрытия оболочкой:

Open3.popen2e('doesnt_exist') { |i, o_and_e, t| o_and_e.gets } # Raises Errno::ENOENT

Если задан один или несколько аргументов args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen2e('echo', 'C #') { |i, o_and_e, t| o_and_e.gets }
# => "C #\n"
Open3.popen2e('echo', 'hello', 'world') { |i, o_and_e, t| o_and_e.gets }
# => "hello world\n"

Связанные методы:

  • Open3.popen2: Предоставляет отдельные потоки для стандартного ввода и стандартного вывода дочернего процесса, не предоставляя доступа к потоку стандартного вывода ошибок.

  • Open3.popen3: Предоставляет отдельные потоки для стандартного ввода, стандартного вывода и стандартного вывода ошибок дочернего процесса.

popen3([env, ] command_line, options = {}) → [stdin, stdout, stderr, wait_thread] Показать исходный код
popen3([env, ] exe_path, *args, options = {}) → [stdin, stdout, stderr, wait_thread]
popen3([env, ] command_line, options = {}) {|stdin, stdout, stderr, wait_thread| ... } → object
popen3([env, ] exe_path, *args, options = {}) {|stdin, stdout, stderr, wait_thread| ... } → object
# File lib/open3.rb, line 218
def popen3(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  err_r, err_w = IO.pipe
  opts[:err] = err_w

  popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
end

По сути, это обёртка для Process.spawn, которая:

  • Создаёт дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создаёт потоки stdin, stdout и stderr, которые являются потоками стандартного ввода, стандартного вывода и стандартного вывода ошибок дочернего процесса.

  • Создаёт поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout, stderr, wait_thread]. Вызывающий код должен закрыть каждый из трёх возвращённых потоков.

stdin, stdout, stderr, wait_thread = Open3.popen3('echo')
# => [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Process::Waiter:0x00007f58d5428f58 run>]
stdin.close
stdout.close
stderr.close
wait_thread.pid   # => 2210481
wait_thread.value # => #<Process::Status: pid 2210481 exit 0>

Если блок задан, вызывает его с четырьмя переменными (тремя потоками и потоком ожидания) и возвращает значение, возвращённое блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen3('echo') do |stdin, stdout, stderr, wait_thread|
  p stdin
  p stdout
  p stderr
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<IO:fd 9>
#<Process::Waiter:0x00007f58d53606e8 sleep>
2211047
#<Process::Status: pid 2211047 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызывается с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент является хешем, он становится начальным аргументом env при вызове Process.spawn; см. раздел Окружение выполнения.

Если последний аргумент является хешем, он становится завершающим аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.popen3('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen3('echo') {|*args| p args }                         # Built-in.
Open3.popen3('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведённых выше вызовов):

[#<IO:(closed)>, #<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f58d52f28c8 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen3('echo "Foo"') { |i, o, e, t| o.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path — это одно из следующего:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, используемую в качестве имени запускаемого процесса.

Пример:

Open3.popen3('/usr/bin/date') { |i, o, e, t| o.gets }
# => "Wed Sep 27 02:56:44 PM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и без раскрытия оболочкой:

Open3.popen3('doesnt_exist') { |i, o, e, t| o.gets } # Raises Errno::ENOENT

Если задан один или несколько аргументов args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen3('echo', 'C #') { |i, o, e, t| o.gets }
# => "C #\n"
Open3.popen3('echo', 'hello', 'world') { |i, o, e, t| o.gets }
# => "hello world\n"

Следите за тем, чтобы не возникали взаимоблокировки. Потоки вывода stdout и stderr имеют буферы фиксированного размера, поэтому интенсивное чтение из одного потока без чтения из другого может привести к взаимоблокировке, когда непрочитанный буфер заполнится. Чтобы этого избежать, stdout и stderr следует считывать одновременно (с помощью потоков или IO.select).

Связанные методы:

  • Open3.popen2: Предоставляет отдельные потоки для стандартного ввода и стандартного вывода дочернего процесса, не предоставляя доступа к потоку стандартного вывода ошибок.

  • Open3.popen2e: Предоставляет отдельные потоки для стандартного ввода и объединённых потоков стандартного вывода и стандартного вывода ошибок дочернего процесса.

Приватные методы экземпляра

capture2([env, ] command_line, options = {}) → [stdout_s, status] Показать исходный код
capture2([env, ] exe_path, *args, options = {}) → [stdout_s, status]
# File lib/open3.rb, line 775
def capture2(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data)
  binmode = opts.delete(:binmode)

  popen2(*cmd, opts) {|i, o, t|
    if binmode
      i.binmode
      o.binmode
    end
    out_reader = Thread.new { o.read }
    if stdin_data
      begin
        if stdin_data.respond_to? :readpartial
          IO.copy_stream(stdin_data, i)
        else
          i.write stdin_data
        end
      rescue Errno::EPIPE
      end
    end
    i.close
    [out_reader.value, t.value]
  }
end

По сути, это оболочка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает стандартный вывод дочернего процесса в виде строки stdout_s.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_s, status]:

stdout_s, status = Open3.capture2('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326047 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

В отличие от Process.spawn, этот метод дожидается завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Open3.popen3; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Open3.popen3; см. Параметры выполнения.

Передаётся хеш options; два параметра локально влияют на метод Open3.capture2:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся стандартному вводу команды:

    Open3.capture2('tee', stdin_data: 'Foo')
    
    # => ["Foo", #<Process::Status: pid 2326087 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а внутренние потоки переводятся в двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.capture2('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", #<Process::Status: pid 2326131 exit 0>]
Open3.capture2('echo')                         # Built-in.
# => ["\n", #<Process::Status: pid 2326139 exit 0>]
Open3.capture2('date > date.tmp')              # Contains meta character.
# => ["", #<Process::Status: pid 2326174 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture2('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих вариантов:

  • Строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени исполняемого процесса.

Пример:

Open3.capture2('/usr/bin/date')
# => ["Fri Sep 29 01:00:39 PM CDT 2023\n", #<Process::Status: pid 2326222 exit 0>]

Ruby запускает исполняемый файл напрямую, без оболочки и без раскрытия выражений оболочкой:

Open3.capture2('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture2('echo', 'C #')
# => ["C #\n", #<Process::Status: pid 2326267 exit 0>]
Open3.capture2('echo', 'hello', 'world')
# => ["hello world\n", #<Process::Status: pid 2326299 exit 0>]
capture2e([env, ] command_line, options = {}) → [stdout_and_stderr_s, status] Показать исходный код
capture2e([env, ] exe_path, *args, options = {}) → [stdout_and_stderr_s, status]
# File lib/open3.rb, line 902
def capture2e(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data)
  binmode = opts.delete(:binmode)

  popen2e(*cmd, opts) {|i, oe, t|
    if binmode
      i.binmode
      oe.binmode
    end
    outerr_reader = Thread.new { oe.read }
    if stdin_data
      begin
        if stdin_data.respond_to? :readpartial
          IO.copy_stream(stdin_data, i)
        else
          i.write stdin_data
        end
      rescue Errno::EPIPE
      end
    end
    i.close
    [outerr_reader.value, t.value]
  }
end

По сути, это оболочка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает объединённые стандартный вывод и стандартный поток ошибок дочернего процесса в виде строки stdout_and_stderr_s.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_and_stderr_s, status]:

stdout_and_stderr_s, status = Open3.capture2e('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2371692 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

В отличие от Process.spawn, этот метод дожидается завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Open3.popen3; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Open3.popen3; см. Параметры выполнения.

Передаётся хеш options; два параметра локально влияют на метод Open3.capture2e:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся стандартному вводу команды:

    Open3.capture2e('tee', stdin_data: 'Foo')
    # => ["Foo", #<Process::Status: pid 2371732 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а внутренние потоки переводятся в двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.capture2e('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", #<Process::Status: pid 2371740 exit 0>]
Open3.capture2e('echo')                         # Built-in.
# => ["\n", #<Process::Status: pid 2371774 exit 0>]
Open3.capture2e('date > date.tmp')              # Contains meta character.
# => ["", #<Process::Status: pid 2371812 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture2e('echo "Foo"')
# => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих вариантов:

  • Строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени исполняемого процесса.

Пример:

Open3.capture2e('/usr/bin/date')
# => ["Sat Sep 30 09:01:46 AM CDT 2023\n", #<Process::Status: pid 2371820 exit 0>]

Ruby запускает исполняемый файл напрямую, без оболочки и без раскрытия выражений оболочкой:

Open3.capture2e('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture2e('echo', 'C #')
# => ["C #\n", #<Process::Status: pid 2371856 exit 0>]
Open3.capture2e('echo', 'hello', 'world')
# => ["hello world\n", #<Process::Status: pid 2371894 exit 0>]
capture3([env, ] command_line, options = {}) → [stdout_s, stderr_s, status] Показать исходный код
capture3([env, ] exe_path, *args, options = {}) → [stdout_s, stderr_s, status]
# File lib/open3.rb, line 648
def capture3(*cmd)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  stdin_data = opts.delete(:stdin_data) || ''
  binmode = opts.delete(:binmode)

  popen3(*cmd, opts) {|i, o, e, t|
    if binmode
      i.binmode
      o.binmode
      e.binmode
    end
    out_reader = Thread.new { o.read }
    err_reader = Thread.new { e.read }
    begin
      if stdin_data.respond_to? :readpartial
        IO.copy_stream(stdin_data, i)
      else
        i.write stdin_data
      end
    rescue Errno::EPIPE
    end
    i.close
    [out_reader.value, err_reader.value, t.value]
  }
end

По сути, это оболочка для Open3.popen3, которая:

  • Создаёт дочерний процесс, вызывая Open3.popen3 с указанными аргументами (за исключением некоторых записей в хеше options; см. ниже).

  • Возвращает стандартный вывод и стандартный поток ошибок дочернего процесса в виде строк stdout_s и stderr_s.

  • Возвращает в качестве status объект Process::Status, представляющий код завершения дочернего процесса.

Возвращает массив [stdout_s, stderr_s, status]:

stdout_s, stderr_s, status = Open3.capture3('echo "Foo"')
# => ["Foo\n", "", #<Process::Status: pid 2281954 exit 0>]

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

В отличие от Process.spawn, этот метод дожидается завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Open3.popen3; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Open3.popen3; см. Параметры выполнения.

Передаётся хеш options; два параметра локально влияют на метод Open3.capture3:

  • Если существует запись options[:stdin_data], она удаляется, а её строковое значение передаётся стандартному вводу команды:

    Open3.capture3('tee', stdin_data: 'Foo')
    # => ["Foo", "", #<Process::Status: pid 2319575 exit 0>]
    
  • Если существует запись options[:binmode], она удаляется, а внутренние потоки переводятся в двоичный режим.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в противном случае.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки, специальной встроенной команды или содержать метасимволы:

Open3.capture3('if true; then echo "Foo"; fi') # Shell reserved word.
# => ["Foo\n", "", #<Process::Status: pid 2282025 exit 0>]
Open3.capture3('echo')                         # Built-in.
# => ["\n", "", #<Process::Status: pid 2282092 exit 0>]
Open3.capture3('date > date.tmp')              # Contains meta character.
# => ["", "", #<Process::Status: pid 2282110 exit 0>]

Командная строка также может содержать аргументы и параметры команды:

Open3.capture3('echo "Foo"')
# => ["Foo\n", "", #<Process::Status: pid 2282092 exit 0>]

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих вариантов:

  • Строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени исполняемого процесса.

Пример:

Open3.capture3('/usr/bin/date')
# => ["Thu Sep 28 05:03:51 PM CDT 2023\n", "", #<Process::Status: pid 2282300 exit 0>]

Ruby запускает исполняемый файл напрямую, без оболочки и без раскрытия выражений оболочкой:

Open3.capture3('doesnt_exist') # Raises Errno::ENOENT

Если указаны один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.capture3('echo', 'C #')
# => ["C #\n", "", #<Process::Status: pid 2282368 exit 0>]
Open3.capture3('echo', 'hello', 'world')
# => ["hello world\n", "", #<Process::Status: pid 2282372 exit 0>]
pipeline([env, ] *cmds, options = {}) → array_of_statuses Показать исходный код
# File lib/open3.rb, line 1334
def pipeline(*cmds)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  pipeline_run(cmds, opts, [], []) {|ts|
    ts.map(&:value)
  }
end

По сути, это оболочка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Передаёт stdout каждого дочернего процесса в stdin следующего процесса, а для последнего процесса — в stdout вызывающего кода.

  • Дожидается завершения дочерних процессов.

  • Возвращает массив объектов Process::Status (по одному для каждого дочернего процесса).

Пример:

wait_threads = Open3.pipeline('ls', 'grep R')
# => [#<Process::Status: pid 2139200 exit 0>, #<Process::Status: pid 2139202 exit 0>]

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

Если первый аргумент — хеш, он становится начальным аргументом env при каждом вызове Process.spawn; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при каждом вызове Process.spawn; см. Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. Аргумент command_line или exe_path.

pipeline_r([env, ] *cmds, options = {}) → [last_stdout, wait_threads] Показать исходный код
# File lib/open3.rb, line 1104
def pipeline_r(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  pipeline_run(cmds, opts, [out_w], [out_r], &block)
end

По сути, это оболочка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Передаёт stdout каждого дочернего процесса в stdin следующего процесса, а для последнего процесса — в stdout вызывающего кода.

Метод не дожидается завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не передан, возвращается массив из двух элементов:

  • Поток stdout последнего дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

last_stdout, wait_threads = Open3.pipeline_r('ls', 'grep R')
# => [#<IO:fd 5>, [#<Process::Waiter:0x000055e8de2f9898 dead>, #<Process::Waiter:0x000055e8de2f94b0 sleep>]]
puts last_stdout.read
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

Rakefile
README.md

Если блок передан, он вызывается с потоком stdout последнего дочернего процесса и массивом потоков ожидания:

Open3.pipeline_r('ls', 'grep R') do |last_stdout, wait_threads|
  puts last_stdout.read
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

Если первый аргумент — хеш, он становится начальным аргументом env при каждом вызове Process.spawn; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при каждом вызове Process.spawn; см. Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. Аргумент command_line или exe_path.

pipeline_rw([env, ] *cmds, options = {}) → [first_stdin, last_stdout, wait_threads] Показать исходный код
# File lib/open3.rb, line 1014
def pipeline_rw(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block)
end

По сути, это оболочка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Передаёт stdout каждого дочернего процесса в stdin следующего процесса, для первого процесса — из stdin вызывающего кода, а для последнего — в stdout вызывающего кода.

Метод не дожидается завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не передан, возвращается массив из трёх элементов:

  • Поток stdin первого дочернего процесса.

  • Поток stdout последнего дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

first_stdin, last_stdout, wait_threads = Open3.pipeline_rw('sort', 'cat -n')
# => [#<IO:fd 20>, #<IO:fd 21>, [#<Process::Waiter:0x000055e8de29ab40 sleep>, #<Process::Waiter:0x000055e8de29a690 sleep>]]
first_stdin.puts("foo\nbar\nbaz")
first_stdin.close # Send EOF to sort.
puts last_stdout.read
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

1 bar
2 baz
3 foo

Если блок передан, он вызывается с потоком stdin первого дочернего процесса, потоком stdout последнего дочернего процесса и массивом потоков ожидания:

Open3.pipeline_rw('sort', 'cat -n') do |first_stdin, last_stdout, wait_threads|
  first_stdin.puts "foo\nbar\nbaz"
  first_stdin.close # send EOF to sort.
  puts last_stdout.read
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

1 bar
2 baz
3 foo

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

Если первый аргумент — хеш, он становится начальным аргументом env при каждом вызове Process.spawn; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при каждом вызове Process.spawn; см. Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. Аргумент command_line или exe_path.

pipeline_start([env, ] *cmds, options = {}) → [wait_threads] Показать исходный код
# File lib/open3.rb, line 1272
def pipeline_start(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  if block
    pipeline_run(cmds, opts, [], [], &block)
  else
    ts, = pipeline_run(cmds, opts, [], [])
    ts
  end
end

По сути, это оболочка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Не дожидается завершения дочерних процессов.

Если блок не передан, возвращается массив потоков ожидания для всех дочерних процессов.

Пример:

wait_threads = Open3.pipeline_start('ls', 'grep R')
# => [#<Process::Waiter:0x000055e8de9d2bb0 run>, #<Process::Waiter:0x000055e8de9d2890 run>]
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

Rakefile
README.md

Если блок передан, он вызывается с массивом потоков ожидания:

Open3.pipeline_start('ls', 'grep R') do |wait_threads|
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

Rakefile
README.md

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

Если первый аргумент — хеш, он становится начальным аргументом env при каждом вызове Process.spawn; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при каждом вызове Process.spawn; см. Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. Аргумент command_line или exe_path.

pipeline_w([env, ] *cmds, options = {}) → [first_stdin, wait_threads] Показать исходный код
# File lib/open3.rb, line 1195
def pipeline_w(*cmds, &block)
  if Hash === cmds.last
    opts = cmds.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  pipeline_run(cmds, opts, [in_r], [in_w], &block)
end

По сути, это оболочка для Process.spawn, которая:

  • Создаёт дочерний процесс для каждой из указанных cmds, вызывая Process.spawn.

  • Передаёт stdout каждого дочернего процесса в stdin следующего процесса, а для первого процесса передаёт stdout вызывающего кода в stdin дочернего процесса.

Метод не дожидается завершения дочерних процессов, поэтому вызывающий код должен сделать это самостоятельно.

Если блок не передан, возвращается массив из двух элементов:

  • Поток stdin первого дочернего процесса.

  • Массив потоков ожидания для всех дочерних процессов.

Пример:

first_stdin, wait_threads = Open3.pipeline_w('sort', 'cat -n')
# => [#<IO:fd 7>, [#<Process::Waiter:0x000055e8de928278 run>, #<Process::Waiter:0x000055e8de923e80 run>]]
first_stdin.puts("foo\nbar\nbaz")
first_stdin.close # Send EOF to sort.
wait_threads.each do |wait_thread|
  wait_thread.join
end

Вывод:

1 bar
2 baz
3 foo

Если блок передан, он вызывается с потоком stdin первого дочернего процесса и массивом потоков ожидания:

Open3.pipeline_w('sort', 'cat -n') do |first_stdin, wait_threads|
  first_stdin.puts("foo\nbar\nbaz")
  first_stdin.close # Send EOF to sort.
  wait_threads.each do |wait_thread|
    wait_thread.join
  end
end

Вывод:

1 bar
2 baz
3 foo

Как и Process.spawn, этот метод может быть уязвим для атак, если ему переданы недоверенные данные; см. Внедрение команд.

Если первый аргумент — хеш, он становится начальным аргументом env при каждом вызове Process.spawn; см. Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при каждом вызове Process.spawn; см. Параметры выполнения.

Каждый оставшийся аргумент в cmds должен быть одним из следующих:

  • command_line: строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path: строковый путь к исполняемому файлу, который нужно вызвать.

  • Массив, содержащий command_line или exe_path, а также ноль или более строковых аргументов команды.

См. Аргумент command_line или exe_path.

popen2([env, ] command_line, options = {}) → [stdin, stdout, wait_thread] Показать исходный код
popen2([env, ] exe_path, *args, options = {}) → [stdin, stdout, wait_thread]
popen2([env, ] command_line, options = {}) {|stdin, stdout, wait_thread| ... } → object
popen2([env, ] exe_path, *args, options = {}) {|stdin, stdout, wait_thread| ... } → object
# File lib/open3.rb, line 365
def popen2(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
end

По сути, это оболочка для Process.spawn, которая:

  • Создает дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создает потоки stdin и stdout, соответствующие потокам стандартного ввода и стандартного вывода дочернего процесса.

  • Создает поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout, wait_thread]. Вызывающий код должен закрыть каждый из двух возвращенных потоков.

stdin, stdout, wait_thread = Open3.popen2('echo')
# => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f58d52dbe98 run>]
stdin.close
stdout.close
wait_thread.pid   # => 2263572
wait_thread.value # => #<Process::Status: pid 2263572 exit 0>

Если блок задан, вызывает его с тремя переменными (двумя потоками и потоком ожидания) и возвращает значение, возвращенное блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen2('echo') do |stdin, stdout, wait_thread|
  p stdin
  p stdout
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<Process::Waiter:0x00007f58d59a34b0 sleep>
2263636
#<Process::Status: pid 2263636 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызвать его с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в остальных случаях.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.popen2('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen2('echo') {|*args| p args }                         # Built-in.
Open3.popen2('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведенных выше вызовов):

# => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577dfe410 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen2('echo "Foo"') { |i, o, t| o.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени выполняемого процесса.

Пример:

Open3.popen2('/usr/bin/date') { |i, o, t| o.gets }
# => "Thu Sep 28 09:41:06 AM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и подстановки оболочки:

Open3.popen2('doesnt_exist') { |i, o, t| o.gets } # Raises Errno::ENOENT

Если задан один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen2('echo', 'C #') { |i, o, t| o.gets }
# => "C #\n"
Open3.popen2('echo', 'hello', 'world') { |i, o, t| o.gets }
# => "hello world\n"

Связанные методы:

  • Open3.popen2e: предоставляет стандартный ввод и объединенные потоки стандартного вывода и стандартного потока ошибок дочернего процесса в виде отдельных потоков.

  • Open3.popen3: предоставляет потоки стандартного ввода, стандартного вывода и стандартного потока ошибок дочернего процесса в виде отдельных потоков.

popen2e([env, ] command_line, options = {}) → [stdin, stdout_and_stderr, wait_thread] Показать исходный код
popen2e([env, ] exe_path, *args, options = {}) → [stdin, stdout_and_stderr, wait_thread]
popen2e([env, ] command_line, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } → object
popen2e([env, ] exe_path, *args, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } → object
# File lib/open3.rb, line 508
def popen2e(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[[:out, :err]] = out_w

  popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block)
ensure
  if block
    in_r.close
    in_w.close
    out_r.close
    out_w.close
  end
end

По сути, это оболочка для Process.spawn, которая:

  • Создает дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создает потоки stdin, stdout_and_stderr, соответствующие стандартному вводу и объединенным потокам стандартного вывода и стандартного потока ошибок дочернего процесса.

  • Создает поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout_and_stderr, wait_thread]. Вызывающий код должен закрыть каждый из двух возвращенных потоков.

stdin, stdout_and_stderr, wait_thread = Open3.popen2e('echo')
# => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f7577da4398 run>]
stdin.close
stdout_and_stderr.close
wait_thread.pid   # => 2274600
wait_thread.value # => #<Process::Status: pid 2274600 exit 0>

Если блок задан, вызывает его с тремя переменными (двумя потоками и потоком ожидания) и возвращает значение, возвращенное блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen2e('echo') do |stdin, stdout_and_stderr, wait_thread|
  p stdin
  p stdout_and_stderr
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<Process::Waiter:0x00007f75777578c8 sleep>
2274763
#<Process::Status: pid 2274763 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызвать его с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в остальных случаях.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.popen2e('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen2e('echo') {|*args| p args }                         # Built-in.
Open3.popen2e('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведенных выше вызовов):

# => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577d8a1f0 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen2e('echo "Foo"') { |i, o_and_e, t| o_and_e.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени выполняемого процесса.

Пример:

Open3.popen2e('/usr/bin/date') { |i, o_and_e, t| o_and_e.gets }
# => "Thu Sep 28 01:58:45 PM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и подстановки оболочки:

Open3.popen2e('doesnt_exist') { |i, o_and_e, t| o_and_e.gets } # Raises Errno::ENOENT

Если задан один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen2e('echo', 'C #') { |i, o_and_e, t| o_and_e.gets }
# => "C #\n"
Open3.popen2e('echo', 'hello', 'world') { |i, o_and_e, t| o_and_e.gets }
# => "hello world\n"

Связанные методы:

  • Open3.popen2: предоставляет потоки стандартного ввода и стандартного вывода дочернего процесса в виде отдельных потоков, без доступа к потоку стандартных ошибок.

  • Open3.popen3: предоставляет потоки стандартного ввода, стандартного вывода и стандартного потока ошибок дочернего процесса в виде отдельных потоков.

popen3([env, ] command_line, options = {}) → [stdin, stdout, stderr, wait_thread] Показать исходный код
popen3([env, ] exe_path, *args, options = {}) → [stdin, stdout, stderr, wait_thread]
popen3([env, ] command_line, options = {}) {|stdin, stdout, stderr, wait_thread| ... } → object
popen3([env, ] exe_path, *args, options = {}) {|stdin, stdout, stderr, wait_thread| ... } → object
# File lib/open3.rb, line 218
def popen3(*cmd, &block)
  if Hash === cmd.last
    opts = cmd.pop.dup
  else
    opts = {}
  end

  in_r, in_w = IO.pipe
  opts[:in] = in_r
  in_w.sync = true

  out_r, out_w = IO.pipe
  opts[:out] = out_w

  err_r, err_w = IO.pipe
  opts[:err] = err_w

  popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
end

По сути, это оболочка для Process.spawn, которая:

  • Создает дочерний процесс, вызывая Process.spawn с указанными аргументами.

  • Создает потоки stdin, stdout и stderr, соответствующие стандартному вводу, стандартному выводу и стандартному потоку ошибок дочернего процесса.

  • Создает поток wait_thread, который ожидает завершения дочернего процесса; у потока есть метод pid, возвращающий идентификатор процесса дочернего процесса.

Если блок не задан, возвращает массив [stdin, stdout, stderr, wait_thread]. Вызывающий код должен закрыть каждый из трех возвращенных потоков.

stdin, stdout, stderr, wait_thread = Open3.popen3('echo')
# => [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Process::Waiter:0x00007f58d5428f58 run>]
stdin.close
stdout.close
stderr.close
wait_thread.pid   # => 2210481
wait_thread.value # => #<Process::Status: pid 2210481 exit 0>

Если блок задан, вызывает его с четырьмя переменными (тремя потоками и потоком ожидания) и возвращает значение, возвращенное блоком. Вызывающему коду не нужно закрывать потоки:

Open3.popen3('echo') do |stdin, stdout, stderr, wait_thread|
  p stdin
  p stdout
  p stderr
  p wait_thread
  p wait_thread.pid
  p wait_thread.value
end

Вывод:

#<IO:fd 6>
#<IO:fd 7>
#<IO:fd 9>
#<Process::Waiter:0x00007f58d53606e8 sleep>
2211047
#<Process::Status: pid 2211047 exit 0>

Как и Process.spawn, этот метод может быть уязвим для атак, если вызвать его с недоверенными данными; см. раздел Внедрение команд.

В отличие от Process.spawn, этот метод ожидает завершения дочернего процесса перед возвратом, поэтому вызывающему коду не нужно делать это самостоятельно.

Если первый аргумент — хеш, он становится начальным аргументом env при вызове Process.spawn; см. раздел Среда выполнения.

Если последний аргумент — хеш, он становится конечным аргументом options при вызове Process.spawn; см. раздел Параметры выполнения.

Единственный обязательный аргумент должен быть одним из следующих:

  • command_line, если это строка, начинающаяся с зарезервированного слова оболочки или специальной встроенной команды либо содержащая один или несколько метасимволов.

  • exe_path в остальных случаях.

Аргумент command_line

Строковый аргумент command_line — это командная строка, передаваемая оболочке; она должна начинаться с зарезервированного слова оболочки или специальной встроенной команды либо содержать метасимволы:

Open3.popen3('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
Open3.popen3('echo') {|*args| p args }                         # Built-in.
Open3.popen3('date > date.tmp') {|*args| p args }              # Contains meta character.

Вывод (аналогичен для каждого из приведенных выше вызовов):

[#<IO:(closed)>, #<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f58d52f28c8 dead>]

Командная строка также может содержать аргументы и параметры команды:

Open3.popen3('echo "Foo"') { |i, o, e, t| o.gets }
"Foo\n"

Аргумент exe_path

Аргумент exe_path должен быть одним из следующих:

  • Строковый путь к вызываемому исполняемому файлу.

  • Массив из двух элементов, содержащий путь к исполняемому файлу и строку, которая будет использоваться в качестве имени выполняемого процесса.

Пример:

Open3.popen3('/usr/bin/date') { |i, o, e, t| o.gets }
# => "Wed Sep 27 02:56:44 PM CDT 2023\n"

Ruby вызывает исполняемый файл напрямую, без оболочки и подстановки оболочки:

Open3.popen3('doesnt_exist') { |i, o, e, t| o.gets } # Raises Errno::ENOENT

Если задан один или несколько args, каждый из них является аргументом или параметром, передаваемым исполняемому файлу:

Open3.popen3('echo', 'C #') { |i, o, e, t| o.gets }
# => "C #\n"
Open3.popen3('echo', 'hello', 'world') { |i, o, e, t| o.gets }
# => "hello world\n"

Не допускайте взаимных блокировок. Выходные потоки stdout и stderr имеют буферы фиксированного размера, поэтому интенсивное чтение из одного потока при отсутствии чтения из другого может привести к взаимной блокировке, когда непрочитанный буфер заполнится. Чтобы избежать этого, stdout и stderr следует читать одновременно (с помощью потоков или IO.select).

Связанные методы:

  • Open3.popen2: предоставляет потоки стандартного ввода и стандартного вывода дочернего процесса в виде отдельных потоков, без доступа к потоку стандартных ошибок.

  • Open3.popen2e: предоставляет стандартный ввод и объединенные потоки стандартного вывода и стандартного потока ошибок дочернего процесса в виде отдельных потоков.

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API