Gr8

a great utility brings Ruby power to your command-line.

Souce Code RubyGems.org View on GitHub

About Gr8

Gr8 is a great tool which brings Ruby power (such as map, select, inject, grep, min, max, and so on) to your command-line.

(Release: $Release: 0.1.1 $)

Installation

$ gem install gr8

Or:

$ curl -o gr8 http://j.mp/Gr8_rb
$ chmod a+x gr8
$ sudo mv gr8 /usr/local/bin

Gr8 requires Ruby (>= 2.0).

Usage

Usage: gr8 [options] ruby-code

Options:

  • -h, --help : show help
  • --doc : open document with browser
  • -v, --version : show version
  • -r lib[,lib2,...] : require libraries
  • -F[regexp] : separate each line with separator
  • -C N : select column (1-origin)

Example 1: Aggregation

Data file:

$ cat data
Haruhi  100
Mikuru   80
Yuki    120

Prints each line (gr8 command prints expression value automatically when non-nil):

$ cat data | gr8 '$stdin.lazy.map{|s| s }'
Haruhi  100
Mikuru   80
Yuki    120

$stdin.lazy is omissible because gr8 command uses it as current context (=self):

$ cat data | gr8 'map{|s| s }'
Haruhi  100
Mikuru   80
Yuki    120

Select second column:

$ cat data | gr8 'map{|s| s.split()[1] }'
100
80
120

map{|s|s.split()} can be map{split()} in gr8 command because gr extends map() and select() to set each item as self in block arguments of them:

$ cat data | gr8 'map{split()[1]}'
100
80
120

Calculates total of numbers:

$ cat data | gr8 'map{split()[1]}.map(&:to_i).inject(0,:+)'
300

sum() is a short-hand for inject(0,:+):

$ cat data | gr8 'map{split()[1]}.map(&:to_i).sum'
300

sum_i() is a short-hand for map(&:to_i).inject(0,:+):

$ cat data | gr8 'map{split()[1]}.sum_i'   # or 'sum_f' for float
300

Command-line opiton '-F' splits each line into array:

$ cat data | gr8 -F 'map{self.inspect}'
["Haruhi", "100"]
["Mikuru", "80"]
["Yuki", "120"]
$ cat data | gr8 -F 'map{self[1]}.sum_i'   # or 'map{|a|a[1]}.sum_i'
300

Command-line option '-C n' selects column (1-origin):

$ cat data | gr8 -C 2 'map{self}'
100
80
120
$ cat data | gr8 -C 2 'sum_i'
300

Calculates average of numbers instead of total:

$ cat data | gr8 -C 2 'map(&:to_i).avg'
300.0
$ cat data | gr8 -C 2 'avg_i'
300.0

Compared to ruby -ne:

$ cat data | ruby -ne 'BEGIN{t=0};t+=$_.split[1].to_i;END{p t}'
300
$ cat data | ruby -ane 'BEGIN{t=0};t+=$F[1].to_i;END{p t}'
300

Example 2: Generating Shell Commands

Assume that there are some image files:

$ ls
img1.jpg      img2.jpg      img3.jpg
img4.png      img5.png      img6.png

Select PNG files:

$ ls | gr8 'grep(/(.*)\.png$/)'
img1.png
img2.png
img3.png

Prints new filename replacing '.png' with '.jpg':

$ ls | gr8 'grep(/(.*)\.png/) { "#{$1}.jpg" }'
img1.jpg
img2.jpg
img3.jpg

Prints OS command to convert PNG file into JPG:

$ ls | gr8 'grep(/(.*)\.png/) { "convert #{$1}.png #{$1}.jpg" }'
convert img1.png img1.jpg
convert img2.png img2.jpg
convert img3.png img3.jpg

You may want quotes file name with single quotation:

$ ls | gr8 'grep(/(.*)\.png/) { "convert #{$1.q}.png #{$1.q}.jpg" }'
convert 'img1'.png 'img1'.jpg
convert 'img2'.png 'img2'.jpg
convert 'img3'.png 'img3'.jpg

Or double quotation:

$ ls | gr8 'grep(/(.*)\.png/) { "convert #{$1.qq}.png #{$1.qq}.jpg" }'
convert "img1".png "img1".jpg
convert "img2".png "img2".jpg
convert "img3".png "img3".jpg

Run os commands after you confirmed them:

$ ls | gr8 'grep(/(.*)\.png/){"convert #{$1.q}.png #{$1.q}.jpg"}' | sh

Example 3: File Manipulation

Kernel#fu() is a short-hand which returns FileUtil module. Using it, You can rename or move files very easily.

Assume that there are several PNG files:

$ ls | gr8 'grep(/^a(\d+)/)'
a1.png
a2.png
a3.png

And you want to rename them to other names:

$ ls | gr8 'grep(/^a(\d+)/) { "b#{$1.to_i+100}.png" }'
b101.png
b102.png
b103.png

fu.mv is a short-hand to require "fileutils"; FileUtils.mv:

$ ls | gr8 'grep(/^a(\d+)/){fu.mv "a#{$1}.png", "b#{$1.to_i+100}.png"}'
$ ls b*.png
b101.png   b102.png   b103.png     # renamed from 'a1.png', 'a2.png' and 'a3.png'

(Experimental)

gr8 provides more convenient methods to mapipulate files:

$ ls | gr8 'copy_as    { sub(/\.htm$/, ".html") }'
$ ls | gr8 'copy_as!   { sub(/\.htm$/, ".html") }'  # overwrite existing file
$ ls | gr8 'rename_as  { sub(/\.htm$/, ".html") }'
$ ls | gr8 'rename_as! { sub(/\.htm$/, ".html") }'  # overwrite existing file
$ ls | gr8 'copy_to  { "some/where/directory/" }'
$ ls | gr8 'copy_to! { "some/where/directory/" }'   # overwrite existing file
$ ls | gr8 'move_to  { "some/where/directory/" }'
$ ls | gr8 'move_to! { "some/where/directory/" }'   # overwrite existing file

References

Kernel#fu()

Returns FileUtils class object.

Example:

$ ls | gr8 'grep(/(.*)\.png/){fu.mv "#{$1}.png", "#{$1}.jpg"}'

String#q(), #qq()

q() quotes string with single-quotation, with escaping singile-quotation with backslash.

qq() quotes string with double-quotation, with escaping singile-quotation with backslash.

These are convenient when file name contains spaces.

Example:

$ echo 'Image 1.png' | gr8 'grep(/(.*)\.png/){"convert #{$1.q}.png #{$1.q}.jpg"}'
convert 'Image 1'.png 'Image 1'.jpg
$ ls | gr8 'grep(/(.*)\.png/){"convert #{$1.qq}.png #{$1.qq}.jpg"}'
convert "Image 1".png "Image 1".jpg

Enumerable#transform(){...}, #xf(){...}

Similar to map(), but it sets each item as self in block argument.

Example:

$ ls *.png | gr8 'xf{self}'
A.png
B.png
C.png
$ ls *.png | gr8 'xf{sub(/\.png/, '.jpg')}'
A.jpg
B.jpg
C.jpg

Source code:

def transform(&block)
  collect {|x| x.instance_exec(x, &block) }
end
alias xf transform

Enumerable#map(){...}

Extended to set each item as self in block argument of map(). If you want original map(), use collect() instead.

Example:

$ ls *.png | gr8 'map{self}'
A.png
B.png
C.png
$ ls *.png | gr8 'map{sub(/\.png/, '.jpg')}'
A.jpg
B.jpg
C.jpg

Source code:

alias __map map
def map(&block)
  __map {|x| x.instance_exec(x, &block) }
end

Enumerable#select(){...}

Extended to set each item as self in block argument of select(). If you want original select(), use find_all() instead.

Example:

$ ls *.png | gr8 'select{self}'
A.png
B.png
C.png
$ ls *.png | gr8 'select{start_with?("B")}'
B.jpg

Source code:

alias __select select def select(&block) __select {|x| x.instance_exec(x, &block) } end

Enumerable#sum()

Same as inject(0, :+).

Example:

$ cat file
10.5
20.5
30.5
$ cat file | gr8 'map(&:to_f).sum'
61.5

Enumerable#sum_i(), #sum_f()

Same as map(&:to_i).inject(0, :+) or map(&:to_f).inject(0, :+)

Example:

$ cat file
10.5
20.5
30.5
$ cat file | gr8 'sum_i'
60
$ cat file | gr8 'sum_f'
61.5

Enumerable#avg()

Returns average of numbers.

Example:

$ cat file
10.1
20.2
30.3
$ cat data |gr8 'map(&:to_i).avg'
20.0

Enumerable#avg_i(), #avg_f()

Same as map(&:to_i).avg or map(&:to_f).avg.

Example:

$ cat file
10.1
20.2
30.3
$ cat data |gr8 'avg_i'
20.0
$ cat data |gr8 'avg_f'
20.2

Enumerable#sed(pattern, replacing), #sed(pattern){...}

Replaces the first pattern in each line with replacing string or block. Internally, sed() calls String#sub().

Example:

$ ls *.png
A.png
B.png
C.png
$ ls *.png | gr8 'sed(/png/, "jpg")'
A.jpg
B.jpg
C.jpg

Source code:

def sed(pat, str=nil, &block)
  if block_given?
    collect {|s| s.sub(pat, &block) }
  else
    collect {|s| s.sub(pat, str) }
  end
end

Enumerable#gsed(pattern, replacing), #gsed(pattern){...}

Replaces all of pattern in each line with replacing string or block. Internally, gsed() calls String#gsub().

Example:

$ ls *.png
A1-1.png
A1-2.png
A1-3.png
$ ls *.png | gr8 'sed(/\d+/, "00\\&")'
A001-1.png
A001-2.png
A001-3.png
$ ls *.png | gr8 'gsed(/\d+/, "00\\&")'
A001-001.png
A001-002.png
A001-003.png

Source code:

def gsed(pat, str=nil, &block)
  if block_given?
    collect {|s| s.gsub(pat, &block) }
  else
    collect {|s| s.gsub(pat, str) }
  end
end

Enumerable#paths(), #paths{...}

Converts each item into Pathname object. Library pathname will be loaded automatically.

Example:

$ /bin/ls | gr8 'paths{|x| "#{x}: #{x.ftype}"}'
MIT-LICENSE: file
README.txt: file
Rakefile: file
bin: directory
lib: directory
test: directory

Source code:

def paths(&block)
  require "pathname" unless defined?(Pathname)
  if block_given?
    collect {|s| x = Pathname(s); x.instance_exec(x, &block) }
  else
    collect {|s| Pathname(s) }
  end
end

Enumerable#edit(verbose=true, encoding='utf-8'){|content, filepath|...}

Replace file content with result of block argument.

Example:

$ ls *.rb | gr8 'edit{|s|
  s = s.gsub(/Release: \d+\.\d+\.\d+/, "Release: 1.2.3")
  s = s.gsub(/Copyright: \d+-\d+/, "Copyright: 2013-2015")
  s }'

Enumerable#edit_i(suffix, verbose=true, encoding='utf-8'){|content, filepath|...}

Copy backup file with suffix before editing file.

Example:

$ ls *.rb
hom.rb    mad.rb
$ ls *.rb | gr8 'edit_i(".bkup"){|s|
  s = s.gsub(/Release: \d+\.\d+\.\d+/, "Release: 1.2.3")
  s = s.gsub(/Copyright: \d+-\d+/, "Copyright: 2013-2015")
  s }'
$ ls *.bkup
hom.rb    hom.rb.bkup     mad.rb     mad.rb.bkup

Enumerable#copy_to{...}, #copy_to!{...}

(Experimental)

Copy files into destination directory, without renaming basename.

  • Block argument should return destination directory name.
  • copy_to() skips when destination file already exists.
  • copy_to!() overwrites when destination file already exists.
  • Both skips copying when destination directory doesn't exist.

Enumerable#mkdir_and_copy_to{...}, #mkdir_and_copy_to!{...}

(Experimental)

Similar to copy_to() or copy_to!() except creating destination directory when not exist.

Enumerable#move_to{...}, #move_to!{...}

(Experimental)

Move files into destination directory, without renaming basename.

  • Block argument should return destination directory name.
  • move_to() skips when destination file already exists.
  • move_to!() overwrites when destination file already exists.
  • Both skips moving files when destination directory doesn't exist.

Enumerable#mkdir_and_move_to{...}, #mkdir_and_move_to!{...}

(Experimental)

Similar to move_to() or move_to!() except creating destination directory when not exist.

Enumerable#copy_as{...}, #copy_as!{...}

(Experimental)

Copy files into destination directory, with renaming basename.

  • Block argument should return destination file name.
  • copy_as() skips when destination file already exists.
  • copy_as!() overwrites when destination file already exists.
  • Both skips copying when destination directory doesn't exist.

Enumerable#mkdir_and_copy_as{...}, #mkdir_and_copy_as!{...}

(Experimental)

Similar to copy_as() or copy_as!() except creating destination directory when not exist.

Enumerable#rename_as{...}, #rename_as!{...}

(Experimental)

Move files into destination directory, with renaming basename.

  • Block argument should return destination file name.
  • rename_as() skips when destination file already exists.
  • rename_as!() overwrites when destination file already exists.
  • Both skips moving when destination directory doesn't exist.

Enumerable#mkdir_and_rename_as{...}, #mkdir_and_rename_as!{...}

(Experimental)

Similar to rename_as() or rename_as!() except creating destination directory when not exist.

License and Copyright

$License: MIT License $

$Copyright: copyright(c) 2015 kuwata-lab.com all rights reserved $