Раскрашивание вывода консольного скрипта Ruby

Posted by Dmytro Shteflyuk on under Ruby & Rails · English (31,830 views)

Довольно часто мне приходится (или из-за врожденной лени для оптимизации каких-то рутинных вещей) писать консольные скрипты. Многие из них выводят какую-то информацию, отображают статус процесса или выводят результаты своей работы. Как бы то ни было, читать весь вывод скрипта бывает довольно утомительным занятием, и хочется каким-то образом подсветить наиболее важные моменты вывода: ошибки красным, успех ключевых шагов зеленым и т.д. И тут на помощь приходят управляющие коды ANSI, поддерживаемые многими терминалами, включая VT100 (кстати, консоль Windows семейства NT не поддерживает, но об этом ниже).

Для начала, рассмотрим, что же представляет из себя ANSI-последовательность. Она начинается с символа ESC (ASCII-код 27) с последующим символом открывающей квадратной скобки [. Далее следует одно или несколько чисел, разделенных точкой с запятой ;, и завершается буквой.

Я не буду описывать все возможные коды, желающие могут найти их в Wikipedia. Для изменения цвета и фона используется последовательность, завершающаяся символом m, и в общем виде выглядит так: ESC[31m, где 31 – установка цвета текста в красный. Вот таблица кодов, поддерживаемых большинством терминалов:

Код Эффект
0 Сбросить все атрибуты
1 Установить повышенную яркость
4 Включить подчеркивание
5 Включить мигание
7 Поменять местами цвет фона и цвет текста
8 Скрыть текст (цвет у текста установливается такой же, как и у фона)
30 Черный текст
31 Красный текст
32 Зеленый текст
33 Желтый текст
34 Синий текст
35 Пурпурный текст
36 Голубой текст
37 Белый текст
39 Цвет текста по умолчанию
40 Черный фон
41 Красный фон
42 Зеленый фон
43 Желтый фон
44 Синий фон
45 Розовый фон
46 Голубой фон
47 Белый фон
49 Цвет фона по умолчанию

Как видно из таблицы, цвет фона и текста устанавливается отдельными кодами, которые можно комбинировать в одну последовательность (например, ESC[1;33;44m – желтый текст на синем фоне, повышенная яркость).

Внимание: Не забудьте сбросить атрибуты перед выходом из скрипта, иначе весь последующий текст будет отображаться с Вашими атрибутами.

Хватит теории, рассмотрим пример:

1
2
3
4
# Actual work
puts "Importing categories [ e[32mDONEe[0m ]"
# Actual work
puts "Importing tags       [e[31mFAILEDe[0m]"

В результате работы вы увидите что-то вроде:

VT100 Example 1

Всю жизнь использовал коды именно так, как показано в примере, но недавно, когда ковырял исходники RSpec и наткнулся на простенькие хелперы:

1
2
3
4
5
6
7
8
9
10
11
def colorize(text, color_code)
  "#{color_code}#{text}e[0m"
end

def red(text); colorize(text, "e[31m"); end
def green(text); colorize(text, "e[32m"); end

# Actual work
puts 'Importing categories [ ' + green('DONE') + ' ]'
# Actual work
puts 'Importing tags       [' + red('FAILED') + ']'

Идея хорошая, так теперь и пользую. Чего и Вам желаю :-)

Теперь о грустном. Windows XP (и, насколько я помню, Windows 2000 тоже) не поддерживают ANSI-последовательности. Кто любит поизвращаться, вперед читать статью Command Interpreter Ansi Support, остальные могут остаться здесь и взглянуть, как проблема решается средствами Ruby.

Для начала нужно установить win32console:

1
gem install win32console

Теперь добавьте в начало вашего скрипта следующие строки (взято, опять-таки, из RSpec):

1
2
3
4
5
begin
  require 'Win32/Console/ANSI' if PLATFORM =~ /win32/
rescue LoadError
  raise 'You must gem install win32console to use color on Windows'
end

Вывод скрипта будет раскрашенным как в Windows, так и в Unix-системах.

Ну и напоследок приведу полную таблицу разных кодов, которые вы можете использовать в своих скриптах:

VT100 Terminal

Получена с помощью следующего скрипта:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/ruby

[0, 1, 4, 5, 7].each do |attr|
  puts '----------------------------------------------------------------'
  puts "ESC[#{attr};Foreground;Background"
  30.upto(37) do |fg|
    40.upto(47) do |bg|
      print "\033[#{attr};#{fg};#{bg}m #{fg};#{bg}  "
    end
  puts "\033[0m"
  end
end

Updated 06/10/2010: Replaced PLATFORM constant with the RUBY_PLATFORM (thanks to Ian Alexander Wood).

10 Responses to this entry

Subscribe to comments with RSS

shaliko @
said on Март 24, 2007 at 15:24 · Permalink

Большое спасибо за статью!

Непременно воспользуюсь всем выше перечисленным..

Shatyorkin
said on Март 27, 2007 at 11:58 · Permalink

Классная штука – я это для тестов приспособил.

said on Июль 12, 2007 at 00:13 · Permalink

Thanks for the code, it helped a lot for a console app that I am working on.

said on Июль 25, 2007 at 00:17 · Permalink

This is a great article. If you continue to write interesting things like this I will have to subscribe to your feed!

said on Октябрь 12, 2007 at 09:57 · Permalink

спасибо за статью. я как–бы думал, што у руби может быть свой стиль колоризации, но оказывается это те же саммые анси .)

Artem Vasiliev @
said on Ноябрь 27, 2007 at 18:54 · Permalink

Круто, спасибо!

Прикрутил это у себя и к серверной консоли, и к autotest..

Правда, не без помощи такой-то матери – простое добавление в environment.rb для autotest не достаточно, пришлось продублировать в .autotest

pmil @
said on Февраль 27, 2008 at 23:31 · Permalink

I have similar problem but I wanted to see Rails log colors in win console.
A little addition.
1. create file console.rb

1
2
3
4
5
6
require 'rubygems'
require 'win32console'
include Win32::Console::ANSI
include Term::ANSIColor

puts bold
said on Июль 15, 2008 at 05:36 · Permalink

Я так делаю обычно:

1
2
3
4
5
6
7
8
9
10
11
class String

    def red; colorize(self, "\e[1m\e[31m"); end
    def green; colorize(self, "\e[1m\e[32m"); end
    def dark_green; colorize(self, "\e[32m"); end
    def yellow; colorize(self, "\e[1m\e[33m"); end
    def blue; colorize(self, "\e[1m\e[34m"); end
    def dark_blue; colorize(self, "\e[34m"); end
    def pur; colorize(self, "\e[1m\e[35m"); end
    def colorize(text, color_code)  "#{color_code}#{text}\e[0m" end
end

И при использовании

1
puts "Hello".red

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.