Thực hành tốt nhất với STDIN trong Ruby?


307

Tôi muốn xử lý đầu vào dòng lệnh 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 đưa ra hoàn toàn giống nhau theo quan điểm của myprog.rb: input.txttệp được đính kèm vào stdin ; Shell quản lý điều này cho bạn.
Mei

6
^^ đ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.
Steve Kehlet

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

Câu trả lời:


403

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

Vì vậy, trong Ruby, việc triển khai lệnh Unix đơn giản catsẽ là:

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

ARGFlà bạn của bạn khi nói đến đầu vào; nó là một tệp ảo nhận tất cả đầu vào từ các tệp được đặt 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 trời, chúng tôi đã không nhận được nhà điều hành kim cương trong Ruby, nhưng chúng tôi đã ARGFthay thế. Mặc dù tối nghĩa, nó thực sự có ích. Hãy xem xét chương trình này, vốn ưu tiên các tiêu đề bản quyền tại chỗ (nhờ vào một chủ nghĩa Perlism khác -i) cho mỗi tệp đượ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 để:


12
ARGF là con đường để đi. Đó là Ruby được xây dựng để xử lý các tập tin và stdin một cách toàn diện.
Pistos

1
(cưa này và nghĩ tới bạn) lại những khoản tín dụng: blog.nicksieger.com/articles/2007/10/06/...
deau

Điều đó thật tuyệt. Một ngày của tôi sẽ hoàn tất nếu có một mô hình đẹp để mô phỏng cách thức hoạt động của AWK (với độ xen kẽ bằng 0 hoặc tối thiểu). :-)
sẽ

Có lẽ nên lưu ý rằng đó idxsẽ là "số dòng" trong tệp ảo nối tất cả các đầu vào, thay vì số dòng cho mỗi tệp riêng lẻ.
Alec Jacobson

Lưu ý #!/usr/bin/env ruby -idòng này không hoạt động trên Linux: stackoverflow.com/q/4303128/735926
bfontaine

43

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ư nằm 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ụ: tập lệnh 1 dòng sau:

#!/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
Shebang ba parter #!/usr/bin/env ruby -nsẽ không hoạt động, vì "ruby -n" sẽ được chuyển đến / usr / bin / env như là đối số duy nhất. Xem câu trả lời này để biết thêm chi tiết. Kịch bản sẽ hoạt động nếu chạy với ruby -n script.rbrõ ràng.
artm

5
@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 khả dụng .
mkuity0

32

Tôi không 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ì mảng ARGV trống trước tiên gets, Ruby sẽ không cố diễn giải đối số 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.

Vài trường hợp kiểm tra:

$ 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

Một cái gì đó như thế này có lẽ?

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

Thí 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

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

12
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

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

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

4
Chết tiệt, vì đơn giản và dễ đọc! Ồ không, chờ đã, '$ _' đó là gì? Vui lòng sử dụng tiếng Anh trên Stack Overflow!


1

Tôi sẽ thêm rằng để sử dụng ARGFvới các tham số, bạn cần xóa ARGVtrước khi gọi ARGF.each. Điều này là bởi vì ARGFsẽ coi bất cứ điều gì ARGVdưới dạng tên tệp và đọc các dòng từ đó trước tiên.

Dưới đây là một ví dụ về triển khai 'tee':

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 một cái gì đó như thế này:

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

0

Có vẻ như hầu hết các câu trả lời đều cho rằng các đối số là tên tệp chứa nội dung sẽ được chuyển sang stdin. Dưới đây mọi thứ được coi là chỉ 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 stdin 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.