Ruby 预定义全局变量与常量完全指南:从 `$!` 到 `DATA` 的源码级剖析
2026/9/13 18:15:58 网站建设 项目流程

Ruby 预定义全局变量与常量完全指南:从$!DATA的源码级剖析

【免费下载链接】rubyThe Ruby Programming Language项目地址: https://gitcode.com/GitHub_Trending/ru/ruby

本指南以 doc/language/globals.md 为骨架,系统梳理 Ruby 语言内置的全部预定义全局变量(如$!$~$/$LOAD_PATH等)与预定义全局常量(如STDINARGVRUBY_VERSIONDATA等),逐一说明其含义、初始值、可写性、重置时机,并结合当前仓库的 C 源码(ruby.c、io.c、process.c、load.c)与标准库 lib/English.rb 揭示这些特殊变量的底层实现与别名机制。读完本文,你将能熟练运用这些全局变量编写脚本、调试异常、处理命令行输入输出,并理解-0/-a/-F/-l/-p/-d/-v等命令行选项是如何映射到这些变量的。

一、概述:什么是预定义全局变量与常量

Ruby 解释器在启动时预定义了一组全局变量(以$开头)和全局常量(全大写)。它们承载着运行时最核心的状态:最近一次异常、最近一次正则匹配结果、标准输入输出流、进程信息、加载路径、调试开关等。

其中一部分变量带有「密码般」的符号名(如$!$~$/),Ruby 提供名为 English 的标准库,为它们提供可读的英文别名(如$ERROR_INFO$LAST_MATCH_INFO$INPUT_RECORD_SEPARATOR)。启用方式只有一行:

require 'English'

在 lib/English.rb 中,这些别名全部通过alias关键字实现,例如:

alias $ERROR_INFO $! alias $LAST_MATCH_INFO $~ alias $INPUT_RECORD_SEPARATOR $/ alias $OUTPUT_RECORD_SEPARATOR $\ alias $PROCESS_ID $$ alias $CHILD_STATUS $? alias $ARGV $* alias $MATCH $& alias $PREMATCH $` alias $POSTMATCH $' alias $LAST_PAREN_MATCH $+

从源码可以确认,English模块本身只是一个占位声明(module English end if false,见 lib/English.rb),真正起作用的就是文件顶层的这些alias语句。加载该库后,两种写法完全等价。

阅读表格的方法

原文档为每个变量给出了五列属性:

含义
Variable符号形式的变量名
\Englishrequire 'English'后可用的英文别名
Contains变量实际保存的内容
Initially程序启动时的初始值
Read-Only是否只读(只读变量无法在程序中赋值)
Reset By哪些操作会重置/更新该变量

下文将按「异常、匹配数据、分隔符、流、进程、调试、其他、已废弃」的顺序逐一详解。

二、异常相关:$!$@

$!($ERROR_INFO)

$!保存由Kernel#raise设置的 Exception 对象,初始为nil,只读。在rescue块中它指向当前捕获的异常:

begin raise RuntimeError.new('Boo!') rescue RuntimeError p $! end

输出:

#<RuntimeError: Boo!>

英文别名:$ERROR_INFO(由 lib/English.rb 定义)。

$@($ERROR_POSITION)

$@等价于$!.backtrace,返回由回溯位置组成的数组:

begin raise RuntimeError.new('Boo!') rescue RuntimeError pp $@.take(4) end

输出类似:

["(irb):338:in `<top (required)>'", "/snap/ruby/317/lib/ruby/3.2.0/irb/workspace.rb:119:in `eval'", ...]

英文别名:$ERROR_POSITION。在 lib/English.rb 中注明它是线程局部的(thread local)。底层实现上,这两个变量与解释器维护的「当前异常」状态紧密相关:在 eval.c 中可以看到,当ruby_debug为真且当前异常信息非 nil 时,解释器会把每个被 raise 的异常打印到$stderr——这正是$DEBUG相关行为的实现位置(详见下文「调试相关」)。

三、匹配数据:$~$&$``、$'$+$1$n`

这组变量保存当前作用域内最近一次成功的模式匹配的信息,被统称为「匹配数据」。它们具有线程局部与帧(frame)局部的特性。详细的正则匹配语义可参考 doc/language/regexp/methods.rdoc 中的 Global Variables 一节。

变量English 别名内容可写
$~$LAST_MATCH_INFO由匹配生成的 MatchData 对象
$&$MATCH匹配到的子串
$`$PREMATCH匹配位置左侧的子串
$'$POSTMATCH匹配位置右侧的子串
$+$LAST_PAREN_MATCH最后匹配到的分组
$n第 n 个分组匹配到的内容

$1$2等数字变量没有 English 别名,且原文档明确它们是只读的(Read-Only = Yes)。

lib/English.rb 对这些变量做了额外说明,值得注意:

  • $&$``、$'$1$9全部由$~派生而来,**对$~` 赋值会同步改变这些派生变量的值**;
  • $+保存编号最高的那个分组的内容,例如"cat" =~ /(c|a)(t|z)/之后$+"t"
  • 这些变量都是当前作用域局部(local to the current scope)且只读的。

四、记录分隔符:$/$\

$/($INPUT_RECORD_SEPARATOR,别名$RS$-0

输入记录分隔符,初始为换行符"\n"gets等读取方法依据它来确定「一条记录」的边界;若设为nilgets将一次性读取整个文件。它由命令行选项-0设置,且除命令行选项之外将其设为非 nil 值已被标记为废弃(deprecated)

-0的具体规则见 doc/language/options.md:

  • 无参数时,记录分隔符为0x00
  • 参数为0时,分隔符为''(空串);
  • 参数在1..0377(八进制)时,分隔符为对应字符;
  • 参数为其他八进制值时,分隔符为nil

实际效果:

$ ruby -0 -e 'p $/' "\x00" $ ruby -00 -e 'p $/' "" $ ruby -012 -e 'p $/' "\n" $ ruby -015 -e 'p $/' "\r" $ ruby -0377 -e 'p $/' "\xFF" $ ruby -0400 -e 'p $/' nil

英文别名:$INPUT_RECORD_SEPARATOR$RS(见 lib/English.rb)。符号名$-0$/等价。

$\($OUTPUT_RECORD_SEPARATOR,别名$ORS

输出记录分隔符,初始为nil。当命令行选项-l给出时,会把$/的值复制给它。它被追加到printIO#write等每次输出的末尾(默认nil表示不加任何文本)。同样的,除命令行选项之外将其设为非 nil 值已被废弃。英文别名:$OUTPUT_RECORD_SEPARATOR$ORS(见 lib/English.rb)。

五、标准流:$stdin$stdout$stderr$<$>$.$_

$stdin/$stdout/$stderr

当前的(可重定向的)标准输入、输出、错误流,初始值分别为STDINSTDOUTSTDERR

$stdin # => #<IO:<STDIN>> $stdout # => #<IO:<STDOUT>> $stderr # => #<IO:<STDERR>>

这三个变量是可写的,程序中可以重新赋值以重定向输出。它们的底层实现在 io.c:$stdin$stdout$stderr通过rb_define_virtual_variable注册了 getter/setter,并且被标记为 ractor 局部变量(rb_gvar_ractor_local),同时$>$stdout共享同一组 getter/setter。

$<($DEFAULT_INPUT)

指向「默认输入」的只读变量:若ARGF非空则指向ARGF,否则指向$stdin。英文别名:$DEFAULT_INPUT。正如 lib/English.rb 所注释的,它支持大量类似File对象的方法(getseach_linefilenamelinenoreadseek等),并且是只读的。

$>($DEFAULT_OUTPUT)

默认输出流,初始为$stdout,是Kernel#printKernel#printf的目的地。英文别名:$DEFAULT_OUTPUT(见 lib/English.rb)。在 io.c 中它与$stdout共用同一实现,因此给$stdout赋值会同步影响$>

$.($INPUT_LINE_NUMBER / $NR)

最近一次读取的流中的输入位置(行号),初始为 0,由某些读取方法重置。英文别名:$INPUT_LINE_NUMBER$NR

$_($LAST_READ_LINE)

最近一次读取的流返回的字符串行,初始为nilKernel中许多字符串相关方法默认以$_为操作对象。它在 lib/English.rb 中被注明是当前作用域局部、线程局部的变量;在 io.c 中,$_通过虚拟变量注册并被标记为 ractor 局部且动态装箱(rb_gvar_box_dynamic)。

六、进程信息:$0$*$$$?$LOAD_PATH$LOADED_FEATURES

$0($PROGRAM_NAME)

初始为正在执行的脚本名,可在程序中重新赋值(例如修改进程标题)。底层实现在 ruby.c:$0$PROGRAM_NAME都通过rb_define_hooked_variable挂到rb_progname上,setter 为set_arg0;同时Process.setproctitle也提供了等价能力(ruby.c)。

$*($ARGV)

指向 ARGV 数组(命令行参数),只读。英文别名:$ARGV。注意 lib/English.rb 的补充:这个数组是被 Ruby 解释器处理过参数之后的剩余部分(解释器自身的选项已被移除)。

$$($PROCESS_ID / $PID)

当前进程的进程 ID,与Process.pid相同,只读。在 process.c 中被标记为 ractor 局部变量。英文别名:$PROCESS_ID$PID

$?($CHILD_STATUS)

初始为nil,之后保存为最近一个退出的子进程创建的Process::Status对象;线程局部、只读。底层实现在 process.c:$?通过虚拟变量get_CHILD_STATUS读取,而子进程退出状态由rb_last_status_set(process.c)写入(例如rb_syswait等系统调用路径,见 process.c)。英文别名:$CHILD_STATUS

$LOAD_PATH$:$-I

Kernel#loadKernel#require搜索的路径数组,初始为 Ruby 默认值,只读(数组中元素本身冻结,见 load.c)。

它有一个非常实用的单例方法$LOAD_PATH.resolve_feature_path(feature)(实现于 load.c 的rb_resolve_feature_path),返回结果有三种:

  • [:rb, path]path是将被加载的 Ruby 文件路径;
  • [:so, path]path是将被加载的共享对象(.so)文件路径;
  • nil:不存在该 feature。

示例:

$LOAD_PATH.resolve_feature_path('timeout') # => [:rb, "/snap/ruby/317/lib/ruby/3.2.0/timeout.rb"] $LOAD_PATH.resolve_feature_path('date_core') # => [:so, "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/date_core.so"] $LOAD_PATH.resolve_feature_path('foo') # => nil

$LOADED_FEATURES$"

已加载文件的路径数组,初始为 Ruby 默认值,只读。在 load.c 中通过get_LOADED_FEATURES虚拟变量实现;require 成功加载新 feature 时会向其中追加路径(若数组被冻结则抛出错误,见 load.c)。

$LOADED_FEATURES.take(10) # => ["enumerator.so", "thread.rb", "fiber.so", "rational.so", "complex.so", "ruby2_keywords.rb", "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/encdb.so", "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/enc/trans/transdb.so", "/snap/ruby/317/lib/ruby/3.2.0/x86_64-linux/rbconfig.rb", "/snap/ruby/317/lib/ruby/3.2.0/rubygems/compatibility.rb"]

七、调试相关:$FILENAME$DEBUG$VERBOSE

$FILENAME

ARGF.filename方法的返回值,即当前正在读取的命令行文件名(命令行无文件参数时为'-'),只读

$DEBUG(别名$-d

  • 初始为true当且仅当命令行选项-d--debug被给出,否则为false
  • 可在运行程序中被设置为任意布尔值;
  • true时,每个被 raise 的异常都会被打印到$stderr。这一行为在 eval.c 与 ruby.c 中均有体现(RTEST(ruby_debug)判断)。

底层实现在 ruby.c:$DEBUG$-d共享debug_getter/debug_setter,并标记为 ractor 局部(ruby.c)。

$VERBOSE(别名$-v$-w

  • 初始为true当命令行选项-v-w被给出,否则为false
  • 可在运行程序中被设为任意值或nil
  • true时启用 Ruby 警告;
  • nil禁用警告,包括Kernel#warn的输出

底层实现在 ruby.c:$VERBOSE$-v$-w共享verbose_getter/verbose_setter,setter 会把任意真值归一化为Qtrue;另有$-W(只读)通过opt_W_getter将 verbose 状态映射为整数:nil → 0false → 1true → 2(ruby.c)。

八、其他变量:$-F$-a$-i$-l$-p$F

这组变量与命令行选项一一对应,其中$-p$-l$-a在 ruby.c 中被标记为 ractor 局部变量。

变量对应选项内容与说明
$-F$;-FString#split的默认字段分隔符,必须是 String 或 Regexp;除命令行选项外设置为非 nil 已被废弃。英文别名$FS$FIELD_SEPARATOR
$-a-a是否给出-a,只读
$-i-i-i选项给出的扩展名,无则nil;是ARGF.inplace_mode的别名
$-l-l是否设置-l,只读
$-p-p是否给出-p,只读
$F-a+-n/-p若给出-a,在每次-l/-p循环开始时把$_$-F拆分得到的数组赋给它

关于-F的字段分隔语义,doc/language/options.md 说明其参数即为split使用的输入字段分隔符。值得留意的是 lib/English.rb 还提供了$;的两个可读别名$FS$FIELD_SEPARATOR(原文档未列入 English 表,属于标准库额外提供的别名)。

九、已废弃的变量:$=$,

  • $=:原用于「是否区分大小写」的开关,早已废弃;
  • $,print/Array#join参数之间的输出分隔串,默认nil(不加任何文本),亦已废弃。标准库中仍保留了它的别名$OFS$OUTPUT_FIELD_SEPARATOR(见 lib/English.rb)。

十、预定义全局常量

流常量:STDINSTDOUTSTDERR

标准输入、输出、错误流,分别是$stdin$stdout$stderr的默认值:

STDIN # => #<IO:<STDIN>> STDOUT # => #<IO:<STDOUT>> STDERR # => #<IO:<STDERR>>

环境常量:ENVARGFARGVTOPLEVEL_BINDING

常量内容
ENV当前环境变量名与值的 Hash
ARGF命令行给出的多个文件的虚拟拼接(未给文件、给出"-"或全部读完后,回退到$stdin
ARGV给定的命令行参数数组(在 ruby.c 中通过rb_define_global_const("ARGV", rb_argv)定义)
TOPLEVEL_BINDING顶层作用域的 Binding
ENV.take(5) # => [["COLORTERM", "truecolor"], ["DBUS_SESSION_BUS_ADDRESS", "unix:path=/run/user/1000/bus"], ["DESKTOP_SESSION", "ubuntu"], ["DISPLAY", ":0"], ["GDMSESSION", "ubuntu"]] TOPLEVEL_BINDING # => #<Binding:0x00007f58da0da7c0>

版本常量:RUBY_*

这些常量描述当前 Ruby 解释器自身的版本与构建信息,全部由 version.c 提供:

常量内容示例(Ruby 3.2.2)
RUBY_VERSION版本号"3.2.2"
RUBY_RELEASE_DATE发布日期"2023-03-30"
RUBY_PLATFORM平台标识"x86_64-linux"
RUBY_PATCHLEVEL补丁级别(开发版为-153
RUBY_REVISIONGit 提交哈希"e51014f9c05aa65cbf203442d37fef7c12390015"
RUBY_COPYRIGHT版权字符串"ruby - Copyright (C) 1993-2023 Yukihiro Matsumoto"
RUBY_ENGINE实现名称"ruby"
RUBY_ENGINE_VERSION实现版本"3.2.2"
RUBY_DESCRIPTION完整描述"ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]"

从源码看,这些常量的数据源定义在 version.c:RUBY_REVISION在非发布构建中为"HEAD"ruby_versionruby_release_dateruby_platformruby_patchlevel分别是各常量的 C 层数据(version.c),而RUBY_DESCRIPTION则由这些片段拼装而成(version.c)。因此,不同构建环境(或开发版)下这些常量的实际值会与文档示例不同,这是正常的。

嵌入数据常量:DATA

DATA当且仅当程序中包含__END__行时被定义。此时DATA是一个 File 对象,内容为__END__之后的所有行,且读取位置定位在首行:

p DATA DATA.each_line { |line| p line } __END__ Foo Bar Baz

输出:

#<File:t.rb> "Foo\n" "Bar\n" "Baz\n"

这是 Ruby 脚本内嵌数据(self-contained data section)的经典用法,常用于小工具的测试数据或模板。

十一、速查总表

综合以上内容,全部预定义全局变量一览(含 English 别名):

类别变量English 别名只读
异常$!$ERROR_INFO
异常$@$ERROR_POSITION
匹配$~$LAST_MATCH_INFO
匹配$&$MATCH
匹配$`$PREMATCH
匹配$'$POSTMATCH
匹配$+$LAST_PAREN_MATCH
匹配$1$n
分隔符$/$-0$INPUT_RECORD_SEPARATOR$RS
分隔符$\$OUTPUT_RECORD_SEPARATOR$ORS
$stdin/$stdout/$stderr
$<$DEFAULT_INPUT
$>$DEFAULT_OUTPUT
$.$INPUT_LINE_NUMBER$NR
$_$LAST_READ_LINE
进程$0$PROGRAM_NAME
进程$*$ARGV
进程$$$PROCESS_ID$PID
进程$?$CHILD_STATUS
进程$LOAD_PATH$:$-I
进程$LOADED_FEATURES$"
调试$FILENAME
调试$DEBUG$-d
调试$VERBOSE$-v$-w
其他$-F$;$FS$FIELD_SEPARATOR
其他$-a
其他$-i
其他$-l
其他$-p
其他$F
废弃$=$,$OFS$OUTPUT_FIELD_SEPARATOR

如需进一步了解这些变量与命令行选项(-0-F-a-i-l-p-d-v-w)的对应关系,可查阅 doc/language/options.md;正则匹配相关变量的完整语义可参考 doc/language/regexp/methods.rdoc;英文别名的完整清单见 lib/English.rb。

【免费下载链接】rubyThe Ruby Programming Language项目地址: https://gitcode.com/GitHub_Trending/ru/ruby

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询