Le migliori pratiche con STDIN in Ruby?


307

Voglio occuparmi dell'input della riga di comando in Ruby:

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

Qual'è il miglior modo di farlo? In particolare, voglio occuparmi di STDIN vuoto e spero in una soluzione elegante.

#!/usr/bin/env ruby

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

ARGV.each do |b|
    puts b
end

5
Solo una piccola nota: le prime due righe di comando fornite sono esattamente le stesse dal punto di vista di myprog.rb: il input.txtfile è allegato a stdin ; la shell lo gestisce per te.
Mei,

6
^^ questo viene spesso definito "uso inutile del gatto", lo vedrai molto.
Steve Kehlet

18
@SteveKehlet tuttavia credo che sia più abilmente definito "abuso di gatto"
OneChillDude

Risposte:


403

Di seguito sono riportate alcune cose che ho trovato nella mia collezione di oscuri Ruby.

Quindi, in Ruby, una semplice implementazione senza comandi del comando Unix catsarebbe:

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

ARGFè tuo amico quando si tratta di input; è un file virtuale che ottiene tutti gli input dai file con nome o tutti da 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

Per fortuna non abbiamo ottenuto l'operatore di diamanti in Ruby, ma abbiamo ottenuto ARGFin sostituzione. Sebbene oscuro, in realtà si rivela utile. Considera questo programma, che antepone le intestazioni del copyright sul posto (grazie a un altro Perlismo -i) a ogni file menzionato sulla riga di 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.
#++

Credito a:


12
ARGF è la strada da percorrere. È costruito da Ruby per gestire file e stdin in modo completo.
Pistos

1
(visto questo e pensato a te) in merito a quei crediti: blog.nicksieger.com/articles/2007/10/06/…
deau

È molto carino. La mia giornata sarà completa se c'è un bel modello per simulare il modo in cui funziona AWK (con interlocuzione zero o minima). :-)
sarà il

Forse dovresti notare che idxsarà il "numero di riga" nel file virtuale che concatena tutti gli input, piuttosto che il numero di riga per ogni singolo file.
Alec Jacobson,

Nota questa #!/usr/bin/env ruby -ilinea non funziona su Linux: stackoverflow.com/q/4303128/735926
bfontaine

43

Ruby fornisce un altro modo di gestire STDIN: il flag -n. Tratta l'intero programma come all'interno di un loop su STDIN, (inclusi i file passati come argomenti della riga di comando). Vedi ad esempio il seguente script di 1 riga:

#!/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
Lo shebang a tre parti #!/usr/bin/env ruby -nnon funzionerà, poiché "ruby -n" verrà passato a / usr / bin / env come unico argomento. Vedi questa risposta per maggiori dettagli. Lo script verrà funziona se eseguito con ruby -n script.rbesplicito.
artm

5
@jdizzle: funziona su OSX, ma non su Linux - e questo è esattamente il problema: non è portatile .
mklement0

32

Non sono sicuro di cosa tu abbia bisogno, ma userei qualcosa del genere:

#!/usr/bin/env ruby

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

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

Si noti che poiché l'array ARGV è vuoto prima gets, Ruby non tenterà di interpretare l'argomento come file di testo da cui leggere (comportamento ereditato da Perl).

Se lo stdin è vuoto o non ci sono argomenti, non viene stampato nulla.

Pochi casi di test:

$ 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

Qualcosa del genere forse?

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

Esempio:

> 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 non ha bisogno di essere testo. Notorius non testo è ad esempio una sorta di compressione / decompressione. (each_line è una specie di preparazione solo per ascii). each_byte forse?
Jonke,

12
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

Questo è ispirato a Perl:

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

4
Inferno sì, per semplicità e leggibilità! Oh no, aspetta, cos'è '$ _'? Si prega di utilizzare l' inglese su Stack Overflow!


1

Aggiungerò che per usare i ARGFparametri, devi cancellare ARGVprima di chiamare ARGF.each. Questo perché ARGFtratterà qualsiasi cosaARGV come un nome file e leggeranno prima le righe da lì.

Ecco un esempio di implementazione 'tee':

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

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

1

Faccio qualcosa del genere:

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

0

Sembra che la maggior parte delle risposte ipotizzino che gli argomenti siano nomi di file che contengono contenuti da associare allo stdin. Sotto tutto viene trattato come un semplice argomento. Se STDIN proviene dal TTY, viene ignorato.

$ cat tstarg.rb

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

Gli argomenti o lo stdin possono essere vuoti o contenere dati.

$ cat numbers 
1
2
3
4
5
$ ./tstarg.rb a b c < numbers
a
b
c
1
2
3
4
5
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.