2008-11-07 31 views
286

我要處理的紅寶石在命令行輸入:Ruby中STDIN的最佳做法?

> 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

只是一個小提示:你給第一個兩行命令正是從myprog.rb`的`的觀點一樣:`input.txt中`文件附加到_stdin_; shell爲你管理這個。 – Mei 2011-07-12 15:52:49

+5

^^這通常被稱爲「無用的貓」,你會看到很多。 – 2012-03-09 00:22:20

+16

@SteveKehlet但我認爲它更巧妙地稱爲「虐貓」 – OneChillDude 2014-08-13 19:26:35

回答

28

我不太清楚你需要什麼,但我會用這樣的:

#!/usr/bin/env ruby 

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

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

注意,因爲argv數組是前第一gets空,紅寶石不會試圖解釋的說法作爲從中讀取的文本文件(從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! 
15

像這樣的事情吧?

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

標準輸入不需要是文本。 Notorius不是文本,例如某種壓縮/解壓縮。 (each_line只是爲ascii做準備)。每個字節可能是什麼? each_byte也許? – Jonke 2008-11-07 22:17:20

375

以下是一些事情,我在我的收藏晦澀的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 

謝天謝地,我們沒有得到紅寶石鑽石運營商,但我們沒有得到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. 
#++ 

感謝:

+11

ARGF是要走的路。 Ruby的構建方式是以全面的方式處理文件和標準輸入。 – Pistos 2008-11-08 17:08:38

38

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 
11
while STDIN.gets 
    puts $_ 
end 

while ARGF.gets 
    puts $_ 
end 

這是由Perl的啓發:

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

我要補充的是,爲了使用ARGF與參數,你需要在調用之前清除ARGVARGF.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 
1

這似乎是最答案假設參數被包含內容的文件名被cat'd到標準輸入。在一切之下都被視爲只是論點。如果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 
0

快速而簡單:

STDIN.gets.chomp == 'YES'