├── LICENSE ├── README.md └── rb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 redka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rb 2 | 3 | With 9 lines of Ruby replace most of the command line tools that you use to process text inside of the terminal. 4 | 5 | 6 | 7 | Here's the code 8 | 9 | ```ruby 10 | #!/usr/bin/env ruby 11 | File.join(Dir.home, '.rbrc').tap { |f| load f if File.exist?(f) } 12 | 13 | def execute(_, code) 14 | puts _.instance_eval(&code) 15 | rescue Errno::EPIPE 16 | exit 17 | end 18 | 19 | single_line = ARGV.delete('-l') 20 | code = eval("Proc.new { #{ARGV.join(' ')} }") 21 | single_line ? STDIN.each { |l| execute(l.chomp, code) } : execute(STDIN.each_line, code) 22 | ``` 23 | 24 | Clone this repo and copy the `rb` file to somewhere in your path (or just copy and paste the above). 25 | 26 | With this you can use ruby as a command line utility much more ergonomically than invoking it the standard way. 27 | 28 | There's only one switch `-l` which runs your code on each line separately. Otherwise you get an Enumerator that returns the lines of stdin. It's `instance_eval`ed so some methods need `self` to work, eg. `self[-1]` 29 | 30 | ## Install 31 | 32 | Just paste this line into your terminal to install `rb`: 33 | ``` 34 | sudo curl https://raw.githubusercontent.com/thisredone/rb/master/rb -o /usr/local/bin/rb && sudo chmod +x /usr/local/bin/rb 35 | ``` 36 | 37 | 38 | ### Examples 39 | 40 | ###### Extract docker images from running containers 41 | 42 | ```bash 43 | > docker ps | rb drop 1 | rb -l split[1] 44 | 45 | # ubuntu 46 | # postgres 47 | ``` 48 | 49 | 50 | 51 | ###### Display how much time ago containers have exited 52 | 53 | ```shell 54 | > docker ps -a | rb grep /Exited/ | rb -l 'split.last.ljust(20) + " => " + split(/ {2,}/)[-2]' 55 | 56 | # angry_hamilton => Exited (0) 18 hours ago 57 | # dreamy_lamport => Exited (0) 3 days ago 58 | # prickly_hypatia => Exited (0) 2 weeks ago 59 | ``` 60 | 61 | 62 | 63 | ###### Sort `df -h` output by `Use%` 64 | 65 | ```shell 66 | > df -h | rb 'drop(1).sort_by { |l| l.split[-2].to_f }' 67 | 68 | # udev 3,9G 0 3,9G 0% /dev 69 | # tmpfs 3,9G 0 3,9G 0% /sys/fs/cgroup 70 | # /dev/sda1 511M 3,4M 508M 1% /boot/efi 71 | # /dev/sda2 237M 85M 140M 38% /boot 72 | 73 | # or leave the header if you want 74 | > df -h | rb '[first].concat drop(1).sort_by { |l| l.split[-2].to_f }' 75 | 76 | # Filesystem Size Used Avail Use% Mounted on 77 | # udev 3,9G 0 3,9G 0% /dev 78 | # tmpfs 3,9G 0 3,9G 0% /sys/fs/cgroup 79 | # /dev/sda1 511M 3,4M 508M 1% /boot/efi 80 | # /dev/sda2 237M 85M 140M 38% /boot 81 | ``` 82 | 83 | 84 | 85 | ###### Count files by their extension 86 | 87 | ```shell 88 | > find . -type f | rb 'group_by(&File.method(:extname)).map { |ext, o| "#{ext.chomp}: #{o.size}" }' 89 | 90 | # : 3 91 | # .rb: 19 92 | # .md: 1 93 | ``` 94 | 95 | 96 | 97 | ###### This problem http://vegardstikbakke.com/unix/ 98 | 99 | ```ruby 100 | ls | rb 'group_by { |x| x[/\d+/] }.select { |_, y| y.one? }.keys' 101 | ``` 102 | 103 | 104 | ## Extending rb 105 | 106 | The `~/.rbrc` file is loaded if it's available. Anything defined in there will be available inside `rb` scripts. 107 | 108 | ```ruby 109 | # ~/.rbrc 110 | 111 | class String 112 | def black; "\033[30m#{self}\033[0m" end 113 | def red; "\033[31m#{self}\033[0m" end 114 | end 115 | ``` 116 | 117 | 118 | ```shell 119 | > ls | rb first.red 120 | ``` 121 | 122 | ## Alternatives 123 | 124 | For a more portable solution, note you can run `ruby` with the `-e` flag. 125 | 126 | [`$<`](https://ruby-doc.org/3.2.1/globals_rdoc.html#:~:text=%24%3C,as%20ARGF.) is a two-character global equivalent to [`ARGF`](https://ruby-doc.org/3.2.1/ARGF.html). `ARGF` gives you access to the standard input and includes `Enumerable`. 127 | 128 | ```shell 129 | > docker ps | ruby -e '$<.drop(1).each{puts _1.split[1]}' 130 | 131 | # ubuntu 132 | # postgres 133 | ``` 134 | 135 | ```shell 136 | > docker ps -a | ruby -e '$<.grep(/Exited/).each{puts _1.split.last.ljust(20) + " => " + _1.split(/ {2,}/)[-2]}' 137 | 138 | # angry_hamilton => Exited (0) 18 hours ago 139 | # dreamy_lamport => Exited (0) 3 days ago 140 | # prickly_hypatia => Exited (0) 2 weeks ago 141 | ``` 142 | -------------------------------------------------------------------------------- /rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | File.join(Dir.home, '.rbrc').tap { |f| load f if File.exist?(f) } 3 | 4 | def execute(_, code) 5 | puts _.instance_eval(&code) 6 | rescue Errno::EPIPE 7 | exit 8 | end 9 | 10 | single_line = ARGV.delete('-l') 11 | code = eval("Proc.new { #{ARGV.join(' ')} }") 12 | single_line ? STDIN.each { |l| execute(l.chomp, code) } : execute(STDIN.each_line, code) 13 | --------------------------------------------------------------------------------