310

Using Ruby, how can I perform background and foreground text colorization for output in the terminal?

I remember, when programming Pascal we all used to write our own textcolor(…) procedures to make our small educational programs look more pretty and presentational.

How would I go about coding an equivalent of that in Ruby? Is there any built-in support in the core library that lends itself to this? If not, what would be an idiomatic way to add it?

Ian
  • 11,280
  • 3
  • 36
  • 58
oldhomemovie
  • 14,621
  • 13
  • 64
  • 99

11 Answers11

420

Colorize is my favorite gem! :-)

Check it out:

https://github.com/fazibear/colorize

Installation:

gem install colorize

Usage:

require 'colorize'

puts "I am now red".red
puts "I am now blue".blue
puts "Testing".yellow
infinite-etcetera
  • 1,018
  • 8
  • 7
jaredmdobson
  • 4,601
  • 1
  • 17
  • 11
  • Much easier than doing it by hand! – Ryan Michela Sep 28 '09 at 20:46
  • Well, nice solution. But it made me 5 gems to install instead of one, to run it – oldhomemovie Oct 04 '09 at 14:16
  • 2
    Could someone tell me if Colorize does work in Cygwin Terminal ? I tried running the above code in Cygwin but it comes out without colors.. – Redoman Mar 08 '13 at 15:07
  • 5
    This will work fine in a Windows Command Prompt if you install the `win32console` gem and `require 'win32console'` after `colorize`. – Ben Dec 09 '13 at 17:19
  • 2
    @Ben I haven't personally tried it, but since Ruby 2.0 you should no longer need the `win32console` gem. https://github.com/luislavena/win32console/issues/17#issuecomment-20811539 – Dennis May 15 '14 at 19:17
  • 1
    Any way to make this work with Sumblime Text console? – nipponese Oct 19 '14 at 23:21
  • 11
    This gem is licensed under GPL so (I think) it can't be used in proprietary software. http://jonathannen.com/2013/07/07/license-your-gems.html – Andrei Botalov Jan 22 '16 at 15:17
  • Appending to @AndreiBotalov's comment: **this gem should be avoided. It uses a strong copyleft license (GPL2.0)**, so if you use it in your project, you are required to not only open source your code, but you must also license your project under GPL. The more popular alternative nowadays is the [Rainbow gem](https://github.com/sickill/rainbow) – TonyArra May 24 '23 at 14:17
288

Combining the answers above, you can implement something that works like the gem colorize without needing another dependency.

class String
  # colorization
  def colorize(color_code)
    "\e[#{color_code}m#{self}\e[0m"
  end

  def red
    colorize(31)
  end

  def green
    colorize(32)
  end

  def yellow
    colorize(33)
  end

  def blue
    colorize(34)
  end

  def pink
    colorize(35)
  end

  def light_blue
    colorize(36)
  end
end
BananaNeil
  • 10,322
  • 7
  • 46
  • 66
Erik Skoglund
  • 3,075
  • 1
  • 14
  • 12
278

As String class methods (Unix only):

class String
def black;          "\e[30m#{self}\e[0m" end
def red;            "\e[31m#{self}\e[0m" end
def green;          "\e[32m#{self}\e[0m" end
def brown;          "\e[33m#{self}\e[0m" end
def blue;           "\e[34m#{self}\e[0m" end
def magenta;        "\e[35m#{self}\e[0m" end
def cyan;           "\e[36m#{self}\e[0m" end
def gray;           "\e[37m#{self}\e[0m" end

def bg_black;       "\e[40m#{self}\e[0m" end
def bg_red;         "\e[41m#{self}\e[0m" end
def bg_green;       "\e[42m#{self}\e[0m" end
def bg_brown;       "\e[43m#{self}\e[0m" end
def bg_blue;        "\e[44m#{self}\e[0m" end
def bg_magenta;     "\e[45m#{self}\e[0m" end
def bg_cyan;        "\e[46m#{self}\e[0m" end
def bg_gray;        "\e[47m#{self}\e[0m" end

def bold;           "\e[1m#{self}\e[22m" end
def italic;         "\e[3m#{self}\e[23m" end
def underline;      "\e[4m#{self}\e[24m" end
def blink;          "\e[5m#{self}\e[25m" end
def reverse_color;  "\e[7m#{self}\e[27m" end
end

And usage:

puts "I'm back green".bg_green
puts "I'm red and back cyan".red.bg_cyan
puts "I'm bold and green and backround red".bold.green.bg_red

In my console:

Enter image description here

Additionally,

def no_colors
  self.gsub /\e\[\d+m/, ""
end

removes formatting characters.

Note

puts "\e[31m" # set format (red foreground)
puts "\e[0m"   # clear format
puts "green-#{"red".red}-green".green # will be green-red-normal, because of \e[0
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ivan Black
  • 4,827
  • 1
  • 35
  • 33
46

I wrote a little method to test out the basic color modes, based on answers by Erik Skoglund and others.

#outputs color table to console, regular and bold modes
def colortable
  names = %w(black red green yellow blue pink cyan white default)
  fgcodes = (30..39).to_a - [38]

  s = ''
  reg  = "\e[%d;%dm%s\e[0m"
  bold = "\e[1;%d;%dm%s\e[0m"
  puts '                       color table with these background codes:'
  puts '          40       41       42       43       44       45       46       47       49'
  names.zip(fgcodes).each {|name,fg|
    s = "#{fg}"
    puts "%7s "%name + "#{reg}  #{bold}   "*9 % [fg,40,s,fg,40,s,  fg,41,s,fg,41,s,  fg,42,s,fg,42,s,  fg,43,s,fg,43,s,  
      fg,44,s,fg,44,s,  fg,45,s,fg,45,s,  fg,46,s,fg,46,s,  fg,47,s,fg,47,s,  fg,49,s,fg,49,s ]
  }
end

example output: ruby colortest

icy
  • 914
  • 6
  • 12
40

You can use ANSI escape sequences to do this in the console. I know this works on Linux and Mac OS X, but I'm not sure if the Windows console (cmd) supports ANSI.

I did it in Java, but the ideas are the same.

// Foreground color
public static final String BLACK_TEXT()   { return "\033[30m";}
public static final String RED_TEXT()     { return "\033[31m";}
public static final String GREEN_TEXT()   { return "\033[32m";}
public static final String BROWN_TEXT()   { return "\033[33m";}
public static final String BLUE_TEXT()    { return "\033[34m";}
public static final String MAGENTA_TEXT() { return "\033[35m";}
public static final String CYAN_TEXT()    { return "\033[36m";}
public static final String GRAY_TEXT()    { return "\033[37m";}

// Background color
public static final String BLACK_BACK()   { return "\033[40m";}
public static final String RED_BACK()     { return "\033[41m";}
public static final String GREEN_BACK()   { return "\033[42m";}
public static final String BROWN_BACK()   { return "\033[43m";}
public static final String BLUE_BACK()    { return "\033[44m";}
public static final String MAGENTA_BACK() { return "\033[45m";}
public static final String CYAN_BACK()    { return "\033[46m";}
public static final String WHITE_BACK()   { return "\033[47m";}

// ANSI control characters
public static final String RESET_COLORS() { return "\033[0m";}
public static final String BOLD_ON()      { return "\033[1m";}
public static final String BLINK_ON()     { return "\033[5m";}
public static final String REVERSE_ON()   { return "\033[7m";}
public static final String BOLD_OFF()     { return "\033[22m";}
public static final String BLINK_OFF()    { return "\033[25m";}
public static final String REVERSE_OFF()  { return "\033[27m";}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ryan Michela
  • 8,284
  • 5
  • 33
  • 47
19

While the other answers will do the job fine for most people, the "correct" Unix way of doing this should be mentioned. Since all types of text terminals do not support these sequences, you can query the terminfo database, an abstraction over the capabilites of various text terminals. This might seem mostly of historical interest – software terminals in use today generally support the ANSI sequences – but it does have (at least) one practical effect: it is sometimes useful to be able to set the environment variable TERM to dumb to avoid all such styling, for example when saving the output to a text file. Also, it feels good to do things right. :-)

You can use the ruby-terminfo gem. It needs some C compiling to install; I was able to install it under my Ubuntu 14.10 system with:

$ sudo apt-get install libncurses5-dev
$ gem install ruby-terminfo --user-install

Then you can query the database like this (see the terminfo man page for a list of what codes are available):

require 'terminfo' 
TermInfo.control("bold")
puts "Bold text"
TermInfo.control("sgr0")
puts "Back to normal."
puts "And now some " + TermInfo.control_string("setaf", 1) + 
     "red" + TermInfo.control_string("sgr0") + " text."

Here's a little wrapper class I put together to make things a little more simple to use.

require 'terminfo'

class Style
  def self.style() 
    @@singleton ||= Style.new
  end

  colors = %w{black red green yellow blue magenta cyan white}
  colors.each_with_index do |color, index|
    define_method(color) { get("setaf", index) }
    define_method("bg_" + color) { get("setab", index) }
  end

  def bold()  get("bold")  end
  def under() get("smul")  end
  def dim()   get("dim")   end
  def clear() get("sgr0")  end

  def get(*args)
    begin
      TermInfo.control_string(*args)
    rescue TermInfo::TermInfoError
      ""
    end
  end
end

Usage:

c = Style.style
C = c.clear
puts "#{c.red}Warning:#{C} this is #{c.bold}way#{C} #{c.bg_red}too much #{c.cyan + c.under}styling#{C}!"
puts "#{c.dim}(Don't you think?)#{C}"

Output of above Ruby script

(edit) Finally, if you'd rather not require a gem, you can rely on the tput program, as described here – Ruby example:

puts "Hi! " + `tput setaf 1` + "This is red!" + `tput sgr0`
Community
  • 1
  • 1
skagedal
  • 2,323
  • 23
  • 34
  • 8
    Major, *major* +1 for use of `tput`. Can't even articulate how much hair loss `tput` has saved me. – Pierce Dec 29 '15 at 15:40
16

I made this method that could help. It is not a big deal but it works:

def colorize(text, color = "default", bgColor = "default")
    colors = {"default" => "38","black" => "30","red" => "31","green" => "32","brown" => "33", "blue" => "34", "purple" => "35",
     "cyan" => "36", "gray" => "37", "dark gray" => "1;30", "light red" => "1;31", "light green" => "1;32", "yellow" => "1;33",
      "light blue" => "1;34", "light purple" => "1;35", "light cyan" => "1;36", "white" => "1;37"}
    bgColors = {"default" => "0", "black" => "40", "red" => "41", "green" => "42", "brown" => "43", "blue" => "44",
     "purple" => "45", "cyan" => "46", "gray" => "47", "dark gray" => "100", "light red" => "101", "light green" => "102",
     "yellow" => "103", "light blue" => "104", "light purple" => "105", "light cyan" => "106", "white" => "107"}
    color_code = colors[color]
    bgColor_code = bgColors[bgColor]
    return "\033[#{bgColor_code};#{color_code}m#{text}\033[0m"
end

Here's how to use it:

puts "#{colorize("Hello World")}"
puts "#{colorize("Hello World", "yellow")}"
puts "#{colorize("Hello World", "white","light red")}"

Possible improvements could be:

  • colors and bgColors are being defined each time the method is called and they don't change.
  • Add other options like bold, underline, dim, etc.

This method does not work for p, as p does an inspect to its argument. For example:

p "#{colorize("Hello World")}"

will show "\e[0;38mHello World\e[0m"

I tested it with puts, print, and the Logger gem, and it works fine.


I improved this and made a class so colors and bgColors are class constants and colorize is a class method:

EDIT: Better code style, defined constants instead of class variables, using symbols instead of strings, added more options like, bold, italics, etc.

class Colorizator
    COLOURS = { default: '38', black: '30', red: '31', green: '32', brown: '33', blue: '34', purple: '35',
                cyan: '36', gray: '37', dark_gray: '1;30', light_red: '1;31', light_green: '1;32', yellow: '1;33',
                light_blue: '1;34', light_purple: '1;35', light_cyan: '1;36', white: '1;37' }.freeze
    BG_COLOURS = { default: '0', black: '40', red: '41', green: '42', brown: '43', blue: '44',
                   purple: '45', cyan: '46', gray: '47', dark_gray: '100', light_red: '101', light_green: '102',
                   yellow: '103', light_blue: '104', light_purple: '105', light_cyan: '106', white: '107' }.freeze

    FONT_OPTIONS = { bold: '1', dim: '2', italic: '3', underline: '4', reverse: '7', hidden: '8' }.freeze

    def self.colorize(text, colour = :default, bg_colour = :default, **options)
        colour_code = COLOURS[colour]
        bg_colour_code = BG_COLOURS[bg_colour]
        font_options = options.select { |k, v| v && FONT_OPTIONS.key?(k) }.keys
        font_options = font_options.map { |e| FONT_OPTIONS[e] }.join(';').squeeze
        return "\e[#{bg_colour_code};#{font_options};#{colour_code}m#{text}\e[0m".squeeze(';')
    end
end

You can use it by doing:

Colorizator.colorize "Hello World", :gray, :white
Colorizator.colorize "Hello World", :light_blue, bold: true
Colorizator.colorize "Hello World", :light_blue, :white, bold: true, underline: true
Redithion
  • 986
  • 1
  • 19
  • 32
14

Here's what I did to make it work without needing any gems:

def red(mytext) ; "\e[31m#{mytext}\e[0m" ; end
puts red("hello world")

Then only the text in the quotes there is colored, and you're returned to your regularly scheduled program.

prusswan
  • 6,853
  • 4
  • 40
  • 61
suzyQ
  • 301
  • 1
  • 3
  • 4
13

I found a few:

http://github.com/ssoroka/ansi/tree/master

Examples:

puts ANSI.color(:red) { "hello there" }
puts ANSI.color(:green) + "Everything is green now" + ANSI.no_color

http://flori.github.com/term-ansicolor/

Examples:

print red, bold, "red bold", reset, "\n"
print red(bold("red bold")), "\n"
print red { bold { "red bold" } }, "\n"

http://github.com/sickill/rainbow

Example:

puts "this is red".foreground(:red) + " and " + "this on yellow bg".background(:yellow) + " and " + "even bright underlined!".underline.bright

If you are on Windows you may need to do a "gem install win32console" to enable support for colors.

Also the article Colorizing console Ruby-script output is useful if you need to create your own gem. It explains how to add ANSI coloring to strings. You can use this knowledge to wrap it in some class that extends string or something.

Petros
  • 8,862
  • 3
  • 39
  • 38
9

This may help you: Colorized ruby output

ennuikiller
  • 46,381
  • 14
  • 112
  • 137
  • 1
    And improving the sample on this link, you can extend the String class to make it easier to use ("Hello".red): `class String; def red; colorize(self, "\033[31m"); end; end` – Adriano P Jul 18 '12 at 16:06
7

I found the previous answers to be useful. However, they didn't fit the bill if I wanted to colorize something like log output without using any third-party libraries. The following solved the issue for me:

red = 31
green = 32
blue = 34

def color (color=blue)
  printf "\033[#{color}m";
  yield
  printf "\033[0m"
end

color { puts "this is blue" }
color(red) { logger.info "and this is red" }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
pmyjavec
  • 71
  • 1
  • 2