我该如何使用Ruby给终端输出的文本添加颜色?

312

使用Ruby,我该如何在终端输出时进行背景和前景文本着色?

我记得,在编写Pascal的时候,我们都会编写自己的textcolor(…)过程,以使我们的小型教育程序看起来更漂亮、更具表现力。

我应该如何用Ruby编写相当于它的东西?核心库中是否有内置支持可用?如果没有,什么是添加这个功能的惯用方法?


1
所以无法获得“橙色”吗? - Matrix
请查看 https://github.com/kigster/colored2。 - Konstantin Gredeskoul
11个回答

420

Colorize是我最喜欢的gem!:-)

看看这个:

https://github.com/fazibear/colorize

安装:

gem install colorize

使用方法:

require 'colorize'

puts "I am now red".red
puts "I am now blue".blue
puts "Testing".yellow

2
有人能告诉我在Cygwin终端中是否可以使用Colorize吗?我尝试在Cygwin中运行上面的代码,但它没有显示颜色... - Redoman
5
如果你安装了 win32console gem 并在 colorize 后面添加 require 'win32console',那么这段代码就可以在 Windows 命令提示符中正常运行。 - Ben
2
@Ben 我个人没有尝试过,但自从 Ruby 2.0 版本以后,你应该不再需要 win32console gem 了。https://github.com/luislavena/win32console/issues/17#issuecomment-20811539 - Dennis
1
有没有办法让这个在Sublime Text控制台中工作? - nipponese
11
这个宝石采用GPL许可证,因此(我认为)它不能用于专有软件。 http://jonathannen.com/2013/07/07/license-your-gems.html - Andrei Botalov
显示剩余3条评论

291

将以上答案结合起来,您可以实现类似于宝石colorize的东西,而无需其他依赖项。

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

1
啊,Nick编辑得很好。当然不需要传递self参数。我写这个时有点累了 :) - Erik Skoglund
这个在 Windows 上也能用吗? - Alp
如果您使用ConEmu,它可以在Windows上运行。 - Mike Glenn
1
我更喜欢这个比上色,因为它只改变前景颜色。上色似乎总是改变背景颜色。 - jlyonsmith
1
我知道我来晚了,但在这里使用闭包不是更好吗? - user4531029

281

字符串类方法(仅适用于Unix):

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

并且用法:

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

在我的控制台:

这里输入图像描述

此外,

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

去除格式化字符。

注意

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

加粗应该使用转义码22而不是21:def bold; "\e[1m#{self}\e[22m" end - Kanat Bolazar
@KanatBolazar,有些系统支持21,但我因为能力问题而将其更改为22。谢谢。 - Ivan Black
1
太棒了,我把它放在我的Rails应用程序的初始化器中。非常好用! - user4262528
1
在 Windows 10 的 cmd.exe 中,使用 puts "\e[0" 无法清除格式;必须使用 puts "\e[0m" - Nnnes
请访问此链接以查看代码:https://gist.github.com/equivalent/74b1dec423f186c6e594f00b9aacff10 - equivalent8
显示剩余3条评论

48

我写了一个小方法来测试基础颜色模式,这个方法是基于Erik Skoglund和其他人的答案。

#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

示例输出:ruby colortest


40

您可以使用ANSI转义序列在控制台中实现此操作。我知道这适用于Linux和Mac OS X,但我不确定Windows控制台(cmd)是否支持ANSI。

我在Java中实现了它,但思路是相同的。

// 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";}

8
这个方法可行,不需要 Gem,这可能会让一些人感到烦恼。 - ThomasW
3
Windows 控制台确实支持 ANSI 代码。 - Ben

19
虽然其他答案对大多数人来说都可以胜任,但应该提到“正确”的Unix方式。由于不是所有类型的文本终端都支持这些序列,因此您可以查询 terminfo 数据库,这是对各种文本终端能力的抽象。这似乎主要是出于历史兴趣 - 今天使用的软件终端通常支持ANSI序列 - 但它确实具有(至少)一种实际效果:有时有用将环境变量 TERM 设置为 dumb 以避免所有这些样式,例如将输出保存到文本文件中。而且,做正确的事情感觉很好。 :-)
您可以使用 ruby-terminfo gem。它需要一些C编译才能安装;我能在我的Ubuntu 14.10系统下安装它:
$ sudo apt-get install libncurses5-dev
$ gem install ruby-terminfo --user-install

然后你可以像这样查询数据库(请参阅terminfo手册,了解可用代码的列表):

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

这是我编写的一个小封装类,使使用变得更加简单。
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

使用方法:

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}"

上面Ruby脚本的输出

(编辑) 最后,如果您不想要求一个gem,您可以依赖于tput程序,如此描述 - Ruby示例:

puts "Hi! " + `tput setaf 1` + "This is red!" + `tput sgr0`

8
major, *major* +1表示对使用tput命令的大力赞扬。我无法形容tput命令为我节省了多少头发。请注意,这里的“头发”是比喻,意思是说tput帮助解决了很多问题,让工作更加轻松。 - Pierce

16

我做了一个可以帮助的方法。虽然不是什么大不了的事情,但它很有效:

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

这是如何使用它的方法:

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

可能的改进如下:

  • colorsbgColors 每次调用方法时都会被定义,且它们不会改变。
  • 添加其他选项,例如 boldunderlinedim 等。

该方法对于 p 不起作用,因为 p 对其参数执行了 inspect。例如:


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

将显示"\e[0;38m你好世界\e[0m"

我使用了putsprint和Logger gem进行测试,它可以正常工作。


我改进了这个并创建了一个类,使得colorsbgColors成为类的常量,colorize成为一个类方法:

编辑:更好的代码风格,定义常量而不是类变量,使用符号而不是字符串,添加了更多选项,如加粗、斜体等。

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

你可以通过以下方式使用它:

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

14

这是我为了使它能够正常工作而不需要任何宝石(gems)所做的:

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

然后只有引号中的文本被着色,你将返回到正常的程序。


3
对我没用。我收到的确切信息是:e[32m一些文本 - Oscar Godson
第一个转义字符中有一个错别字:应该是 "\e(...)" 而不是 "e\(...)" - arthropod

13

我找到了几个:

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

例子:

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

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

示例:

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

http://github.com/sickill/rainbow

例子:

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

如果您使用的是Windows系统,可能需要执行"gem install win32console"命令以启用颜色支持。
另外,如果您需要创建自己的gem,可以参考Colorizing console Ruby-script output一文。该文章介绍了如何为字符串添加ANSI颜色。您可以使用这些知识来编写一个扩展字符串的类。

9

1
通过改进此链接上的示例,您可以扩展String类以使其更易于使用(“Hello”.red):class String; def red; colorize(self, "\033[31m"); end; end - Adriano P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接