简体   繁体   中英

In Ruby, how do sub, gsub (and other text methods) in shell one-liners work without referring to an object?

I saw this piece of code somewhere on the web:

ruby -pe 'gsub /^\s*|\s*$/, ""'

Evidently this piece of code removes leading and trailing whitespace from each line from STDIN.

I understand the regex and replacement, no problem, but what I don't get is how the method gsub is receiving an object to act upon. I understand that the -p flag wraps this whole thing in a while gets; print; ... ; end while gets; print; ... ; end while gets; print; ... ; end block, but how does gsub receive the string to act upon ? At the very least, shouldn't it be a $_.gsub(..) instead? How does the current input line get "magically" passed to gsub ?

Does the code in these Perl-like one-liners get interpreted in a somewhat different manner? I'm looking for a general idea of the differences from traditional, script-based Ruby code. Haven't found a comprehensive set of resources on this, I'm afraid.

It turns out that this is an instance method defined on Kernel, which magically gets turned on only when you use the -p or -n flag.

ruby -pe 'puts method(:gsub);'

#<Method: Object(Kernel)#gsub>

See the documentation here .

Other magical methods I found are chop , print , and sub .

The magical methods are all sent to $_ implicitly.

Easy:

class Object
  def gsub(*args, &block)
    $_.gsub(*args, &block)
  end
end

Since every object is an instance of Object (well, almost every object), every object has a gsub method now. So, you can call

some_object.gsub('foo', 'bar')

on any object, and it will just work. And since it doesn't matter what object you call it on, because it doesn't actually do anything with that object, you might just as well call it on self :

self.gsub('foo', 'bar')

Of course, since self is the implicit receiver, this is the same as

gsub('foo', 'bar')

For methods such as this, which don't actually depend on the receiver, and are only added to the Object class for convenience reasons, it is a common convention to make them private so that you cannot accidentally call them with an explicit receiver and then somehow get confused into thinking that this method does something to the receiver.

Also, it is common to put such methods (which are actually intended to be used more like procedures than methods , ie completely independent of their receiver) into the Kernel mixin, which is mixed into Object instead of directly into the Object class to distinguish them from methods that are available to every object but actually do depend on its internal state, such as Object#class , Object#to_s etc.

module Kernel
  private

  def gsub(*args, &block)
    $_.gsub(*args, &block)
  end
end

Other methods that are defined in this way, which you may have come across already are require , load , puts , print , p , gets , loop , raise , rand , throw , catch , lambda , proc , eval , Array , Integer , Float etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM