2008-11-07 37 views
286

Tôi muốn để đối phó với dòng đầu vào lệnh trong Ruby:Các phương pháp hay nhất với STDIN trong Ruby?

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

cách tốt nhất để làm điều đó là gì? Đặc biệt tôi muốn đối phó với STDIN trống, và tôi hy vọng cho một giải pháp thanh lịch.

#!/usr/bin/env ruby 

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

ARGV.each do |b| 
    puts b 
end 
+5

Chỉ cần một lưu ý nhỏ: hai dòng lệnh đầu tiên bạn cho là giống hệt nhau từ quan điểm của 'myprog.rb': 'cái INPUT.TXT 'tập tin được gắn vào _stdin_; vỏ quản lý điều này cho bạn. – Mei

+5

^^ điều này thường được gọi là "sử dụng mèo vô dụng", bạn sẽ thấy điều đó rất nhiều. –

+16

@SteveKehlet tuy nhiên tôi tin rằng nó được khéo léo gọi là "lạm dụng mèo" – OneChillDude

Trả lời

28

Tôi không khá chắc chắn những gì bạn cần, nhưng tôi sẽ sử dụng một cái gì đó như thế này:

#!/usr/bin/env ruby 

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

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

Lưu ý rằng vì argv mảng trống trước gets đầu tiên, Ruby sẽ không cố gắng để giải thích lập luận dưới dạng tệp văn bản để đọc (hành vi được kế thừa từ Perl).

Nếu stdin trống hoặc không có đối số, không có gì được in.

trường hợp thử nghiệm ít:

$ 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

Cái gì đó như có lẽ đây?

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

Ví dụ:

> 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

stdin không cần phải là văn bản. Notorius không phải văn bản là ví dụ một số loại nén/giải nén. (each_line là loại chỉ chuẩn bị cho ascii). each_byte có thể? – Jonke

375

Sau đây là một số điều tôi tìm thấy trong bộ sưu tập của tôi che khuất Ruby.

Vì vậy, trong Ruby, một không-chuông đơn giản thực hiện các lệnh Unix cat sẽ là:

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

ARGF là bạn của bạn khi nói đến đầu vào; nó là một tập tin ảo nhận tất cả các đầu vào từ các tập tin có tên hoặc tất cả từ 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 

Cảm ơn lòng tốt, chúng tôi không có nhà điều hành kim cương trong Ruby, nhưng chúng tôi đã thay thế ARGF. Mặc dù tối nghĩa, nó thực sự trở nên hữu ích. Hãy xem xét chương trình này, mà prepends tiêu đề bản quyền tại chỗ (nhờ một Perlism, -i) cho mỗi tập tin được đề cập trên dòng lệnh:

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

tín dụng đối với:

+11

ARGF là con đường để đi. Nó được xây dựng theo cách của Ruby để xử lý các tập tin và stdin trong một thời trang toàn diện. – Pistos

+1

(nhìn thấy điều này và suy nghĩ của bạn) lại những khoản tín dụng: http://blog.nicksieger.com/articles/2007/10/06/obscure-and-ugly-perlisms-in-ruby – deau

+0

Đó là rất tốt đẹp. Ngày của tôi sẽ được hoàn thành nếu có một mô hình tốt đẹp để mô phỏng cách AWK hoạt động (với không hoặc interlocution tối thiểu). :-) – will

38

Ruby cung cấp một cách khác để xử lý STDIN: Cờ -n. Nó xử lý toàn bộ chương trình của bạn như đang ở bên trong một vòng lặp trên STDIN, (bao gồm các tệp được truyền dưới dạng dòng lệnh args). Xem ví dụsau kịch bản 1-line:

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

Ba-parter shebang '#!/Usr/bin/env ruby ​​-n' sẽ không hoạt động, vì" ruby ​​-n "sẽ được chuyển tới/usr/bin/env làm đối số duy nhất. [Xem câu trả lời này] (http://stackoverflow.com/a/4304187) để biết thêm chi tiết. Tập lệnh ** sẽ ** hoạt động nếu chạy với 'ruby -n script.rb' một cách rõ ràng. – artm

+2

Tính năng shebaing bao gồm '-n' hoạt động trên hộp Mac OS X 10.8 của tôi. – jdizzle

+3

@jdizzle: Nó hoạt động trên OSX, nhưng không phải trên Linux - và đó chính xác là vấn đề: nó không phải là _portable_. – mklement0

11
while STDIN.gets 
    puts $_ 
end 

while ARGF.gets 
    puts $_ 
end 

này được lấy cảm hứng từ Perl:

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

Vâng, để đơn giản và dễ đọc! Ôi không, chờ đã, '$ _' là gì vậy? Vui lòng sử dụng [tiếng Anh] (http://www.ruby-doc.org/stdlib-2.0/libdoc/English/rdoc/English.html) trên Stack Overflow! – nus

1

Tôi sẽ thêm rằng để sử dụng ARGF với các thông số, bạn cần xóa ARGV trước khi gọi ARGF.each. Điều này là do ARGF sẽ xử lý mọi thứ trong ARGV làm tên tệp và đọc các dòng từ đó trước tiên.

Dưới đây là một ví dụ 'tee' thực hiện:

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

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

tôi làm điều gì đó như thế này:

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

Dường như hầu hết các câu trả lời được giả định các đối số được tên tập tin chứa nội dung được cat'd để stdin. Dưới đây mọi thứ được coi là đối số. Nếu STDIN là từ TTY, thì nó bị bỏ qua.

$ cat tstarg.rb 

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

Đối số hoặc tiêu chuẩn có thể trống hoặc có dữ liệu.

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

Nhanh chóng và đơn giản:

STDIN.gets.chomp == 'YES'

Các vấn đề liên quan