Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lazy fun: Make unary minus silence stderr for backticks

1 view
Skip to first unread message

Sam Stephenson

unread,
Nov 6, 2005, 6:33:35 PM11/6/05
to
I was reading about mentalguy's lazy.rb and thought it'd be fun to use
lazy evaluation as a vehicle for adding new syntactic sugar to Ruby.
Here's one such example, which lets you prefix backticks with a unary
minus to silence stderr:

| % irb -r lazy_backtick
| irb(main):001:0> `hello`
| /opt/local/lib/ruby/1.8/irb/workspace.rb:81: command not found: hello
| => ""
| irb(main):002:0> -`hello`
| => ""

lazy_backtick.rb:

| class LazyBacktick < String
| instance_methods.each {|m| undef_method(m) unless m =~ /^__/}
|
| def initialize(command)
| @command, @silence_stderr = command, false
| end
|
| def -@
| @swallow_stderr = true
| value
| end
|
| def method_missing(method, *arguments, &block)
| value.send(method, *arguments, &block)
| end
|
| private
| def value
| @value ||= evaluate
| end
|
| def evaluate
| real_backtick "#@command#{' 2>/dev/null' if @silence_stderr}"
| end
| end
|
| module Kernel
| alias_method :real_backtick, :'`'
|
| def `(command)
| LazyBacktick.new(command)
| end
| end

Note that I'm using the same approach as BlankSlate to remove
LazyBacktick's methods, but inheriting from String instead -- this is
so "String === `foo`" doesn't break. (It would be nice to have a mixin
alternative to BlankSlate.)

The way I'm silencing stderr is a bit, um, agile. Can it be made to
work in Ruby on Windows without loading an external library (since
open3 doesn't work there)?

Sam


Sam Stephenson

unread,
Nov 6, 2005, 6:36:11 PM11/6/05
to
On 11/6/05, Sam Stephenson <sstep...@gmail.com> wrote:
> | def -@
> | @swallow_stderr = true
> | value
> | end

Sorry, make that @silence_stderr.


Sean O'Halpin

unread,
Nov 6, 2005, 7:20:15 PM11/6/05
to
On 11/6/05, Sam Stephenson <sstep...@gmail.com> wrote:
> The way I'm silencing stderr is a bit, um, agile. Can it be made to
> work in Ruby on Windows without loading an external library (since
> open3 doesn't work there)?

On Windows, you could use 2>nul: instead of 2> /dev/null or
win32-open3 (see http://raa.ruby-lang.org/project/win32-open3/)

Regards,

Sean


Sam Stephenson

unread,
Nov 6, 2005, 7:33:22 PM11/6/05
to

I knew about win32-open3 but not about 2>:nul. Thanks for the tip Sean!

Sam


Sean O'Halpin

unread,
Nov 6, 2005, 7:39:38 PM11/6/05
to
On 11/7/05, Sam Stephenson <sstep...@gmail.com> wrote:
> I knew about win32-open3 but not about 2>:nul. Thanks for the tip Sean!
Be careful where you put the colon! It's 2>nul: not 2>:nul. Win/DOS
has a few pseudo-devices that use the same basic syntax as drive
letters (A:, C:) such as NUL:, CON: and AUX:

Regards,
Sean


Mauricio Fernández

unread,
Nov 6, 2005, 7:46:06 PM11/6/05
to
On Mon, Nov 07, 2005 at 08:33:35AM +0900, Sam Stephenson wrote:
> The way I'm silencing stderr is a bit, um, agile. Can it be made to
> work in Ruby on Windows without loading an external library (since
> open3 doesn't work there)?

batsman@tux-chan:/tmp$ cat test.rb
require 'rbconfig'
def nostderr
old = STDERR.dup
# unsure about the next line
file = (::Config::CONFIG["arch"] =~ /dos|win32|mingw/) ? "NUL" : "/dev/null"
STDERR.reopen(file)
STDERR.sync = true # you want this so `hello3` still outputs something below...
yield
ensure
STDERR.reopen(old)
end

`hello`
nostderr { `hello2`; $stderr.puts "foo" }
`hello3`
batsman@tux-chan:/tmp$ ruby test.rb
test.rb:2: command not found: hello
test.rb:2: command not found: hello3


--
Mauricio Fernandez


Sam Stephenson

unread,
Nov 6, 2005, 8:07:15 PM11/6/05
to
On 11/6/05, Sean O'Halpin <sean.o...@gmail.com> wrote:

My mistake. I'm full of typos tonight. Thanks again.

Sam


Sam Stephenson

unread,
Nov 6, 2005, 8:11:36 PM11/6/05
to
On 11/6/05, Mauricio Fernández <m...@acm.org> wrote:
> batsman@tux-chan:/tmp$ cat test.rb
> require 'rbconfig'
> def nostderr
> old = STDERR.dup
> # unsure about the next line
> file = (::Config::CONFIG["arch"] =~ /dos|win32|mingw/) ? "NUL" : "/dev/null"
> STDERR.reopen(file)
> STDERR.sync = true # you want this so `hello3` still outputs something below...
> yield
> ensure
> STDERR.reopen(old)
> end
>
> `hello`
> nostderr { `hello2`; $stderr.puts "foo" }
> `hello3`
> batsman@tux-chan:/tmp$ ruby test.rb
> test.rb:2: command not found: hello
> test.rb:2: command not found: hello3

Interesting, I didn't know that reopening STDERR would affect
subshells. It's more code but I think I prefer this approach.

Sam


men...@rydia.net

unread,
Nov 6, 2005, 8:37:25 PM11/6/05
to
Quoting Sam Stephenson <sstep...@gmail.com>:

> I was reading about mentalguy's lazy.rb and thought it'd be fun
> to use lazy evaluation as a vehicle for adding new syntactic
> sugar to Ruby.

Hmm, what you've got here doesn't look quite like lazy evaluation,
exactly. More like Delegator, except way better.

Glad to be inspiring such cool things though!

-mental


Florian Groß

unread,
Nov 6, 2005, 9:01:26 PM11/6/05
to
Sam Stephenson wrote:

> I was reading about mentalguy's lazy.rb and thought it'd be fun to use
> lazy evaluation as a vehicle for adding new syntactic sugar to Ruby.
> Here's one such example, which lets you prefix backticks with a unary
> minus to silence stderr:
>
> | % irb -r lazy_backtick
> | irb(main):001:0> `hello`
> | /opt/local/lib/ruby/1.8/irb/workspace.rb:81: command not found: hello
> | => ""
> | irb(main):002:0> -`hello`
> | => ""

Nice hack, but here's a case that doesn't work and for that I don't have
a fix:

String.new(`echo foo`)

Which returns "" for me.

I guess you are better off not inheriting from String and letting the
String === test fail. Nowadays it's all about duck typing, anyway.

Caleb Clausen

unread,
Nov 7, 2005, 10:36:32 AM11/7/05
to
On 11/6/05, Sam Stephenson <sstep...@gmail.com> wrote:
> (It would be nice to have a mixin alternative to BlankSlate.)

I hacked up a BlankSlate module for my own purposes. (This version doesn't
permenently remove any methods, just renames them to something.... unusual.)

Here it is:

module BlankSlate;
module ClassMethods
def restore(*names)
names.each{|name| alias_method name, "##{name}"}
end
def hide(*names)
names.each do|name|
undef_method name if instance_methods.include?(name.to_s)
end
end
end

def BlankSlate.included(othermod)
othermod.instance_eval {
instance_methods.each { |m|
alias_method "##{m}", m #archive m
undef_method m unless m =~ /^__/ || m=='instance_eval'
}
extend BlankSlate::ClassMethods
}
end
end


0 new messages