Лучшие практики с STDIN в Ruby?

Я хочу иметь дело с вводом командной строки в 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

Ответ 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.
#++

Кредит для:

Ответ 2

Ruby предоставляет другой способ обработки STDIN: флаг -n. Он рассматривает всю вашу программу как находящуюся внутри цикла над STDIN (включая файлы, переданные как аргументы командной строки). См. следующая 1 строка script:

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

Ответ 3

Я не совсем уверен, что вам нужно, но я бы использовал что-то вроде этого:

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

Ответ 4

Что-то вроде этого возможно?

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

Ответ 5

while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

Это вдохновляет Perl:

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

Ответ 6

Быстро и просто:

STDIN.gets.chomp == 'YES'

Ответ 7

Я добавлю, что для использования ARGF с параметрами вам нужно очистить ARGV до вызова ARGF.each. Это связано с тем, что ARGF будет обрабатывать что-либо в ARGV как имя файла и сначала читать строки.

Вот пример реализации "tee":

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

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

Ответ 8

Я делаю что-то вроде этого:

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

Ответ 9

Кажется, что большинство ответов предполагают, что аргументами являются имена файлов, содержащие контент для catdd для 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