Module: ShellHelpers::Utils

Extended by:
Utils
Included in:
ShellHelpers, Utils
Defined in:
lib/shell_helpers/utils.rb

Class Attribute Summary collapse

Instance Method Summary collapse

Class Attribute Details

.orig_stderrObject

Returns the value of attribute orig_stderr.



38
39
40
# File 'lib/shell_helpers/utils.rb', line 38

def orig_stderr
  @orig_stderr
end

.orig_stdinObject

Returns the value of attribute orig_stdin.



38
39
40
# File 'lib/shell_helpers/utils.rb', line 38

def orig_stdin
  @orig_stdin
end

.orig_stdoutObject

Returns the value of attribute orig_stdout.



38
39
40
# File 'lib/shell_helpers/utils.rb', line 38

def orig_stdout
  @orig_stdout
end

Instance Method Details

#capture_stdoutObject



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/shell_helpers/utils.rb', line 310

def capture_stdout
	old_stdout = $stdout
	$stdout = StringIO.new('','w')
	if block_given?
		begin
			yield
			output=$stdout.string
		ensure
			$stdout = old_stdout
		end
		return output
	else
		return old_stdout
	end
end

#escape_pager(mode = nil) ⇒ Object

inside run_pager, escape from the pager does not work :-(



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/shell_helpers/utils.rb', line 138

def escape_pager(mode=nil)
	case mode
	when :orig
		stdout=ShellUtils.orig_stdout
		stderr=ShellUtils.orig_stderr
		stdin=ShellUtils.orig_stdin
	else
		stdout=STDOUT
		stderr=STDERR
		stdin=STDIN
	end
	$stdout.reopen(stdout)
	$stderr.reopen(stderr)
	$stdin.reopen(stdin)
end

#eval_shell(r, shell: :puts) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/shell_helpers/utils.rb', line 19

def eval_shell(r, shell: :puts)
	return r if r.nil? or r.empty?
	case (shell||"").to_sym
	when :puts
		puts r
	when :eval
		r+=";" if r && !r.end_with?(';')
		print r
	when :exec
		require 'shell_helpers/sh'
		return ShLog.sh(r)
	when :exec_quiet
		require 'shell_helpers/sh'
		return Sh.sh(*r)
	end
	return r
end

#find(*bases, filter: nil, prune: nil, follow_symlink: false, depth: false, max_depth: nil, chdir: false) ⇒ Object

An improved find from Find::find that takes in the block the absolute and relative name of the files (+the directory where the relative file is from), and has filter options Returns ::Pathname, except when the value is a SH::Pathname where it returns a SH::Pathname



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/shell_helpers/utils.rb', line 47

def find(*bases, filter: nil, prune: nil, follow_symlink: false, depth: false, max_depth: nil, chdir: false)
	block_given? or return enum_for(__method__, *bases, filter: filter, follow_symlink: follow_symlink, depth: depth, max_depth: max_depth, chdir: chdir)
	bases.collect!{|d| raise Errno::ENOENT unless File.exist?(d); d.dup}.each do |base|
		klass=base.is_a?(::Pathname) ? base.class : ::Pathname
		base=klass.new(base)

		test_filter=lambda do |filter,*files|
			case filter
			when Proc
				filter.call(*files)
			when Array
				file=files.first
				filter.any? do |test|
					case test
					when :directory? #special case
						file.directory? && !file.symlink?
					else
						file.send(test)
					end
				end
			end
		end

		yield_files=lambda do |*files|
			unless test_filter.(filter,*files)
				files.map! {|f| f.dup.taint}
				if chdir
					Dir.chdir(base) do
						yield(*files, base)
					end
				else
					yield(*files, base)
				end
			end
		end

		do_find=lambda do |*files|
			file,filerel=*files
			catch(:prune) do #use throw(:prune) to skip a path (recursively)
				unless test_filter.(prune,*files)
					yield_files.(*files) unless depth
					if file.directory? and (max_depth.nil? or (filerel.to_s=="." and max_depth>0) or filerel.each_filename.to_a.size < max_depth)
						next if !follow_symlink && file.symlink?
						file.children(false).sort.reverse_each do |f|
							fj = file + f
							f = filerel + f
							do_find.(fj.untaint,f.untaint)
						end
						yield_files.(*files) if depth
					end
				end
			end
		end
		do_find.call(base, klass.new('.'))
	end
end

#find_executable(bin, path = nil, exts: nil) ⇒ Object

Stolen from mkmf: Searches for the executable +bin+ on +path+. The default path is your +PATH+ environment variable. If that isn't defined, it will resort to searching /usr/local/bin, /usr/ucb, /usr/bin and /bin. If found, it will return the full path, including the executable name, of where it was found. exts: an array of extensions to add



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

def find_executable(bin, path = nil, exts: nil)
	executable_file = lambda do |name|
		name=Pathname.new(name)
		return name if name.file? and name.executable?
	end
	#we use Proc so that 'return' escapes the block
	try_executable = Proc.new do |file|
		return file if executable_file.call(file)
		exts && exts.each {|ext| executable_file.call(ext = file.append_name(ext)) and return ext}
		nil
	end

	bin=Pathname.new(bin)
	if bin.absolute?
		try_executable.call(bin)
	else
		path ||= ENV['PATH'] || %w[/usr/local/bin /usr/bin /bin]
		path = path.split(File::PATH_SEPARATOR) unless path.kind_of?(Array)
		path.each do |dir|
			dir=Pathname.new(dir)
			try_executable.call(dir+bin)
		end
	end
	nil
end

#find_file(file, path) ⇒ Object



192
193
194
195
196
197
198
199
# File 'lib/shell_helpers/utils.rb', line 192

def find_file(file,path)
	path.each do |dir|
		dir=Pathname.new(dir)
		path=dir+file
		return path if path.file?
	end
	return nil
end

#find_files(pattern, path) ⇒ Object



201
202
203
# File 'lib/shell_helpers/utils.rb', line 201

def find_files(pattern,path)
	path.map { |dir| Pathname.glob(dir+pattern) }.flatten
end

#output_list(s, split: "\n") ⇒ Object



154
155
156
157
# File 'lib/shell_helpers/utils.rb', line 154

def output_list(s, split: "\n")
	s=s.shelljoin if s.kind_of?(Array)
	return open("| #{s}").read.split(split)
end

#rsync(*files, out, default_opts: "-vczz", preserve: true, partial: true, keep_dirlinks: false, backup: false, relative: false, delete: false, clean_out: false, clobber: true, expected: 23, chown: nil, sshcommand: nil, exclude: [], **opts, &b) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/shell_helpers/utils.rb', line 205

def rsync(*files, out, default_opts: "-vczz", preserve: true, partial: true, keep_dirlinks: false, backup: false, relative: false, delete: false, clean_out: false, clobber: true, expected: 23, chown: nil, sshcommand: nil, exclude: [], **opts, &b)
	require 'shell_helpers/sh'
	rsync_opts=[*opts.delete(:rsync_opts)] || []
	rsync_opts << default_opts
	rsync_opts << "-a" if preserve
	rsync_opts << "-P" if partial #--partial --progress
	rsync_opts+=%w(--no-owner --no-group) if preserve==:nochown
	rsync_opts+=["--chown", chown] if chown
	rsync_opts << "--ignore-existing" unless clobber
	#on dest: do not replace a symlink to a directory with the real directory
	#use --copy-dirlinks for the same usage on source
	rsync_opts << "--keep-dirlinks" if keep_dirlinks
	exclude.each do |ex|
		rsync_opts += ["--exclude", ex.shellescape]
	end
	if relative
		rsync_opts << "--relative"
		rsync_opts << "--no-implied-dirs"
	end
	rsync_opts << "--delete" if delete
	if clean_out
		out=Pathname.new(out)
		out.rmtree
		out.mkpath
	end
	opts[:log]||=true
	opts[:log_level_execute]||=:info
	if backup
		rsync_opts << "--backup"
		rsync_opts << (backup.to_s[-1]=="/" ? "--backup-dir=#{backup}" : "--suffix=#{backup}") unless backup==true
	end
	if sshcommand
		rsync_opts << "-e"
		rsync_opts << sshcommand.shellescape
	end
	rsync_opts+=opts.delete(:rsync_late_opts)||[]
	cmd=["rsync"]+rsync_opts+files.map(&:to_s)+[out.to_s]
	Sh.sh(*cmd, expected: expected, **opts, &b)
	#expected: rsync error code 23 is some files/attrs were not transferred
end

#run_pager(*args, launch: :tty, default_less_env: "-FRX") ⇒ Object

all output is sent to the pager



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/shell_helpers/utils.rb', line 105

def run_pager(*args, launch: :tty, default_less_env: "-FRX")
	return unless $stdout.tty? and launch != :never
	read, write = IO.pipe

	unless Kernel.fork # Child process
		$stdout.reopen(write)
		$stderr.reopen(write) if $stderr.tty?
		read.close
		write.close
		return
	end

	# Parent process, become pager
	$stdin.reopen(read)
	read.close
	write.close

	#ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
	less_env=ENV['LESS']
	less_env=default_less_env if less_env.empty?
	less_env+="F" unless less_env.match(/F/) or launch == :always
	less_env+="R" unless less_env.match(/R/)
	less_env+="X" unless less_env.match(/X/)
	ENV['LESS']=less_env

	Kernel.select [$stdin] # Wait until we have input before we start the pager
	pager = ENV['PAGER'] || 'less'
	run=args.unshift(pager)
	exec(*run) rescue exec "/bin/sh", "-c", *run
end

#ssh(host, *commands, mode: :system, ssh_command: 'ssh', ssh_options: [], ssh_Ooptions: [], port: nil, forward: nil, x11: nil, user: nil, path: nil, parse: true, pty: nil, ssh_env: nil, **opts, &b) ⇒ Object

host can be of the form user@host:port warning this is different from standard ssh syntax of user@host:path



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
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/shell_helpers/utils.rb', line 248

def ssh(host, *commands, mode: :system, ssh_command: 'ssh',
	ssh_options: [], ssh_Ooptions: [],
	port: nil, forward: nil, x11: nil, user: nil, path: nil, parse: true,
	pty: nil, ssh_env:nil,
	**opts, &b)

	#sshkit has a special setting for :local
	host=host.to_s unless mode==:sshkit and host.is_a?(Symbol)
	parse and host.is_a?(String) and host.match(/^(?:(.*)@)?(.*?)(?::(\d*))?$/) do |m|
		user||=m[1]
		host=m[2]
		port||=m[3]
	end
	unless mode==:net_ssh or mode==:sshkit
		ssh_command, *command_options= ssh_command.shellsplit
		ssh_options=command_options+ssh_options
		ssh_options += ["-p", port.to_s] if port
		ssh_options += ["-W", forward] if forward
		if x11 == :trusted
			ssh_options << "-Y"
		elsif x11
			ssh_options << "-X"
		end
		ssh_options << "-T" if pty==false
		ssh_options << "-t" if pty==true
		ssh_options += ssh_Ooptions.map {|o| ["-o", o]}.flatten
	else #net_ssh options needs to be a hash
		ssh_options={} if ssh_options.is_a?(Array)
		ssh_options[:port]=port if port
	end
	case mode
	when :system,:spawn,:capture,:exec
		host="#{user}@#{host}" if user
		env_commands= ssh_env ? [Export.export_variables(ssh_env, inline: true)]+commands : commands
		Sh.sh(* [ssh_command]+ssh_options+[host]+env_commands, mode: mode, **opts)
	when :net_ssh
		require 'net/ssh'
		user=nil;
		Net::SSH.start(host, user, ssh_options, &b)
	when :sshkit
		require 'sshkit'
		host=SSHKit::Host.new(host)
		host.extend(ExtendSSHKit)
		host.port=port if port
		host.user=user if user
		host.ssh_options=ssh_options
		host
	when :uri
		URI::Ssh::Generic.build(scheme: 'ssh', userinfo: user, host: host, path: path, port: port) #, query: ssh_options.join('&'))
	else
		# return options
		{ ssh_command: ssh_command,
		  ssh_options: ssh_options,
		  ssh_command_options: ([ssh_command]+ssh_options).shelljoin,
		  user: user,
		  host: host,
		  path: path,
		  hostssh: user ? "#{user}@#{host}" : host,
		  command: commands }
	end
end