Melhores práticas com STDIN em Ruby?

Eu quero lidar com a input de linha de comando no Ruby:

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

Qual é a melhor maneira de fazer isso? Em particular, quero lidar com STDIN em branco e espero por uma solução elegante.

 #!/usr/bin/env ruby STDIN.read.split("\n").each do |a| puts a end ARGV.each do |b| puts b end 

A seguir estão algumas coisas que encontrei na minha coleção de Ruby obscuro.

Então, em Ruby, uma implementação simples do comando Unix cat seria:

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

ARGF é sua amiga quando se trata de input; é um arquivo virtual que recebe todas as inputs de arquivos nomeados ou todos de 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 

Graças a Deus nós não conseguimos o operador de diamante em Ruby, mas conseguimos ARGF como um substituto. Embora obscuro, na verdade acaba sendo útil. Considere este programa, que acrescenta headers de copyright no local (graças a outro Perlism, -i ) para cada arquivo mencionado na linha de comando:

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

Crédito para:

Ruby fornece outra maneira de manipular STDIN: O sinalizador -n. Ele trata todo o seu programa como estando dentro de um loop sobre STDIN, (incluindo arquivos passados ​​como argumentos de linha de comando). Veja, por exemplo, o seguinte roteiro de 1 linha:

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

Eu não tenho certeza do que você precisa, mas eu usaria algo assim:

 #!/usr/bin/env ruby until ARGV.empty? do puts "From arguments: #{ARGV.shift}" end while a = gets puts "From stdin: #{a}" end 

Observe que, como a matriz ARGV está vazia antes da primeira obtenção, o Ruby não tentará interpretar o argumento como um arquivo de texto a partir do qual ler (comportamento herdado do Perl).

Se stdin estiver vazio ou não houver argumentos, nada será impresso.

Poucos casos de teste:

 $ 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! 

Algo assim talvez?

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

Exemplo:

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

Isto é inspirado por Perl:

 while(){ print "$_\n" } 

Eu adicionarei isso para usar o ARGF com parâmetros, você precisa limpar o ARGV antes de chamar o ARGF.each . Isso ocorre porque o ARGF tratará qualquer coisa no ARGV como um nome de arquivo e lerá as linhas de lá primeiro.

Aqui está um exemplo de implementação ‘tee’:

 File.open(ARGV[0], 'w') do |file| ARGV.clear ARGF.each do |line| puts line file.write(line) end end 

Eu faço algo assim:

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

Parece que a maioria das respostas está assumindo que os argumentos são nomes de arquivo contendo conteúdo a ser enviado para o stdin. Abaixo tudo é tratado apenas como argumentos. Se STDIN é do TTY, então é ignorado.

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

Os argumentos ou stdin podem estar vazios ou ter dados.

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

Rápido e simples:

STDIN.gets.chomp == 'YES'