RubyでのSTDINのベストプラクティス


307

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

5
ただ、マイナー注:あなたが与える最初の2本のコマンドラインは、正確性の観点から同じですmyprog.rbinput.txtファイルが添付されてSTDIN ; シェルがこれを管理します。
Mei

6
^^これはしばしば「猫の無駄な使用」と呼ばれ、あなたはそれをたくさん見るでしょう。
Steve Kehlet、2012年

18
@SteveKehletしかし、より巧妙に「猫虐待」と呼ばれていると私は信じています
OneChillDude '08 / 08/13

回答:


403

以下は、あいまいな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でdiamond演算子を取得できませんでしたが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.
#++

クレジット:


12
ARGFは進むべき道です。Rubyは、ファイルとstdinをオールラウンドに処理するために組み込まれています。
ピストス

1
これらのクレジット再(のこぎり本とあなたの思い)blog.nicksieger.com/articles/2007/10/06/...
deau

とてもいいです。AWKの動作をシミュレートするための素晴らしいパターン(ゼロ間または最小限の対話)があれば、1日は終わりです。:-)
意志

おそらくidx、個々のファイルの行番号ではなく、すべての入力を連結した仮想ファイルの「行番号」になることに注意してください。
Alec Jacobson

この注意#!/usr/bin/env ruby -i:行は、Linux上で動作しませんstackoverflow.com/q/4303128/735926
bfontaine

43

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

8
#!/usr/bin/env ruby -n「ruby -n」が唯一の引数として/ usr / bin / envに渡されるため、3つの部分からなるシバンは機能しません。詳細については、この回答を参照してください。スクリプトがされますと実行した場合に動作しruby -n script.rb、明示的に。
artm

5
@jdizzle:OSXでは機能しますが、Linuxでは機能しません-それがまさに問題です。移植性がありません。
mklement0

32

あなたが何を必要としているのかよくわかりませんが、私は次のようなものを使用します:

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

18

おそらくこのような何か?

#/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はテキストである必要はありません。ノートではなくテキストは、たとえば、ある種の圧縮/圧縮解除です。(each_lineは、ASCIIの準備だけの一種です)。each_byte多分?
Jonke、

12
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

これはPerlに触発されたものです。

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

4
地獄ええ、シンプルさと読みやすさのために!ああ、待って、その「$ _」は何ですか?Stack Overflowで英語を使用してください!


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

1

私はこのようなことをします:

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

0

ほとんどの答えは、引数が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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.