Top Level Namespace

Defined Under Namespace

Modules: Exeggutor

Instance Method Summary collapse

Instance Method Details

#exeg(args, can_fail: false, show_stdout: false, show_stderr: false, env: nil, chdir: nil, stdin_data: nil) ⇒ ProcessResult

Executes a command with the provided arguments and options. Waits for the process to finish.

Parameters:

  • args (Array<String>)

    The command and its arguments as an array.

  • can_fail (Boolean) (defaults to: false)

    If false, raises a ProcessError on failure.

  • show_stdout (Boolean) (defaults to: false)

    If true, prints stdout to the console in real-time.

  • show_stderr (Boolean) (defaults to: false)

    If true, prints stderr to the console in real-time.

  • chdir (String, nil) (defaults to: nil)

    The working directory to run the command in. If nil, uses the current working directory.

  • stdin (String, nil)

    Input data to pass to the command’s stdin. If nil, doesn’t pass any data to stdin.

  • env (Hash{String => String}, nil) (defaults to: nil)

    A hashmap containing environment variable overrides, or ‘nil` if no overrides are desired

Returns:

  • (ProcessResult)

    An object containing process info such as stdout, stderr, and exit code.

Raises:

  • (ProcessError)

    If the command fails and ‘can_fail` is false.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/exeggutor.rb', line 163

def exeg(args, can_fail: false, show_stdout: false, show_stderr: false, env: nil, chdir: nil, stdin_data: nil)
  raise "args.size must be >= 1" if args.empty?

  stdin_io, stdout_io, stderr_io, wait_thr = Exeggutor::run_popen3(args, env, chdir)
  stdin_io.write(stdin_data) if stdin_data
  stdin_io.close

  # Make the streams as synchronous as possible, to minimize the possibility of a surprising lack
  # of output
  stdout_io.sync = true
  stderr_io.sync = true

  stdout = +''
  stderr = +''

  # Although there could be more code sharing between this and exeg_async, it would either complicate exeg_async's inner workings
  # or force us to pay the same performance cost that exeg_async does
  remaining_ios = [stdout_io, stderr_io]
  while remaining_ios.size > 0
    readable_ios, = IO.select(remaining_ios)
    for readable_io in readable_ios
      begin
        data = readable_io.read_nonblock(100_000)
        if readable_io == stdout_io
          stdout << data
          $stdout.print(data) if show_stdout
        else
          stderr << data
          $stderr.print(data) if show_stderr
        end
      rescue IO::WaitReadable
        # Shouldn't usually happen because IO.select indicated data is ready, but maybe due to EINTR or something
        next
      rescue EOFError
        remaining_ios.delete(readable_io)
      end
    end
  end

  result = Exeggutor::ProcessResult.new(
    stdout: stdout,
    stderr: stderr,
    exit_code: wait_thr.value.exitstatus,
    pid: wait_thr.pid
  )
  if !can_fail && !result.success?
    error_str = <<~ERROR_STR
      Command failed: #{args.shelljoin}
      Exit code: #{result.exit_code}
      stdout: #{result.stdout}
      stderr: #{result.stderr}
      pid: #{result.pid}
    ERROR_STR
    raise Exeggutor::ProcessError.new(result), error_str
  end

  result
end

#exeg_async(args, env: nil, chdir: nil) ⇒ ProcessHandle

Executes a command with the provided arguments and options. Does not wait for the process to finish.

Parameters:

  • args (Array<String>)

    The command and its arguments as an array.

  • chdir (String, nil) (defaults to: nil)

    The working directory to run the command in. If nil, uses the current working directory.

  • env (Hash{String => String}, nil) (defaults to: nil)

    A hashmap containing environment variable overrides, or ‘nil` if no overrides are desired

Returns:

  • (ProcessHandle)


230
231
232
# File 'lib/exeggutor.rb', line 230

def exeg_async(args, env: nil, chdir: nil)
  Exeggutor::ProcessHandle.new(args, env: env, chdir: chdir)
end