Module: ShellHelpers::Sh

Extended by:
Sh
Includes:
CLILogging
Included in:
ShellHelpers, Sh, ShDryRun, ShLog, ShQuiet, ShSilent
Defined in:
lib/shell_helpers/sh.rb

Constant Summary

Constants included from CLILogging

CLILogging::LOG_LEVELS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CLILogging

#change_logger, #log_and_do, #logger, setup_toggle_trap

Instance Attribute Details

#default_sh_optionsObject



152
153
154
155
156
157
158
159
# File 'lib/shell_helpers/sh.rb', line 152

def default_sh_options
  @default_sh_options||={log: true, capture: false, on_success: nil, on_error: nil, expected:0, dryrun: false, escape: false,
  log_level_execute_debug: :debug,
  log_level_execute: :info, log_level_error: :error,
  log_level_stderr: :error, log_level_stdout_success: :info,
  log_level_stdout_fail: :warn, detach: false,
  mode: :system}
end

#spawnedObject



162
163
164
# File 'lib/shell_helpers/sh.rb', line 162

def spawned
  @spawned||=[]
end

Instance Method Details

#change_sh_logger(logger) ⇒ Object

Override the default logger (which is the one provided by CLILogging). You would do this if you want a custom logger or you aren't mixing-in CLILogging.

Note that this method is not called sh_logger= to avoid annoying situations where Ruby thinks you are setting a local variable



322
323
324
# File 'lib/shell_helpers/sh.rb', line 322

def change_sh_logger(logger)
  @sh_logger = logger
end

#sh(*command, argv0: nil, **opts) ⇒ Object

callbacks: on_success, on_error yield process_status.success?,stdout,stderr,process_status if block_given? returns success; except in capture mode where it returns success, stdout, stderr, process_status



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/shell_helpers/sh.rb', line 229

def sh(*command, argv0: nil, **opts)
  defaults=default_sh_options
  curopts=defaults.dup
  defaults.keys.each do |k|
    v=opts.delete(k)
    curopts[k]=v unless v.nil?
  end

  log=curopts[:log]
  command=[[command.first, argv0], *command[1..-1]] if argv0 and command.length > 1 and !curopts[:escape]

  if command.length==1 and command.first.kind_of?(Array) #so that sh(["ls", "-a"]) works
    command=command.first
    command=[[command.first, argv0], *command[1..-1]] if argv0 and !curopts[:escape]
  end
  command_name = curopts[:name] || command_name(command) #this keep the options
  # this should not be needed
  command=command.shelljoin if curopts[:escape]
  if log
    sh_logger.send(curopts[:log_level_execute], SimpleColor.color("Executing '#{command_name}'",:bold))
    p_env, p_args, p_opts= Run.process_command(*command, **opts)
    sh_logger.send(curopts[:log_level_execute_debug], SimpleColor.color("Debug execute: #{[p_env, *p_args, p_opts]}", :bold))
  end

  if !curopts[:dryrun]
    if curopts[:capture] || curopts[:mode]==:capture
      stdout,stderr,status = shrun(*command,**opts,mode: :capture)
    elsif curopts[:detach] || curopts[:mode]==:spawn || curopts[:mode]==:detach
      mode = curopts[:detach] ? :detach : curops[:mode]
      _pid = shrun(*command,**opts, mode: mode)
      status=0; stdout=nil; stderr=nil
    elsif curopts[:mode]==:run
      status, stdout, stderr=shrun(*command,mode: curopts[:mode], **opts)
    else
      mode = curopts[:mode] || :system
      shrun(*command,mode: mode, **opts)
      status=$?; stdout=nil; stderr=nil
    end
  else
    sh_logger.info command.to_s
    status=0; stdout=nil; stderr=nil
  end
  process_status = ProcessStatus.new(status,curopts[:expected])

  sh_logger.send(curopts[:log_level_stderr], SimpleColor.color("stderr output of '#{command_name}':\n",:bold,:red)+stderr) unless stderr.nil? or stderr.strip.length == 0 or !log
  if process_status.success?
    sh_logger.send(curopts[:log_level_stdout_success], SimpleColor.color("stdout output of '#{command_name}':\n",:bold,:green)+stdout) unless stdout.nil? or stdout.strip.length == 0 or !log
    curopts[:on_success].call(stdout,stderr,process_status) unless curopts[:on_success].nil?
    # block.call(stdout,stderr,process_status) unless block.nil?
  else
    sh_logger.send(curopts[:log_level_stdout_fail], SimpleColor.color("stdout output of '#{command_name}':\n",:bold,:yellow)+stdout) unless stdout.nil? or stdout.strip.length == 0 or !log
    sh_logger.send(curopts[:log_level_error], SimpleColor.color("Error running '#{command_name}': #{process_status.status}",:red,:bold)) if log
    curopts[:on_error].call(stdout,stderr,process_status) unless curopts[:on_error].nil?
  end
  yield process_status.success?,stdout,stderr,process_status if block_given?
  if curopts[:capture] || curopts[:mode]==:capture || curopts[:mode]==:run
    return process_status.success?,stdout,stderr,process_status
  else
    return process_status.success?
  end

rescue SystemCallError => ex
  sh_logger.send(curopts[:log_level_error], SimpleColor.color("Error running '#{command_name}': #{ex.message}",:red,:bold)) if log
  if block_given?
    yield 127, nil, nil, nil
  else
    return 127, nil, nil, nil
  end
end

#sh!(*args, failure_msg: nil, **opts, &block) ⇒ Object

Run a command, throwing an exception if the command exited nonzero. Otherwise, behaves exactly like #sh. Raises SH::FailedCommandError if the command exited nonzero. Examples:

    sh!("rsync foo bar")
    # => if command fails, app exits and user sees: "error: Command 'rsync foo bar' exited 12"
    sh!("rsync foo bar", :failure_msg => "Couldn't rsync, check log for details")
    # => if command fails, app exits and user sees: "error: Couldn't rsync, check log for details


308
309
310
311
312
313
314
# File 'lib/shell_helpers/sh.rb', line 308

def sh!(*args,failure_msg: nil,**opts, &block)
  on_error=Proc.new do |*blockargs|
    process_status=blockargs.last
    raise FailedCommandError.new(process_status.exitstatus,command_name(args),failure_msg: failure_msg)
  end
  sh(*args,**opts,on_error: on_error,&block)
end

#sh_commands(com, **opts) ⇒ Object

split commands on newlines and run sh on each line



327
328
329
330
331
# File 'lib/shell_helpers/sh.rb', line 327

def sh_commands(com, **opts)
  com.each_line do |line|
    sh(line.chomp,**opts)
  end
end

#sh_or_proc(cmd, *args, **opts, &b) ⇒ Object

returns only the success or failure



334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/shell_helpers/sh.rb', line 334

def sh_or_proc(cmd, *args, **opts, &b)
  case cmd
  when Proc
    cmd.call(*args, **opts, &b)
  when Array
    suc, _r=SH.sh(*cmd, *args, **opts, &b)
    suc
  when String
    suc, _r=SH.sh(cmd + " #{args.shelljoin}", **opts, &b)
    suc
  end
end

#shrun(*args, mode: :system, block: nil, **opts) ⇒ Object

callback called by sh to select the exec mode mode: :system,:spawn,:exec,:capture opts: sudo, env



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
# File 'lib/shell_helpers/sh.rb', line 172

def shrun(*args,mode: :system, block: nil, **opts)
  env, args, spawn_opts=Run.process_command(*args, **opts)
  # p env, args, spawn_opts
  case mode
  when :system
    system(env,*args,spawn_opts)
  when :spawn, :detach
    pid=spawn(env,*args,spawn_opts, &block)
    if mode==:detach
      Process.detach(pid) 
    else
      spawned << pid
      if block_given?
        yield pid
        Process.wait(pid)
      else
        pid
      end
    end
  when :exec
    exec(env,*args,spawn_opts, &block)
  when :capture
    Run.run_command(env,*args,spawn_opts, &block)
  when :run
    Run.run(env,*args,spawn_opts, &block)
  else
    raise ShError.new("In shrun, mode #{mode} not understood")
  end
end

#wait_spawnedObject



165
166
167
# File 'lib/shell_helpers/sh.rb', line 165

def wait_spawned
  spawned.each {|c| Process.waitpid(c)}
end