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