【问题标题】:Best practices with STDIN in Ruby? [closed]Ruby 中 STDIN 的最佳实践? [关闭]
【发布时间】:2010-09-21 08:34:04
【问题描述】:

我想处理 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

【问题讨论】:

  • 请注意:从myprog.rb 的角度来看,您给出的前两个命令行完全相同:input.txt 文件附加到 stdin; shell 会为你管理这个。
  • ^^ 这通常被称为“无用的猫”,你会看到很多。
  • @SteveKehlet 但我认为它更巧妙地称为“虐待猫”

标签: ruby stdin


【解决方案1】:

以下是我在不知名的 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.
#++

归功于:

【讨论】:

  • ARGF 是要走的路。它是 Ruby 内置的一种全方位处理文件和标准输入的方式。
  • (看到这个并想到你)重新获得这些学分:blog.nicksieger.com/articles/2007/10/06/…
  • 这很好。如果有一个很好的模式来模拟 AWK 的工作方式(零或最少的对话),那么我的一天将是完整的。 :-)
  • 也许应该注意idx 将是连接所有输入的虚拟文件中的“行号”,而不是每个单独文件的行号。
  • 请注意,#!/usr/bin/env ruby -i 行在 Linux 上不起作用:stackoverflow.com/q/4303128/735926
【解决方案2】:

您还可以使用STDIN.each_lineSTDIN.each_line.to_a 将其作为数组获取。

例如

STDIN.each_line do |line|
  puts line
end

【讨论】:

    【解决方案3】:

    快速简单:

    STDIN.gets.chomp == 'YES'

    【讨论】:

      【解决方案4】:

      似乎大多数答案都假设参数是包含要添加到标准输入的内容的文件名。下面的一切都被视为只是参数。如果 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
      

      【讨论】:

        【解决方案5】:

        我做这样的事情:

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

        【讨论】:

          【解决方案6】:

          我要补充一点,为了将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
          

          【讨论】:

            【解决方案7】:
            while STDIN.gets
              puts $_
            end
            
            while ARGF.gets
              puts $_
            end
            

            这是受 Perl 启发的:

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

            【讨论】:

            • 地狱,是的,为了简单和可读性!哦不,等等,'$_' 是什么?请在 Stack Overflow 上使用English
            【解决方案8】:

            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
            

            【讨论】:

            • 三部分 shebang #!/usr/bin/env ruby -n 将不起作用,因为“ruby -n”将作为唯一参数传递给 /usr/bin/env。 See this answer 了解更多详情。如果显式使用ruby -n script.rb 运行,脚本工作。
            • @jdizzle:它适用于 OSX,但不适用于 Linux - 这正是问题所在:它不是便携
            【解决方案9】:

            大概是这样的吧?

            #/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
            

            【讨论】:

            • stdin 不需要是文本。 Notorius 不是文本,例如某种压缩/解压缩。 (each_line 只是为 ascii 做准备)。每个字节可能吗?
            【解决方案10】:

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

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

            【讨论】:

              猜你喜欢
              • 2013-10-02
              • 2010-10-06
              • 2011-10-28
              • 2010-09-10
              • 2011-10-11
              • 1970-01-01
              • 2014-04-26
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多