318

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

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

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

#!/usr/bin/env ruby

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

ARGV.each do |b|
    puts b
end
4

10 回答 10

416

以下是我在我的晦涩 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了替代品。尽管晦涩难懂,但事实证明它很有用。-i考虑这个程序,它在命令行中提到的每个文件中都预先添加了版权标题(感谢另一个 Perlism, ):

#!/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.
#++

归功于:

于 2008-11-07T22:29:13.043 回答
44

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

#!/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
于 2011-03-03T03:13:07.673 回答
33

我不太确定你需要什么,但我会使用这样的东西:

#!/usr/bin/env ruby

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

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

请注意,因为 ARGV 数组在 first 之前为空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!
于 2008-11-07T19:51:40.020 回答
19

大概是这样的?

#/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
于 2008-11-07T20:53:14.127 回答
13
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

This is inspired by Perl:

while(<STDIN>){
  print "$_\n"
}
于 2012-07-09T21:52:58.373 回答
4

快速简单:

STDIN.gets.chomp == 'YES'

于 2016-11-11T12:00:50.837 回答
1

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

这是一个示例“三通”实现:

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

  ARGF.each do |line|
    puts line
    file.write(line)
  end
end
于 2016-01-20T17:15:32.050 回答
1

您也可以使用STDIN.each_line, 和STDIN.each_line.to_a将其作为数组获取。

例如

STDIN.each_line do |line|
  puts line
end
于 2020-07-07T21:22:54.840 回答
1

我做这样的事情:

all_lines = ""
ARGV.each do |line|
  all_lines << line + "\n"
end
puts all_lines
于 2016-03-31T10:19:40.317 回答
0

似乎大多数答案都假设参数是包含要添加到标准输入的内容的文件名。下面的一切都被视为只是参数。如果 STDIN 来自 TTY,则将其忽略。

$ cat tstarg.rb

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

参数或标准输入可以为空或有数据。

$ cat numbers 
1
2
3
4
5
$ ./tstarg.rb a b c < numbers
a
b
c
1
2
3
4
5
于 2016-10-12T13:01:00.520 回答