benry-unixcmd Gem README
($Release: 0.9.0 $)
benry-unixcmd gem implements popular UNIX commands, like FileUtils.
Features compared to FileUtils:
- supports file patterns (
*,.,{}) directly. - provides
cp :r,mv :p,rm :rf, ... instead ofcp_r,mv_p,rm_rf, ... - prints command prompt
$before command echoback. - provides
pushdwhich is similar tocdbut supports nested calls naturally. - implements
capture2,capture2e, andcapture3which callsPopen3.capture2,Popen3.capture2, andPopen3.capture3respectively. - supports
touch -r reffile. - provides
syscommand which is similar toshin Rake but different in details. - provides
zipandunzipcommands (requiresrubyzipgem). - provides
storecommand which copies files recursively into target directory, keeping file path. - provides
atomic_symlink!command which switches symlink atomically.
(benry-unixcmd gem requires Ruby >= 2.3)
Table of Contents
Install
$ gem install benry-unixcmd
File: ex1.rb
require 'benry/unixcmd' # !!!!!
include Benry::UnixCommand # !!!!!
output = capture2 "ls -al" # run command and return output
#print output
Result:
[localhost] ruby ex1.rb
$ ls -al
Command Reference
echo
File: ex-echo1.rb
require 'benry/unixcmd'
include Benry::UnixCommand
echo "aa", "bb", "cc"
echo :n, "aa" # not print "\n"
echo "bb"
Result:
[localhost]$ ruby ex_echo1.rb
$ echo aa bb cc
aa bb cc
$ echo -n aa
aa$ echo bb
bb
Options:
echo :n-- don't print "\n".
echoback
echoback "command"prints$ commandstring into stdout.echoback "command"indents command if in block ofcdorpushd.
File: ex-echoback1.rb
require 'benry/unixcmd'
include Benry::UnixCommand
echoback "command 123"
cd "dir1" do
echoback "command 456"
cd "dir2" do
echoback "command 789"
end
end
Result:
[localhost]$ ruby ex_echoback1.rb
$ command 123
$ cd dir1
$ command 456
$ cd dir2
$ command 789
$ cd -
$ cd -
cp
cp "x", "y"copiesxto new filey'. Fails wheny` already exists.cp! "x", "y"is similar to above, but overwritesyeven if it exists.cp "x", "y", to: "dir"copiesxandyintodir.cp "x", "y", "dir"will be error! (useto: "dir"instead.)- Glob pattern such as
*,**,?, and{}are available. - (See FAQ about
to:keyword option.)
require 'benry/unixcmd'
include Benry::UnixCommand
## copy file to newfile
cp "file1.txt", "newfile.txt" # error if newfile.txt already exists.
cp! "file1.txt", "newfile.txt" # overrides newfile.txt if exists.
## copy dir to newdir recursively
cp :r, "dir1", "newdir" # error if newdir already exists.
## copy files to existing directory
cp :pr, "file*.txt", "lib/**/*.rb", to: "dir1" # error if dir1 not exist.
Options:
cp :p-- preserves timestamps and permission.cp :r-- copies files and directories recursively.cp :l-- creates hard links instead of copying files.cp :f-- ignores non-existing source files. Notice that this is different fromcp -fof unix command.
mv
mv "x", "y"renamesxtoy. Fails whenyalready exists.mv! "x", "y"is similar to above, but overwritesyeven if it exists.mv "x", "y", to: "dir"movesxandyintodir.mv "x", "y", "dir"will be error! (useto: "dir"instead.)- Glob patten such as
*,**,?, and{}are available. - (See FAQ about
to:keyword option.)
require 'benry/unixcmd'
include Benry::UnixCommand
## rename file
mv "file1.txt", "newfile.txt" # error if newfile.txt already exists.
mv! "file1.txt", "newfile.txt" # overrides newfile.txt if exists.
## rename directory
mv "dir1", "newdir" # error if newdir already exists.
## move files and directories to existing directory
mv "file*.txt", "lib", to: "dir1" # error if dir1 not exist.
## ignore non-existing files.
mv "foo*.txt", to: "dir1" # error if foo*.txt not exist.
mv :f, "foo*.txt", to: "dir1" # not error even if foo*.txt not exist.
Options:
mv :f-- ignores non-existing source files.
rm
rm "x", "y"removes filexandy.rm :r, "dir1"removes directory recursively.rm "dir1"will raise error because:roption not specified.rm "foo*.txt"will raise error iffoo*.txtnot exists.rm :f, "foo*.txt"will not raise error even iffoo*.txtnot exists.- Glob patten such as
*,**,?, and{}are available.
require 'benry/unixcmd'
include Benry::UnixCommand
## remove files
rm "foo*.txt", "bar*.txt" # error if files not exist.
rm :f, "foo*.txt", "bar*.txt" # not error even if files not exist.
## remove directory
rm :r, "dir1" # error if dir1 not exist.
rm :rf, "dir1" # not error even if dir1 not exist.
Options:
rm :r-- remove files and directories recursively.rm :f-- ignores non-existing files and directories.
mkdir
mkdir "x", "y"createsxandydirectories.mkdir :p, "x/y/z"createsx/y/zdirectory.mkdir "x"will be error ifxalready exists.mkdir :p, "x"will not be error even ifxalready exists.mkdir :m, 0775, "x"creates new directory with permission 0775.
require 'benry/unixcmd'
include Benry::UnixCommand
## creates new directory
mkdir "newdir"
## creates new directory with path
mkdir :p, "dir/x/y/z"
## creats new directory with specific permission
mkdir :m, 0755, "newdir"
Options:
mkdir :p-- creates intermediate path.mkdir :m, 0XXX-- specifies directory permission.
rmdir
rmdir "x", "y"removed empty directores.- Raises error when directory not empty.
require 'benry/unixcmd'
include Benry::UnixCommand
## remove empty directory
rmdir "dir" # error if directory not empty.
Options:
- (no options)
ln
ln "x", "y"creates hard link.ln :s, "x", "y"creates symbolic link. Error ifyalready exists.ln! :s, "x", "y"overwrites existing symbolic linky.ln "files*.txt', to: "dir"creates hard links intodir.ln "files*.txt', "dir"will be error! (useto: "dir"instead.)- (See FAQ about
to:keyword option.)
require 'benry/unixcmd'
include Benry::UnixCommand
## create hard link
ln "foo1.txt", "dir/foo1.txt"
## create symbolic link
ln :s, "foo1.txt", "dir/foo1.txt" # error if dir/foo1.txt alreay exists.
ln! :s, "foo1.txt", "dir/foo1.txt" # overwrites dir/foo1.txt if exists.
## create symbolic link into directory.
ln :s, "foo1.txt", to: "dir"
## error! use `to: "dir"` instead.
ln :s, "foo1.txt", "dir"
atomic_symlink!
atomic_symlink! "x", "y"creates symbolic link atomically.
require 'benry/unixcmd'
include Benry::UnixCommand
## create symbolic link atomically
atomic_symlink! "src-20200101", "src"
## the above is same as the following
tmplink = "src.#{rand().to_s[2..6]}" # random name
File.symlink("src-20200101", tmplink) # create symblink with random name
File.rename(tmplink, "src") # rename symlink atomically
Options:
- (no options)
touch
touch "x"updates timestamp of file.touch :r, "reffile", "x"uses timestamp ofreffileinstead current timestamp.
require 'benry/unixcmd'
include Benry::UnixCommand
## updates timestamp of files to current timestamp.
touch "files*.txt"
## copy timestamp from reffile to other files.
touch :r, "reffile", "files*.txt"
Options:
touch :a-- updates only access time.touch :m-- updates only modification time.touch :r, "reffile"-- uses timestamp ofreffileinstead of current timestamp.
chmod
chmod 0644, "x"changes file permission.chmod :R, "a+r", "dir"changes permissions recursively.- Permission can be
0644sytle, oru+wstyle.
require 'benry/unixcmd'
include Benry::UnixCommand
## change permissions of files.
chmod 0644, "file*.txt"
chmod "a+r", "file*.txt"
## change permissions recursively.
chmod :R, 0644, "dir"
chmod :R, "a+r", "dir"
Optionns:
chmod :R-- changes permissions recursively.
chown
chown "user:group", "x", "y"changes owner and group of files.chown "user", "x", "y"changes owner of files.chown ":group", "x", "y"changes group of files.chown :R, "user:group", "dir"changes owner and group recursively.
require 'benry/unixcmd'
include Benry::UnixCommand
## change owner and/or group.
chown "user1:group1", "file*.txt" # change both owner and group
chown "user1", "file*.txt" # change owner
chown ":group1", "file*.txt" # change group
Optionns:
chown :R-- changes owner and/or group recursively.
pwd
pwd()prints current working directory path.
require 'benry/unixcmd'
include Benry::UnixCommand
## prints current working directory
pwd()
Options:
- (no options)
store
store "x", "y", to: "dir",copies files underxandytodirkeeping file path. For example,x/foo/bar.rbwill be copied asdir/x/foo/bar.rb.store!overwrites existing files whilestoredoesn't.
require 'benry/unixcmd'
include Benry::UnixCommand
## copies files into builddir, keeping file path
store "lib/**/*.rb", "test/**/*.rb", to: "builddir"
Options:
store :p-- preserves timestamps, permission, file owner and group.store :l-- creates hard link instead of copying file.store :f-- ignores non-existing files.
sys
sys "ls -al"runsls -alcommand.sysraises error when command failed.sys!ignores error even when command failed.sysandsys!returnProcess::Statusobject regardless of command result.sysandsys!can take a block argument as error handler called only when command failed. If result of block argument is truthy, error will not be raised.
require 'benry/unixcmd'
include Benry::UnixCommand
## run `ls` command
sys "ls foo.txt" # may raise error when command failed
sys! "ls foo.txt" # ignore error even when command filed
## error handling
sys "ls /fooobarr" do |stat| # block called only when command failed
p stats.class #=> Process::Status
p stat.exitstatus #=> 1 (non-zero)
true # suppress raising error
end
Options:
- (no options)
ruby
ruby "...."is almost same assys "ruby ....".RbConfig.rubyis used as ruby command path.rubyraises error when ruby command failed.ruby!ignores error even when ruby command failed.
require 'benry/unixcmd'
include Benry::UnixCommand
## run ruby command
ruby "file1.rb" # raise error when ruby command failed
ruby! "file1.rb" # ignore error even when ruby command filed
Options:
- (no options)
capture2
capture2 "ls -al"runsls -aland returns output of the command.capture2 "cat -n", stdin_data: "A\nB\n"runcat -ncommand and uses"A\nB\n"as stdin data.caputre "ls foo"will raise error when command failed.caputre! "ls foo"ignores error even when command failed, and returns command output and process status object.capture2()invokesPopen3.capture2()internally. All keyword arguments are available.
require 'benry/unixcmd'
include Benry::UnixCommand
## run command and get output of the command.
output = capture2 "ls -l foo.txt" # error if command failed
output, process_status = capture2 "ls -l foot.xt" # ignore error even command failed
puts process_status.exitstatus
## run command with stdin data.
input = "AA\nBB\nCC\n"
output = capture2 "cat -n", stdin_data: input
Options:
capture2e
- almost same as
capture2, but output contains both stdout and stderr.
require 'benry/unixcmd'
include Benry::UnixCommand
## run command and get output of the command, including stderr.
output = capture2e "time ls -al"
output, process_status = capture2e! "time ls -al" # ignore error even command failed
puts process_status.exitstatus
Options:
capture3
- almost same as
capture3, but returns both stdout output and stderr output.
require 'benry/unixcmd'
include Benry::UnixCommand
## run command and get output of both stdout and stderr separately
output, error = capture3 "time ls -al"
output, error, process_status = capture3! "time ls -al" # ignore error even command failed
puts process_status.exitstatus
## run command with stdin data.
input = "AA\nBB\nCC\n"
output, error = capture3 "cat -n", stdin_data: input
Options:
zip
zip "foo.zip", "file1", "file2"creates new zip filefoo.zip.zip :r, "foo.zip", "dir1"adds files underdir1into zip file recursively.zipwill be error if zip file already exists.zip!will overwrite existing zip file.zip :'0'doesn't compress files.zip :'1'compress files in best speed.zip :'9'compress files in best compression level.zipandzip!requiresrubyzipgem. You must install it by yourself.zipandzip!doesn't support absolute path.
require 'benry/unixcmd'
include Benry::UnixCommand
## create zip file
zip "foo.zip", "file*.txt" # requires 'rubyzip' gem
## create zip file, adding files under directory
zip :r, "foo.zip", "dir1"
## create high-compressed zip file
zip :r9, "foo.zip", "dir1"
Options:
zip :r-- adds files under directory into zip file recursively.zip :'0'-- not compress files.zip :'1'-- compress files in best speed.zip :'9'-- compress files in best compression level.
unzip
unzip "foo.zip"extracts files in zip file into current directory.unzip :d, "dir1", "foo.zip"extracts files underdir1. Diretorydir1should not exist or should be empty.unzip "foo.zip"will be error if extracting file already exists.unzip! "foo.zip"will overwrite existing files.unzip "foo.txt", "file1", "file2"extracts onlyfile1andfile2.zunipandunzip!requiresrubyzipgem. You must install it by yourself.unzipandunzip!doesn't support absolute path.
require 'benry/unixcmd'
include Benry::UnixCommand
## extracts zip file
unzip "foo.zip" # requires 'rubyzip' gem
## extracts files in zip file into the directory.
unzip :d, "dir1", "foo.zip" # 'dir1' should be empty, or should not exist
## overwrites existing files.
unzip! "foo.zip"
Options:
unzip :d, "dir1"-- extracts files into the directory.
time
time do ... endinvokes block and prints elapsed time into stderr.
File: ex-time1.rb
require 'benry/unixcmd'
include Benry::UnixCommand
time do
sh "zip -qr9 dir1.zip dir1"
end
Result:
[localhost]$ ruby ex-time1.rb
$ zip -qr9 dir1.zip dir1
1.511s real 1.501s user 0.006s sys
FAQ
Why mv or cp requires to: option?
Because UNIX command has bad interface which causes unexpected result.
For example, mv command of UNIX has two function: rename and move.
- rename:
mv foo bar(ifbaris a file or not exist) - move:
mv foo bar(if directorybaralready exists)
Obviously, rename function and move function are same form. This causes unexpected result easily due to, for example, typo.
### Assume that you want rename 'foo' file to 'bar'.
### But if 'bar' exists as directory, mv command moves 'foo' into 'bar'.
### In this case, mv command should be error.
$ mv foo bar
To avoid this unexpected result, mv() command of Benry::UnixCommand handles two functions in different forms.
- rename:
mv "foo", "bar"(error if directorybarexists) - move:
mv "foo", to: "bar"(error if 'bar' is a file or not exist)
In the same reason, cp() and ln() of Benry::UnixCommand also requires to: option.
How to use in Rakefile?
File: Rakefile
require 'benry/unixcmd' # !!!!!
include Benry::UnixCommand # !!!!!
Rake::DSL.prepend Benry::UnixCommand # !!!!!
task :example do
mkdir :p, "foo/bar/baz"
here = Dir.pwd()
pushd "foo/bar/baz" do
output = capture2 "pwd"
puts output.sub(here+"/", "")
end
end
Result:
[localhost]$ rake example
$ mkdir -p foo/bar/baz
$ pushd foo/bar/baz
$ pwd
foo/bar/baz
$ popd # back to /home/yourname
How to change prompt string?
File: ex-prompt1.rb
require 'benry/unixcmd'
include Benry::UnixCommand
def prompt() # !!!!!
"myname@localhost>" # !!!!!
end # !!!!!
sh "date"
Result:
[localhost]$ ruby ex-prompt1.rb
myname@localhost> date
Wed Jan 15 20:23:07 UTC 2021
How to make prompt colored?
require 'benry/unixcmd'
include Benry::UnixCommand
def prompt()
s = "myname@localhost>"
"\e[0;31m#{s}\e[0m" # red
#"\e[0;32m#{s}\e[0m" # green
#"\e[0;33m#{s}\e[0m" # yellow
#"\e[0;34m#{s}\e[0m" # blue
#"\e[0;35m#{s}\e[0m" # magenta
#"\e[0;36m#{s}\e[0m" # cyan
#"\e[0;37m#{s}\e[0m" # white
end
sh "date"
How to disable command echoback?
File: ex-quiet1.rb
require 'benry/unixcmd'
include Benry::UnixCommand
BENRY_ECHOBACK = false # !!!!!
sh "date"
Result:
$ ruby ex-quiet1.rb
Wed Jan 1 22:29:55 UTC 2020 # no echoback, only output
License and Copyright
$License: MIT License $
$Copyright: copyright(c) 2021 kuwata-lab.com all rights reserved $