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