module Benry::UnixCommand

Constants

BENRY_ECHOBACK
CHMOD_MODES

Public Instance Methods

__echo(cmd, args) click to toggle source
# File lib/benry/unixcmd.rb, line 57
def __echo(cmd, args)
  #; [!mzbdj] echoback command arguments.
  optchars = __prepare(cmd, args, "n", nil)
  not_nl   = optchars.include?('n')
  #; [!cjggd] prints arguments.
  #; [!vhpw3] not print newline at end if '-n' option specified.
  print args.join(" ")
  puts "" unless not_nl
end
__echoback?() click to toggle source

alias fu_output_message echoback private :fu_output_message

# File lib/benry/unixcmd.rb, line 46
def __echoback?()
  return self.class.const_get(:BENRY_ECHOBACK)
end
__err(msg) click to toggle source
# File lib/benry/unixcmd.rb, line 24
def __err(msg)
  raise ArgumentError.new(msg)
end
__mkpath(dirpath, pathcache={}) click to toggle source
# File lib/benry/unixcmd.rb, line 1032
def __mkpath(dirpath, pathcache={})
  if ! pathcache.include?(dirpath)
    parent = File.dirname(dirpath)
    __mkpath(parent, pathcache) unless parent == dirpath
    Dir.mkdir(dirpath) unless File.exist?(dirpath)
    pathcache[dirpath] = true
  end
end
__ruby(cmd, args, ignore_error, &b) click to toggle source
# File lib/benry/unixcmd.rb, line 106
def __ruby(cmd, args, ignore_error, &b)
  #; [!98qro] echoback command and args.
  #; [!u5f5l] run ruby command.
  #; [!2jano] returns process status object if ruby command succeeded.
  #; [!69clt] (ruby) error when ruby command failed.
  #; [!z1f03] (ruby!) ignores error even when ruby command failed.
  ruby = RbConfig.ruby
  if args.length == 1
    __sys(cmd, ["#{ruby} #{args[0]}"], ignore_error, &b)
  else
    __sys(cmd, [ruby]+args, ignore_error, &b)
  end
end
__store(cmd, args, overwrite, to:) click to toggle source
# File lib/benry/unixcmd.rb, line 990
def __store(cmd, args, overwrite, to:)
  #; [!9wr1o] error when `to:` keyword argument not specified.
  ! to.nil?  or
    __err "#{cmd}: 'to:' keyword argument required."
  #; [!n43u2] echoback command and arguments.
  optchars = __prepare(cmd, args, "pfl", to)
  preserve = optchars.include?("p")
  ignore   = optchars.include?("f")
  hardlink = optchars.include?("l")
  #; [!588e5] error when destination directory not exist.
  #; [!lm43y] error when destination pattern matched to multiple filenames.
  #; [!u5zoy] error when destination is not a directory.
  dir = __glob_onedir(cmd, to)
  #; [!g1duw] error when absolute path specified.
  args.each do |arg|
    #! File.absolute_path?(arg)  or   # Ruby >=  2.7
    File.absolute_path(arg) != arg  or
      __err "#{cmd}: #{arg}: absolute path not expected (only relative path expected)."
  end
  #; [!je1i2] error when file not exist but '-f' option not specified.
  filenames = __glob_filenames(cmd, args, ignore)
  #; [!5619q] (store) error when target file or directory already exists.
  #; [!cw08t] (store!) overwrites existing files.
  if ! overwrite
    filenames.each do |fpath|
      newpath = File.join(dir, fpath)
      ! File.exist?(newpath)  or
        __err "#{cmd}: #{newpath}: destination file or directory already exists."
    end
  end
  #; [!4y4zy] copy files with keeping filepath.
  #; [!f0n0y] copy timestamps if '-p' option specified.
  #; [!w8oq6] creates hard links if '-l' option specified.
  #; [!7n869] error when copying supecial files such as character device.
  pathcache = {}
  filenames.each do |fpath|
    newpath = File.join(dir, fpath)
    __mkpath(File.dirname(newpath), pathcache)
    __cp_file(cmd, fpath, newpath, preserve, hardlink, bufsize=4096)
  end
end
__sys(cmd, args, ignore_error) { |stat| ... } click to toggle source
# File lib/benry/unixcmd.rb, line 76
def __sys(cmd, args, ignore_error, &b)
  #; [!rqe7a] echoback command and arguments.
  echoback(args.join(" ")) if __echoback?()
  result = system(*args)
  #; [!agntr] returns process status if command succeeded.
  #; [!clfig] yields block if command failed.
  #; [!deu3e] not yield block if command succeeded.
  #; [!chko8] block argument is process status.
  #; [!0yy6r] (sys) not raise error if block result is truthy
  #; [!xsspi] (sys) raises error if command failed.
  #; [!tbfii] (sys!) returns process status if command failed.
  stat = $?
  return stat if result
  if block_given?()
    result = yield stat
    return stat if result
  end
  return stat if ignore_error
  raise "Command failed with status (#{$?.exitstatus}): #{args.join(' ')}"
end
__unzip(cmd, args, overwrite) click to toggle source
# File lib/benry/unixcmd.rb, line 1131
def __unzip(cmd, args, overwrite)
  #; [!eqx48] requires 'zip' gem automatically.
  require 'zip' unless defined?(::Zip)
  #; [!ednxk] echoback command and arguments.
  optchars = __prepare(cmd, args, "d", nil)
  outdir   = optchars.include?('d') ? args.shift() : nil
  #; [!1lul7] error if zip file not specified.
  zip_filename = args.shift()  or
    __err "#{cmd}: zip filename required."
  #; [!0yyg8] target directory should not exist, or be empty.
  if outdir
    if ! File.exist?(outdir)
      # pass
    elsif File.directory?(outdir)
      #; [!1ls2h] error if target directory not empty.
      found = Dir.open(outdir) {|dir|
        dir.find {|x| x != '.' && x != '..' }
      }
      ! found  or
        __err "#{cmd}: #{outdir}: directory not empty."
    else
      #; [!lb6r5] error if target directory is not a directory.
      __err "#{cmd}: #{outdir}: not a directory."
    end
  end
  #; [!o1ot5] expands glob pattern.
  #; [!92bh4] error if glob pattern matched to multiple filenames.
  #; [!esnke] error if zip file not found.
  arr = Dir.glob(zip_filename); n = arr.length
  if    n < 1 ; __err "#{cmd}: #{zip_filename}: zip file not found."
  elsif n > 1 ; __err "#{cmd}: #{zip_filename}: matched to multiple filenames (#{arr.sort.join(' ')})."
  else        ; zip_filename = arr[0]
  end
  #
  filenames = args
  filenames = nil if filenames.empty?
  #; [!dzk7c] creates target directory if not exists.
  __mkpath(outdir, {}) if outdir && ! File.exist?(outdir)
  #
  orig = ::Zip.on_exists_proc
  begin
    #; [!06nyv] (unzip!) overwrites existing files.
    ::Zip.on_exists_proc = overwrite
    extglob = File::FNM_EXTGLOB
    #; [!ekllx] (unzip) error when file already exists.
    ::Zip::File.open(zip_filename) do |zf|
      zf.each do |x|
        next if filenames && ! filenames.find {|pat| File.fnmatch?(pat, x.name, extglob) }
        #; [!zg60i] error if file has absolute path.
        outdir || File.absolute_path(x.name) != x.name  or
          __err "#{cmd}: #{x.name}: cannot extract absolute path."
        #
        next if x.directory?
        fpath = outdir ? File.join(outdir, x.name) : x.name
        overwrite || ! File.exist?(fpath)  or
          __err "#{cmd}: #{fpath}: file already exists (to overwrite it, call `#{cmd}!` command instead of `#{cmd}` command)."
      end
    end
    #; [!0tedi] extract zip file.
    ::Zip::File.open(zip_filename) do |zf|
      zf.each do |x|
        #; [!ikq5w] if filenames are specified, extracts files matched to them.
        next if filenames && ! filenames.find {|pat| File.fnmatch?(pat, x.name, extglob) }
        #; [!dy4r4] if '-d' option specified, extracts files under target directory.
        if outdir
          x.extract(File.join(outdir, x.name))
        #; [!5u645] if '-d' option not specified, extracts files under current directory.
        else
          x.extract()
        end
      end
    end
  ensure
    #; [!sjf80] (unzip!) `Zip.on_exists_proc` should be recovered.
    ::Zip.on_exists_proc = orig
  end
end
__zip(cmd, args, overwrite) click to toggle source
# File lib/benry/unixcmd.rb, line 1050
def __zip(cmd, args, overwrite)
  #; [!zzvuk] requires 'zip' gem automatically.
  require 'zip' unless defined?(::Zip)
  #; [!zk1qt] echoback command and arguments.
  optchars = __prepare(cmd, args, "r0123456789", nil)
  recursive = optchars.include?('r')
  complevel = (optchars =~ /(\d)/ ? $1.to_i : nil)
  #; [!lrnj7] zip filename required.
  zip_filename = args.shift()  or
    __err "#{cmd}: zip filename required."
  #; [!khbiq] zip filename can be glob pattern.
  #; [!umbal] error when zip file glob pattern matched to mutilple filenames.
  arr = Dir.glob(zip_filename); n = arr.length
  if    n < 1 ; nil
  elsif n > 1 ; __err "#{cmd}: #{zip_filename}: matched to multiple filenames (#{arr.sort.join(', ')})."
  else        ; zip_filename = arr[0]
  end
  #; [!oqzna] (zip) raises error if zip file already exists.
  ! File.exist?(zip_filename) || overwrite  or
    __err "#{cmd}: #{zip_filename}: already exists (to overwrite it, call `#{cmd}!` command instead of `#{cmd}` command)."
  #; [!uu8uz] expands glob pattern.
  #; [!nahxa] error if file not exist.
  filenames = __glob_filenames(cmd, args, false) do |arg, _|
    __err "#{cmd}: #{arg}: file or directory not found."
  end
  #; [!qsp7c] cannot specify absolute path.
  filenames.each do |fname|
    if File.absolute_path(fname) == fname   # Ruby >= 2.7: File.absolute_path?()
      __err "#{cmd}: #{fname}: not support absolute path."
    end
  end
  #; [!e995z] (zip!) removes zip file if exists.
  File.unlink(zip_filename) if File.exist?(zip_filename)
  #; [!3sxmg] supports complession level (0~9).
  orig = Zip.default_compression
  Zip.default_compression = complevel if complevel
  #; [!p8alf] creates zip file.
  begin
    zipf = ::Zip::File.open(zip_filename, create: true) do |zf|  # `compression_level: n` doesn't work. why?
      filenames.each do |fname|
        __zip_add(cmd, zf, fname, recursive)
      end
      zf
    end
  ensure
    #; [!h7yxl] restores value of `Zip.default_compression`.
    Zip.default_compression = orig if complevel
  end
  #; [!fvvn8] returns zip file object.
  return zipf
end
__zip_add(cmd, zf, fpath, recursive) click to toggle source
# File lib/benry/unixcmd.rb, line 1102
def __zip_add(cmd, zf, fpath, recursive)
  ftype = File.ftype(fpath)
  case ftype
  when 'link'; zf.add(fpath, fpath)
  when 'file'; zf.add(fpath, fpath)
  when 'directory'
    zf.add(fpath, fpath)
    #; [!bgdg7] adds files recursively into zip file if '-r' option specified.
    Dir.open(fpath) do |dir|
      dir.each do |x|
        next if x == '.' || x == '..'
        __zip_add(cmd, zf, File.join(fpath, x), recursive)
      end
    end if recursive
  else
    #; [!jgt96] error when special file specified.
    __err "#{cmd}: #{fpath}: #{ftype} file not supported."
  end
end
capture2( *args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 134
def capture2(  *args, **kws); __capture(:capture2 , args, kws, false); end
capture2!( *args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 137
def capture2!( *args, **kws); __capture(:capture2 , args, kws, true ); end
capture2e( *args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 135
def capture2e( *args, **kws); __capture(:capture2e, args, kws, false); end
capture2e!(*args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 138
def capture2e!(*args, **kws); __capture(:capture2e, args, kws, true ); end
capture3( *args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 136
def capture3(  *args, **kws); __capture(:capture3 , args, kws, false); end
capture3!( *args, **kws) click to toggle source
# File lib/benry/unixcmd.rb, line 139
def capture3!( *args, **kws); __capture(:capture3 , args, kws, true ); end
cd(arg) { || ... } click to toggle source
# File lib/benry/unixcmd.rb, line 162
def cd(arg, &b)
  cmd = 'cd'
  #; [!gnmdg] expands file pattern.
  #; [!v7bn7] error when pattern not matched to any file.
  #; [!08wuv] error when pattern matched to multiple files.
  #; [!hs7u8] error when argument is not a directory name.
  dir = __glob_onedir(cmd, arg)
  #; [!cg5ns] changes current directory.
  here = Dir.pwd
  echoback("cd #{dir}") if __echoback?()
  Dir.chdir(dir)
  #; [!uit6q] if block given, then back to current dir.
  if block_given?()
    @__depth ||= 0
    @__depth += 1
    begin
      yield
    ensure
      @__depth -= 1
      echoback("cd -") if __echoback?()
      Dir.chdir(here)
    end
  end
  #; [!cg298] returns path before changing directory.
  return here
end
Also aliased as: chdir
chdir(arg, &b)
Alias for: cd
chmod(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 822
def chmod(*args)
  __chmod("chmod", args)
end
chown(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 909
def chown(*args)
  __chown("chown", args)
end
cp(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 306
def cp(*args, to: nil)
  __cp('cp', args, to: to, overwrite: false)
end
cp!(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 310
def cp!(*args, to: nil)
  __cp('cp!', args, to: to, overwrite: true)
end
echo(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 53
def echo(*args)
  __echo('echo', args)
end
echoback(cmd) click to toggle source
# File lib/benry/unixcmd.rb, line 39
def echoback(cmd)
  #; [!x7atu] prints argument string into $stdout with prompt.
  puts "#{prompt!(@__depth ||= 0)}#{cmd}"
end
ln(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 634
def ln(*args, to: nil)
  __ln('ln', args, to: to, overwrite: false)
end
ln!(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 638
def ln!(*args, to: nil)
  __ln('ln!', args, to: to, overwrite: true)
end
mkdir(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 529
def mkdir(*args)
  __mkdir('mkdir', args)
end
mv(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 432
def mv(*args, to: nil)
  __mv('mv', args, to: to, overwrite: false)
end
mv!(*args, to: nil) click to toggle source
# File lib/benry/unixcmd.rb, line 436
def mv!(*args, to: nil)
  __mv('mv!', args, to: to, overwrite: true)
end
prompt() click to toggle source
# File lib/benry/unixcmd.rb, line 29
def prompt()
  #; [!uilyk] returns prompt string.
  return "$"
end
prompt!(depth) click to toggle source
# File lib/benry/unixcmd.rb, line 34
def prompt!(depth)
  #; [!q992e] adds indentation after prompt.
  return prompt() + ' ' * (depth+1)
end
pushd(arg) { || ... } click to toggle source
# File lib/benry/unixcmd.rb, line 190
def pushd(arg, &b)
  cmd = 'pushd'
  #; [!xl6lg] raises error when block not given.
  block_given?()  or
    raise ArgumentError, "pushd: requires block argument."
  #; [!nvkha] expands file pattern.
  #; [!q3itn] error when pattern not matched to any file.
  #; [!hveaj] error when pattern matched to multiple files.
  #; [!y6cq9] error when argument is not a directory name.
  dir = __glob_onedir(cmd, arg)
  #; [!7ksfd] replaces home path with '~'.
  here = Dir.pwd
  home = File.expand_path("~")
  here2 = here.start_with?(home) ? here.sub(home, "~") : here
  #; [!rxtd0] changes directory and yields block.
  echoback("pushd #{dir}") if __echoback?()
  @__depth ||= 0
  @__depth += 1
  Dir.chdir(dir)
  yield
  @__depth -= 1
  #; [!9jszw] back to origin directory after yielding block.
  echoback("popd    # back to #{here2}") if __echoback?()
  Dir.chdir(here)
  here
end
pwd() click to toggle source
# File lib/benry/unixcmd.rb, line 755
def pwd()
  #; [!aelx6] echoback command and arguments.
  echoback("pwd") if __echoback?()
  #; [!kh3l2] prints current directory path.
  puts Dir.pwd
end
rm(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 496
def rm(*args)
  __rm('rm', args)
end
rmdir(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 593
def rmdir(*args)
  __rmdir('rmdir', args)
end
ruby(*args, &b) click to toggle source
# File lib/benry/unixcmd.rb, line 98
def ruby(*args, &b)
  __ruby('ruby', args, false, &b)
end
ruby!(*args, &b) click to toggle source
# File lib/benry/unixcmd.rb, line 102
def ruby!(*args, &b)
  __ruby('ruby!', args, true, &b)
end
store(*args, to:) click to toggle source
# File lib/benry/unixcmd.rb, line 982
def store(*args, to:)
  __store('store', args, false, to: to)
end
store!(*args, to:) click to toggle source
# File lib/benry/unixcmd.rb, line 986
def store!(*args, to:)
  __store('store!', args, true, to: to)
end
sys(*args, &b) click to toggle source
# File lib/benry/unixcmd.rb, line 68
def sys(*args, &b)
  __sys('sh', args, false, &b)
end
sys!(*args, &b) click to toggle source
# File lib/benry/unixcmd.rb, line 72
def sys!(*args, &b)
  __sys('sh!', args, true, &b)
end
time(format=nil) { || ... } click to toggle source
# File lib/benry/unixcmd.rb, line 1210
def time(format=nil, &b)
  #; [!ddl3a] measures elapsed time of block and reports into stderr.
  pt1 = Process.times()
  t1  = Time.new
  yield
  t2  = Time.new
  pt2 = Process.times()
  user = pt2.cutime - pt1.cutime
  sys  = pt2.cstime - pt1.cstime
  real = t2 - t1
  format ||= "        %.3fs real       %.3fs user       %.3fs sys"
  $stderr.puts ""
  $stderr.puts format % [real, user, sys]
end
touch(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 763
def touch(*args)
  __touch('touch', *args)
end
unzip(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 1123
def unzip(*args)
  __unzip('unzip', args, false)
end
unzip!(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 1127
def unzip!(*args)
  __unzip('unzip!', args, true)
end
zip(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 1042
def zip(*args)
  __zip('zip', args, false)
end
zip!(*args) click to toggle source
# File lib/benry/unixcmd.rb, line 1046
def zip!(*args)
  __zip('zip', args, true)
end