Лучшие практики с STDIN в Ruby? - PullRequest
295 голосов
/ 07 ноября 2008

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

Ответы [ 9 ]

390 голосов
/ 08 ноября 2008

Ниже приведены некоторые вещи, которые я нашел в своей коллекции малоизвестного Руби.

Таким образом, в 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.
#++

Кредит:

40 голосов
/ 03 марта 2011

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
31 голосов
/ 07 ноября 2008

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

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

Если стандартный ввод пуст или аргументов нет, ничего не печатается.

Несколько тестовых случаев:

$ 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 голосов
/ 07 ноября 2008

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

#/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
11 голосов
/ 10 июля 2012
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

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

while(<STDIN>){
  print "$_\n"
}
1 голос
/ 11 ноября 2016

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

STDIN.gets.chomp == 'YES'

1 голос
/ 31 марта 2016

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

all_lines = ""
ARGV.each do |line|
  all_lines << line + "\n"
end
puts all_lines
1 голос
/ 20 января 2016

Я добавлю, что для использования 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
0 голосов
/ 12 октября 2016

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