Ruby中STDIN的最佳实践?

我想在Ruby中处理命令行输入:

> cat input.txt | myprog.rb
> myprog.rb < input.txt
> myprog.rb arg1 arg2 arg3 ...

最好的方法是什么?我特别想处理空白的STDIN,我希望有一个优雅的解决方案。

#!/usr/bin/env ruby


STDIN.read.split("\n").each do |a|
puts a
end


ARGV.each do |b|
puts b
end
207715 次浏览

我不太清楚你需要什么,但我会用这样的东西:

#!/usr/bin/env ruby


until ARGV.empty? do
puts "From arguments: #{ARGV.shift}"
end


while a = gets
puts "From stdin: #{a}"
end

注意,因为ARGV数组在第一个gets之前是空的,Ruby不会尝试将参数解释为要读取的文本文件(行为继承自Perl)。

如果stdin为空或没有参数,则不打印任何内容。

很少的测试用例:

$ cat input.txt | ./myprog.rb
From stdin: line 1
From stdin: line 2


$ ./myprog.rb arg1 arg2 arg3
From arguments: arg1
From arguments: arg2
From arguments: arg3
hi!
From stdin: hi!

也许是这样的?

#/usr/bin/env ruby


if $stdin.tty?
ARGV.each do |file|
puts "do something with this file: #{file}"
end
else
$stdin.each_line do |line|
puts "do something with this line: #{line}"
end
end

例子:

> cat input.txt | ./myprog.rb
do something with this line: this
do something with this line: is
do something with this line: a
do something with this line: test
> ./myprog.rb < input.txt
do something with this line: this
do something with this line: is
do something with this line: a
do something with this line: test
> ./myprog.rb arg1 arg2 arg3
do something with this file: arg1
do something with this file: arg2
do something with this file: arg3

以下是我在收藏的一些不知名的Ruby代码中发现的一些东西。

因此,在Ruby中,Unix命令cat的简单无铃实现将是:

#!/usr/bin/env ruby
puts ARGF.read

当涉及到输入时,ARGF是你的朋友;它是一个虚拟文件,从命名文件或STDIN中获取所有输入。

ARGF.each_with_index do |line, idx|
print ARGF.filename, ":", idx, ";", line
end


# print all the lines in every file passed via command line that contains login
ARGF.each do |line|
puts line if line =~ /login/
end

谢天谢地,我们在Ruby中没有得到菱形操作符,但我们得到了ARGF作为替换。虽然晦涩难懂,但它实际上是有用的。考虑一下这个程序,它在命令行中提到的每个文件前预先添加了版权头文件(感谢另一个Perlism, -i):

#!/usr/bin/env ruby -i


Header = DATA.read


ARGF.each_line do |e|
puts Header if ARGF.pos - e.length == 0
puts e
end


__END__
#--
# Copyright (C) 2007 Fancypants, Inc.
#++

信贷:

Ruby提供了另一种处理STDIN的方法:-n标志。它将整个程序视为在STDIN上的循环中(包括作为命令行参数传递的文件)。参见以下一行脚本:

#!/usr/bin/env ruby -n


#example.rb


puts "hello: #{$_}" #prepend 'hello:' to each line from STDIN


#these will all work:
# ./example.rb < input.txt
# cat input.txt | ./example.rb
# ./example.rb input.txt
while STDIN.gets
puts $_
end


while ARGF.gets
puts $_
end

这是受到Perl的启发:

while(<STDIN>){
print "$_\n"
}

我要补充一点,为了带参数使用ARGF,你需要在调用ARGF.each之前清除ARGV。这是因为ARGF将把ARGV中的任何内容视为文件名,并首先从那里读取行。

这是一个'tee'实现的例子:

File.open(ARGV[0], 'w') do |file|
ARGV.clear


ARGF.each do |line|
puts line
file.write(line)
end
end

我是这样做的:

all_lines = ""
ARGV.each do |line|
all_lines << line + "\n"
end
puts all_lines

似乎大多数答案都假设参数是包含要添加到stdin的内容的文件名。以下的一切都被视为只是论点。如果STDIN来自TTY,那么它将被忽略。

$ cat tstarg.rb


while a=(ARGV.shift or (!STDIN.tty? and STDIN.gets) )
puts a
end

参数或stdin都可以为空或有数据。

$ cat numbers
1
2
3
4
5
$ ./tstarg.rb a b c < numbers
a
b
c
1
2
3
4
5

简单快捷:

STDIN.gets.chomp == 'YES'

你也可以使用STDIN.each_lineSTDIN.each_line.to_a来获取数组。

如。

STDIN.each_line do |line|
puts line
end