Ru ===== Ruby in your shell! [](http://travis-ci.org/tombenner/ru) Overview -------- Ru brings Ruby's expressiveness, cleanliness, and readability to the command line. It lets you avoid looking up pesky options in man pages and Googling how to write a transformation in bash that would take you approximately 1s to write in Ruby. For example, to center a file's lines, use [String#center](http://ruby-doc.org/core-2.0/String.html#method-i-center): ```bash ru 'map(:center, 80)' myfile ``` Using traditional tools, this isn't as easy or readable: ```bash awk 'printf "%" int(40+length($0)/2) "s\n", $0' myfile ``` For another example, let's compare summing the lines of a list of integers using Ru vs. a traditional approach: ```bash ru 'map(:to_i).sum' myfile ``` ```bash awk '{s+=$1} END {print s}' myfile ``` Any method from Ruby Core and Active Support can be used. Ru also provides new methods (and modifies [#map](#map)) to make transformations easier. Here are some variations on the above example: ```bash ru 'map(:to_i, 10).sum' myfile ru 'map(:to_i).reduce(&:+)' myfile ru 'each_line.to_i.to_a.sum' myfile ru 'grep(/^\d+$/).map(:to_i).sum' myfile ru 'map { |n| n.to_i }.reduce(&:+)' myfile ru 'reduce(0) { |sum, n| sum + n.to_i }' myfile ru 'each_line.match(/(\d+)/)[1].to_i.to_a.sum' myfile ru 'map { |n| n.to_i }.reduce(0) { |sum, n| sum + n }' myfile ``` See [Examples](#examples) and [Methods](#methods) for more. Installation ------------ ```bash gem install ru2 ``` You can now use Ruby in your shell! For example, to sum a list of integers: ```bash $ printf "2\n3" | ru 'map(:to_i).sum' 5 ``` Usage ----- See [Examples](#examples) below, too! Ru reads from stdin: ```bash $ printf "2\n3" | ru 'map(:to_i).sum' 5 $ cat myfile | ru 'map(:to_i).sum' 5 ``` Or from file(s): ```bash $ ru 'map(:to_i).sum' myfile 5 $ ru 'map(:to_i).sum' myfile myfile 10 ``` You can also run Ruby code without any input by prepending a `=`: ```bash $ ru '=2 + 3' 5 ``` The code argument is run as if it has `$stdin.each_line.map(&:chomp).` prepended to it. The result is converted to a string and printed. So, if you run `ru 'map(&:to_i).sum'`, you can think of it as running `puts $stdin.each_line.map(&:chomp).map(&:to_i).sum`. In addition to the methods provided by Ruby Core and Active Support, Ru provides other methods for performing transformations, like `each_line`, `files`, and `grep`, and it improves `map`. See [Methods](#methods) for more. Stream mode ----------- If input data is very large or of undefined size it may be better to process it line by line without loading the whole data into memory. This can be done by activating stream mode (which utilizes Enumerator::Lazy) by passing `-s` or `--stream` flag. Note, that in stream mode Ru can process only one file or input stream at a time. For example, let's count how many lines there are in the /dev/urandom :) ```bash $ cat /dev/urandom | ru -s 'inject(0){|a| puts a if a % 100000 == 0; a+1 }' ``` Or how many zeros there are in the /dev/zero :) Note, that there are no lines in the stream, so we have to active binary mode by passing `-b` or `--binary` flag. ```bash $ cat /dev/zero | ru -s -b 'inject(0){|a| puts a if a % 10000000 == 0; a+1 }' ``` As you can see, this allows to read stream or file byte by byte. ```bash $ echo 'test' > /tmp/test && ru -b 'join(" ")' /tmp/test 116 101 115 116 10 ``` Note, that memory consumption is constant no matter how long those commands are running. You can interrupt them with Ctrl+C. Examples -------- Let's compare the readability and conciseness of Ru relative to existing tools: #### Center lines ##### ru ```bash ru 'map(:center, 80)' myfile ``` ##### awk ```bash awk 'printf "%" int(40+length($0)/2) "s\n", $0' myfile ``` ##### sed [Script](https://www.gnu.org/software/sed/manual/sed.html#Centering-lines) #### Sum a list of integers ##### ru ```bash ru 'map(:to_i).sum' myfile ``` ##### awk ```bash awk '{s+=$1} END {print s}' myfile ``` ##### paste ```bash paste -s -d+ myfile | bc ``` #### Print the 5th line ##### ru ```bash ru '[4]' myfile ``` ##### sed ```bash sed '5q;d' myfile ``` #### Print all lines except the first and last ##### ru ```bash ru '[1..-2]' myfile ``` ##### sed ```bash sed '1d;$d' myfile ``` #### Sort an Apache access log by response time (decreasing, with time prepended) ##### ru ```bash ru 'map { |line| [line[/(\d+)( ".+"){2}$/, 1].to_i, line] }.sort.reverse.map(:join, " ")' access.log ``` ##### awk ```bash awk --re-interval '{ match($0, /(([^[:space:]]+|\[[^\]]+\]|"[^"]+")[[:space:]]+){7}/, m); print m[2], $0 }' access.log | sort -nk 1 ``` [Source](https://coderwall.com/p/ueazhw) Methods ------- In addition to the methods provided by Ruby Core and Active Support, Ru provides other methods for performing transformations. #### each_line Provides a shorthand for calling methods on each iteration of the input. Best explained by example: ```bash ru 'each_line.strip.center(80)' myfile ``` If you'd like to transform it back into a list, call `to_a`: ```bash ru 'each_line.strip.to_a.map(:center, 80)' myfile ``` #### files Converts the lines to `Ru::File` objects (see Ru::File below). ```bash $ printf "foo.txt" | ru 'files.map(:updated_at).map(:strftime, ""%Y-%m-%d")' 2014-11-08 ``` #### format(format='l') Formats a list of `Ru::File`s. You'll typically call this after calling `files` to transform them into strings: ```bash $ ru 'files.format' 644 tom staff 3 2014-10-26 09:06 bar.txt 644 tom staff 11 2014-11-04 08:29 foo.txt ``` The default format, `'l'`, is shown above. It prints `[omode, owner, group, size, date, name]`. #### grep Selects lines which match the given regex. ```bash $ printf "john\npaul\ngeorge" | ru 'grep(/o[h|r]/)' john george ``` #### map This is the same as [Array#map](http://www.ruby-doc.org/core-2.0/Array.html#method-i-map), but it adds a new syntax that allows you to easily pass arguments to a method. For example: ```bash $ printf "john\npaul" | ru 'map(:[], 0)' j p $ printf "john\npaul" | ru 'map(:center, 8, ".")' ..john.. ..paul.. ``` Note that the examples above can also be performed with `each_line`: ```bash $ printf "john\npaul" | ru 'each_line[0]' $ printf "john\npaul" | ru 'each_line.center(8, ".")' ``` Ru::File ------------ The [`files`](#files) method returns an enumerable of `Ru::File`s, which are similar to Ruby Core's [`File`](http://ruby-doc.org/core-2.0/File.html). Each one has the following methods: * `basename` * `created_at` (alias for ctime) * `ctime` * `extname` * `format` (see the [`format`](#formatformatl) method above) * `ftype` * `gid` * `group` * `mode` * `mtime` * `name` (alias for basename) * `omode` * `owner` * `size` * `to_s` (alias for name) * `uid` * `updated_at` (alias for mtime) * `world_readable?` Options ------- #### -h, --help Print a help page. #### -v, --version Print the installed version of Ru. Testing ------- Ru is tested against Active Support 3, 4 and 5. If you'd like to submit a PR, please be sure to use [Appraisal](https://github.com/thoughtbot/appraisal) to test your changes in all contexts: ```bash appraisal rspec ``` License ------- Ru is released under the MIT License. Please see the MIT-LICENSE file for details.