Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Command-line Ruby

jeg2
February 22, 2013

Command-line Ruby

A discussion about ways to learn Ruby faster and one possible avenue for doing that.

jeg2

February 22, 2013
Tweet

More Decks by jeg2

Other Decks in Technology

Transcript

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

    View Slide

  2. 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

    View Slide

  3. Ruby is Beautiful

    View Slide

  4. 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

    View Slide

  5. That’s All True,
    and Usually Very Important

    View Slide

  6. But Not Today!

    View Slide

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

    View Slide

  8. “Why would you do this???”

    View Slide

  9. 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?

    View Slide

  10. Stay Mindful of
    the Reasons We Write Code

    View Slide

  11. “I can learn Ruby faster???”

    View Slide

  12. 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.

    View Slide

  13. Use Ruby to Do More

    View Slide

  14. Command-line Ruby Basics

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 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.

    View Slide

  20. 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.

    View Slide

  21. 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.

    View Slide

  22. 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.

    View Slide

  23. 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.

    View Slide

  24. 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.

    View Slide

  25. 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.

    View Slide

  26. 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.

    View Slide

  27. 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.

    View Slide

  28. Shortcuts

    View Slide

  29. 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.

    View Slide

  30. 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.

    View Slide

  31. 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.

    View Slide

  32. 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.

    View Slide

  33. 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.

    View Slide

  34. 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.

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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.

    View Slide

  41. 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.

    View Slide

  42. 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.

    View Slide

  43. 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.

    View Slide

  44. 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.

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

  47. More Twists

    View Slide

  48. 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

    View Slide

  49. 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

    View Slide

  50. 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

    View Slide

  51. 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

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. 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

    View Slide

  55. 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"]

    View Slide

  56. 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"]

    View Slide

  57. 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"]

    View Slide

  58. 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"]

    View Slide

  59. 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"]

    View Slide

  60. 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"]

    View Slide

  61. 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"]

    View Slide

  62. 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"]

    View Slide

  63. Getting Back to Normal

    View Slide

  64. 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

    View Slide

  65. 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

    View Slide

  66. 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

    View Slide

  67. Thanks!

    View Slide