Slide 1

Slide 1 text

Now For Something Completely Different Command-line Ruby Or “How You Can Learn Ruby Faster”

Slide 2

Slide 2 text

James Edward Gray II ✤ I am a regular panelist on The Ruby Rogues podcast ✤ I was a Rubyist before Rails shipped and made us popular ✤ I have written a lot of code and documentation for Ruby, including the standard CSV library

Slide 3

Slide 3 text

Ruby is Beautiful

Slide 4

Slide 4 text

I Suspect You’ve Been Learning… ✤ How clean code helps you manage complexity ✤ How well written code eases communication with others ✤ How to identify and improve problem spots in your code

Slide 5

Slide 5 text

That’s All True, and Usually Very Important

Slide 6

Slide 6 text

But Not Today!

Slide 7

Slide 7 text

This Is For Your Own Good! Ugly Ruby Strap In Folks, This Could Get Weird

Slide 8

Slide 8 text

“Why would you do this???”

Slide 9

Slide 9 text

What If… ✤ Our goal isn’t to write the best code possible? ✤ Our goal is to do a big chunk of work quickly? ✤ Our goal is to learn Ruby faster?

Slide 10

Slide 10 text

Stay Mindful of the Reasons We Write Code

Slide 11

Slide 11 text

“I can learn Ruby faster???”

Slide 12

Slide 12 text

The Wheel of Time ✤ A fantasy series from my childhood ✤ The female magic users have a strict training process built up over 100’s of years of magic use ✤ The male magic users, brand new to magic, try to catch up by using it for everything: cooking, chores, etc.

Slide 13

Slide 13 text

Use Ruby to Do More

Slide 14

Slide 14 text

Command-line Ruby Basics

Slide 15

Slide 15 text

The -e (execute) Switch $ ruby -e 'p 21 * 2' 42 $ ruby -e 'p "some data"' "some data" $ ruby -e 'var = 42; p var' 42 ✤ Run some Ruby without a script ✤ Hints: ✤ Use ‘…’ for shell quoting and “…” for Ruby quoting (to minimize escaping) ✤ Use ;’s to get multiple lines

Slide 16

Slide 16 text

The -e (execute) Switch $ ruby -e 'p 21 * 2' 42 $ ruby -e 'p "some data"' "some data" $ ruby -e 'var = 42; p var' 42 ✤ Run some Ruby without a script ✤ Hints: ✤ Use ‘…’ for shell quoting and “…” for Ruby quoting (to minimize escaping) ✤ Use ;’s to get multiple lines

Slide 17

Slide 17 text

The -e (execute) Switch $ ruby -e 'p 21 * 2' 42 $ ruby -e 'p "some data"' "some data" $ ruby -e 'var = 42; p var' 42 ✤ Run some Ruby without a script ✤ Hints: ✤ Use ‘…’ for shell quoting and “…” for Ruby quoting (to minimize escaping) ✤ Use ;’s to get multiple lines

Slide 18

Slide 18 text

The -e (execute) Switch $ ruby -e 'p 21 * 2' 42 $ ruby -e 'p "some data"' "some data" $ ruby -e 'var = 42; p var' 42 ✤ Run some Ruby without a script ✤ Hints: ✤ Use ‘…’ for shell quoting and “…” for Ruby quoting (to minimize escaping) ✤ Use ;’s to get multiple lines

Slide 19

Slide 19 text

The “Unix Filter” Pattern ✤ Accepts a file name on the command-line ✤ Or multiple names at once ✤ Or reads from STDIN ✤ Writes to STDOUT $ echo 'A line from file_1.' > file_1.txt $ echo 'A line from file_2.' > file_2.txt $ cat file_1.txt A line from file_1. $ cat file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | cat A line from $stdin.

Slide 20

Slide 20 text

The “Unix Filter” Pattern ✤ Accepts a file name on the command-line ✤ Or multiple names at once ✤ Or reads from STDIN ✤ Writes to STDOUT $ echo 'A line from file_1.' > file_1.txt $ echo 'A line from file_2.' > file_2.txt $ cat file_1.txt A line from file_1. $ cat file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | cat A line from $stdin.

Slide 21

Slide 21 text

The “Unix Filter” Pattern ✤ Accepts a file name on the command-line ✤ Or multiple names at once ✤ Or reads from STDIN ✤ Writes to STDOUT $ echo 'A line from file_1.' > file_1.txt $ echo 'A line from file_2.' > file_2.txt $ cat file_1.txt A line from file_1. $ cat file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | cat A line from $stdin.

Slide 22

Slide 22 text

The “Unix Filter” Pattern ✤ Accepts a file name on the command-line ✤ Or multiple names at once ✤ Or reads from STDIN ✤ Writes to STDOUT $ echo 'A line from file_1.' > file_1.txt $ echo 'A line from file_2.' > file_2.txt $ cat file_1.txt A line from file_1. $ cat file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | cat A line from $stdin.

Slide 23

Slide 23 text

Ruby’s ARGF ✤ ARGF means use ARGV to build a Unix Filter ✤ It supports the standard Unix Filter patterns ✤ You can use it like an IO object $ ruby -e 'puts ARGF.read' file_1.txt A line from file_1. $ ruby -e 'puts ARGF.read' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts ARGF.read' A line from $stdin.

Slide 24

Slide 24 text

Ruby’s ARGF ✤ ARGF means use ARGV to build a Unix Filter ✤ It supports the standard Unix Filter patterns ✤ You can use it like an IO object $ ruby -e 'puts ARGF.read' file_1.txt A line from file_1. $ ruby -e 'puts ARGF.read' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts ARGF.read' A line from $stdin.

Slide 25

Slide 25 text

Ruby’s ARGF ✤ ARGF means use ARGV to build a Unix Filter ✤ It supports the standard Unix Filter patterns ✤ You can use it like an IO object $ ruby -e 'puts ARGF.read' file_1.txt A line from file_1. $ ruby -e 'puts ARGF.read' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts ARGF.read' A line from $stdin.

Slide 26

Slide 26 text

Ruby’s ARGF ✤ ARGF means use ARGV to build a Unix Filter ✤ It supports the standard Unix Filter patterns ✤ You can use it like an IO object $ ruby -e 'puts ARGF.read' file_1.txt A line from file_1. $ ruby -e 'puts ARGF.read' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts ARGF.read' A line from $stdin.

Slide 27

Slide 27 text

Ruby’s ARGF ✤ ARGF means use ARGV to build a Unix Filter ✤ It supports the standard Unix Filter patterns ✤ You can use it like an IO object $ ruby -e 'puts ARGF.read' file_1.txt A line from file_1. $ ruby -e 'puts ARGF.read' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts ARGF.read' A line from $stdin.

Slide 28

Slide 28 text

Shortcuts

Slide 29

Slide 29 text

Kernel#gets ✤ Ruby’s default “get a line of input” method ✤ Available everywhere ✤ It reads from ARGF ✤ You get a Unix Filter for free $ ruby -e 'puts gets' file_1.txt A line from file_1. $ ruby -e '2.times do puts gets end' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts gets' A line from $stdin.

Slide 30

Slide 30 text

Kernel#gets ✤ Ruby’s default “get a line of input” method ✤ Available everywhere ✤ It reads from ARGF ✤ You get a Unix Filter for free $ ruby -e 'puts gets' file_1.txt A line from file_1. $ ruby -e '2.times do puts gets end' file_1.txt file_2.txt A line from file_1. A line from file_2. $ echo 'A line from $stdin.' | ruby -e 'puts gets' A line from $stdin.

Slide 31

Slide 31 text

The Current Line Variable: $_ ✤ Ruby has a special variable for “the current line:” $_ ✤ Note how it looks like a line ✤ Kernel#gets assigns to $_ ✤ Kernel#print prints $_ if no arguments are given $ ruby -e 'gets; print' file_1.txt A line from file_1. $ ruby -e 'gets; $_.tr! " ", "+"; print' file_1.txt A+line+from+file_1.

Slide 32

Slide 32 text

The Current Line Variable: $_ ✤ Ruby has a special variable for “the current line:” $_ ✤ Note how it looks like a line ✤ Kernel#gets assigns to $_ ✤ Kernel#print prints $_ if no arguments are given $ ruby -e 'gets; print' file_1.txt A line from file_1. $ ruby -e 'gets; $_.tr! " ", "+"; print' file_1.txt A+line+from+file_1.

Slide 33

Slide 33 text

The Current Line Variable: $_ ✤ Ruby has a special variable for “the current line:” $_ ✤ Note how it looks like a line ✤ Kernel#gets assigns to $_ ✤ Kernel#print prints $_ if no arguments are given $ ruby -e 'gets; print' file_1.txt A line from file_1. $ ruby -e 'gets; $_.tr! " ", "+"; print' file_1.txt A+line+from+file_1.

Slide 34

Slide 34 text

The Current Line Variable: $_ ✤ Ruby has a special variable for “the current line:” $_ ✤ Note how it looks like a line ✤ Kernel#gets assigns to $_ ✤ Kernel#print prints $_ if no arguments are given $ ruby -e 'gets; print' file_1.txt A line from file_1. $ ruby -e 'gets; $_.tr! " ", "+"; print' file_1.txt A+line+from+file_1.

Slide 35

Slide 35 text

The -p (print loop) Switch ✤ -p wraps -e code in a loop that: ✤ reads a line ✤ runs the -e code ✤ prints the line while gets # -e code goes here print end $ ruby -pe '# do nothing' file_1.txt file_2.txt A line from file_1. A line from file_2. $ ruby -pe '$_ = "Line #{ARGF.lineno}\n"' file_1.txt file_2.txt Line 1 Line 2

Slide 36

Slide 36 text

The -p (print loop) Switch ✤ -p wraps -e code in a loop that: ✤ reads a line ✤ runs the -e code ✤ prints the line while gets # -e code goes here print end $ ruby -pe '# do nothing' file_1.txt file_2.txt A line from file_1. A line from file_2. $ ruby -pe '$_ = "Line #{ARGF.lineno}\n"' file_1.txt file_2.txt Line 1 Line 2

Slide 37

Slide 37 text

The -p (print loop) Switch ✤ -p wraps -e code in a loop that: ✤ reads a line ✤ runs the -e code ✤ prints the line while gets # -e code goes here print end $ ruby -pe '# do nothing' file_1.txt file_2.txt A line from file_1. A line from file_2. $ ruby -pe '$_ = "Line #{ARGF.lineno}\n"' file_1.txt file_2.txt Line 1 Line 2

Slide 38

Slide 38 text

The -p (print loop) Switch ✤ -p wraps -e code in a loop that: ✤ reads a line ✤ runs the -e code ✤ prints the line while gets # -e code goes here print end $ ruby -pe '# do nothing' file_1.txt file_2.txt A line from file_1. A line from file_2. $ ruby -pe '$_ = "Line #{ARGF.lineno}\n"' file_1.txt file_2.txt Line 1 Line 2

Slide 39

Slide 39 text

The -p (print loop) Switch ✤ -p wraps -e code in a loop that: ✤ reads a line ✤ runs the -e code ✤ prints the line while gets # -e code goes here print end $ ruby -pe '# do nothing' file_1.txt file_2.txt A line from file_1. A line from file_2. $ ruby -pe '$_ = "Line #{ARGF.lineno}\n"' file_1.txt file_2.txt Line 1 Line 2

Slide 40

Slide 40 text

The -n (non-print loop) Switch ✤ Same as -p, but it doesn’t print ✤ This allows you to decide when to print $ ruby -ne 'print if $_ =~ /2\.\Z/' file_1.txt file_2.txt A line from file_2.

Slide 41

Slide 41 text

The -n (non-print loop) Switch ✤ Same as -p, but it doesn’t print ✤ This allows you to decide when to print $ ruby -ne 'print if $_ =~ /2\.\Z/' file_1.txt file_2.txt A line from file_2.

Slide 42

Slide 42 text

The -n (non-print loop) Switch ✤ Same as -p, but it doesn’t print ✤ This allows you to decide when to print $ ruby -ne 'print if $_ =~ /2\.\Z/' file_1.txt file_2.txt A line from file_2.

Slide 43

Slide 43 text

A Bare Regexp Conditional ✤ My last example can be shortened ✤ A bare Regexp conditional is assumed to be a test against $_ $ ruby -ne 'print if /2\.\Z/' file_1.txt file_2.txt A line from file_2.

Slide 44

Slide 44 text

A Bare Regexp Conditional ✤ My last example can be shortened ✤ A bare Regexp conditional is assumed to be a test against $_ $ ruby -ne 'print if /2\.\Z/' file_1.txt file_2.txt A line from file_2.

Slide 45

Slide 45 text

The “Flip-flop” Operator ✤ A conditional can also be a Range of two Regexp objects ✤ It “turns on” when the first matches and will stay true until the second matches $ ruby -e 'puts %w[one two three four five six]' > numbers.txt $ ruby -ne 'print if /\A[os]/../ee\Z/' numbers.txt one two three six

Slide 46

Slide 46 text

The “Flip-flop” Operator ✤ A conditional can also be a Range of two Regexp objects ✤ It “turns on” when the first matches and will stay true until the second matches $ ruby -e 'puts %w[one two three four five six]' > numbers.txt $ ruby -ne 'print if /\A[os]/../ee\Z/' numbers.txt one two three six

Slide 47

Slide 47 text

More Twists

Slide 48

Slide 48 text

The -i (in place edit) Switch ✤ Passing -i turns causes your filter to replace the input itself ✤ This is done with a buffer, so it feels like you are reading from and writing to the same place $ cat numbers.txt one two three four five six $ ruby -pi \ > -e '$_.tr! "aeiou", "X"' \ > numbers.txt $ cat numbers.txt XnX twX thrXX fXXr fXvX sXx

Slide 49

Slide 49 text

The -i (in place edit) Switch ✤ Passing -i turns causes your filter to replace the input itself ✤ This is done with a buffer, so it feels like you are reading from and writing to the same place $ cat numbers.txt one two three four five six $ ruby -pi \ > -e '$_.tr! "aeiou", "X"' \ > numbers.txt $ cat numbers.txt XnX twX thrXX fXXr fXvX sXx

Slide 50

Slide 50 text

The -i (in place edit) Switch ✤ Passing -i turns causes your filter to replace the input itself ✤ This is done with a buffer, so it feels like you are reading from and writing to the same place $ cat numbers.txt one two three four five six $ ruby -pi \ > -e '$_.tr! "aeiou", "X"' \ > numbers.txt $ cat numbers.txt XnX twX thrXX fXXr fXvX sXx

Slide 51

Slide 51 text

Now With Backups ✤ -i can also accept a backup argument ✤ The original content is moved into a file with the backup suffix appended ✤ This can really protect you when you try to make sweeping changes with some gnarly Regexp! $ ruby -pi.bak \ > -e '$_.delete! "X"' \ > numbers.txt $ cat numbers.txt n tw thr fr fv sx $ cat numbers.txt.bak XnX twX thrXX fXXr fXvX sXx

Slide 52

Slide 52 text

Now With Backups ✤ -i can also accept a backup argument ✤ The original content is moved into a file with the backup suffix appended ✤ This can really protect you when you try to make sweeping changes with some gnarly Regexp! $ ruby -pi.bak \ > -e '$_.delete! "X"' \ > numbers.txt $ cat numbers.txt n tw thr fr fv sx $ cat numbers.txt.bak XnX twX thrXX fXXr fXvX sXx

Slide 53

Slide 53 text

Now With Backups ✤ -i can also accept a backup argument ✤ The original content is moved into a file with the backup suffix appended ✤ This can really protect you when you try to make sweeping changes with some gnarly Regexp! $ ruby -pi.bak \ > -e '$_.delete! "X"' \ > numbers.txt $ cat numbers.txt n tw thr fr fv sx $ cat numbers.txt.bak XnX twX thrXX fXXr fXvX sXx

Slide 54

Slide 54 text

Now With Backups ✤ -i can also accept a backup argument ✤ The original content is moved into a file with the backup suffix appended ✤ This can really protect you when you try to make sweeping changes with some gnarly Regexp! $ ruby -pi.bak \ > -e '$_.delete! "X"' \ > numbers.txt $ cat numbers.txt n tw thr fr fv sx $ cat numbers.txt.bak XnX twX thrXX fXXr fXvX sXx

Slide 55

Slide 55 text

The -a (autosplit) Switch ✤ -a will cause $_ to be split() into $F (fields) with each read ✤ By default, split() uses a pattern like: /\s+/ $ ruby -e 'puts "some spaced\tfields"' | \ > ruby -nae 'p $F' ["some", "spaced", "fields"]

Slide 56

Slide 56 text

The -a (autosplit) Switch ✤ -a will cause $_ to be split() into $F (fields) with each read ✤ By default, split() uses a pattern like: /\s+/ $ ruby -e 'puts "some spaced\tfields"' | \ > ruby -nae 'p $F' ["some", "spaced", "fields"]

Slide 57

Slide 57 text

The -a (autosplit) Switch ✤ -a will cause $_ to be split() into $F (fields) with each read ✤ By default, split() uses a pattern like: /\s+/ $ ruby -e 'puts "some spaced\tfields"' | \ > ruby -nae 'p $F' ["some", "spaced", "fields"]

Slide 58

Slide 58 text

The -F (field separator) Switch ✤ Used with -a, -F will let you change the split() pattern ✤ WARNING: this is a weird old switch that doesn’t allow a space before the pattern starts! $ echo "1,2, 3" | \ > ruby -naF',\s*' -e 'p $F' ["1", "2", "3\n"]

Slide 59

Slide 59 text

The -F (field separator) Switch ✤ Used with -a, -F will let you change the split() pattern ✤ WARNING: this is a weird old switch that doesn’t allow a space before the pattern starts! $ echo "1,2, 3" | \ > ruby -naF',\s*' -e 'p $F' ["1", "2", "3\n"]

Slide 60

Slide 60 text

The -F (field separator) Switch ✤ Used with -a, -F will let you change the split() pattern ✤ WARNING: this is a weird old switch that doesn’t allow a space before the pattern starts! $ echo "1,2, 3" | \ > ruby -naF',\s*' -e 'p $F' ["1", "2", "3\n"]

Slide 61

Slide 61 text

The -l (line ending) Switch ✤ -l trims the line ending off of read input ✤ The record separator (Ruby’s idea of a line ending) can be changed by passing an octal character code to -0 (not shown) $ echo "1,2, 3" | \ > ruby -nalF',\s*' -e 'p $F' ["1", "2", "3"]

Slide 62

Slide 62 text

The -l (line ending) Switch ✤ -l trims the line ending off of read input ✤ The record separator (Ruby’s idea of a line ending) can be changed by passing an octal character code to -0 (not shown) $ echo "1,2, 3" | \ > ruby -nalF',\s*' -e 'p $F' ["1", "2", "3"]

Slide 63

Slide 63 text

Getting Back to Normal

Slide 64

Slide 64 text

The Choice is Yours ✤ You could take the blue pill and pretend Ruby is always beautiful, without any of these ugly features ✤ You could take the red pill, go further down the rabbit hole, and learn yourself some command-line Ruby wizardry

Slide 65

Slide 65 text

For Those Who Like Red Pills ✤ Learn some regular expression ✤ Look into the standard “find” library ✤ When you’re ready for the really crazy stuff, look up Ruby’s BEGIN { … } and END { … } blocks

Slide 66

Slide 66 text

One Last Warning ✤ Remember, I didn’t say these are the best ways to do things ✤ There are definitely shell commands that do some of these things better ✤ You can move on to learning those later ✤ Or you may already know them! ✤ That’s not the point ✤ The point: get new ideas and learn new things

Slide 67

Slide 67 text

Thanks!