2008-11-07 17 views
286

Quiero hacer frente a la entrada de línea de comandos en Ruby:¿Buenas prácticas con STDIN en Ruby?

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

¿Cuál es la mejor manera de hacerlo? En particular, quiero tratar con STDIN en blanco, y espero una solución elegante.

#!/usr/bin/env ruby 

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

ARGV.each do |b| 
    puts b 
end 
+5

Sólo una pequeña nota: las dos primeras líneas de comando que das son exactamente los mismos desde el punto de vista de 'myprog.rb': el' entrada.txt 'archivo está adjunto a _stdin_; el caparazón maneja esto para ti. – Mei

+5

^^ esto a menudo se conoce como "uso inútil del gato", lo verá mucho. –

+16

@SteveKehlet, sin embargo, creo que se conoce más astutamente como "abuso de gatos" – OneChillDude

Respuesta

28

No estoy muy seguro de lo que necesita, pero me gustaría utilizar algo como esto:

#!/usr/bin/env ruby 

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

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

Tenga en cuenta que debido tabla ARGV está vacía antes del primer gets, Ruby no va a tratar de interpretar el argumento como archivo de texto desde el cual leer (comportamiento heredado de Perl).

Si stdin está vacío o no hay argumentos, no se imprime nada.

pocos casos de prueba:

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

algo como esto, tal vez?

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

Ejemplo:

> 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 
+0

stdin no tiene que ser texto. Notorius not text es, por ejemplo, algún tipo de compresión/descompresión. (each_line solo se está preparando para ascii). each_byte tal vez? – Jonke

375

A continuación se presentan algunas cosas que encontré en mi colección de Rubí oscuro.

Así, en Ruby, una sencilla aplicación no-campanas del comando Unix cat sería:

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

ARGF es su amigo cuando se trata de la entrada; es un archivo virtual que obtiene todas las entradas de archivos con nombre o todo desde 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 

Gracias a Dios que no tuvimos el operador de diamante en Ruby, pero conseguimos ARGF como un reemplazo. Aunque oscuro, en realidad resulta útil. Considere este programa, que antepone los encabezados de derechos de autor en el lugar (gracias a otra Perlism, -i) para cada archivo mencionado en la línea de comandos:

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

+11

ARGF es el camino a seguir. Es el modo construido por Ruby para manejar archivos y stdin de forma general. – Pistos

+1

(vio esto y pensó en usted) re esos créditos: http://blog.nicksieger.com/articles/2007/10/06/obscure-and-ugly-perlisms-in-ruby – deau

+0

Eso es muy bueno. Mi día estará completo si hay un patrón agradable para simular la forma en que AWK funciona (con cero o mínima interlocución). :-) – will

38

Ruby proporciona otra manera de manejar STDIN: El distintivo -n. Trata todo su programa como dentro de un bucle sobre STDIN, (incluidos los archivos pasados ​​como args de línea de comando). Ver p.la siguiente secuencia de comandos de línea 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 
+7

El shebang de tres partes '#!/Usr/bin/env ruby ​​-n' no funcionará, ya que" ruby ​​-n "se pasará a/usr/bin/env como único argumento. [Ver esta respuesta] (http://stackoverflow.com/a/4304187) para más detalles. El script ** funcionará ** si se ejecuta con 'ruby -n script.rb' explícitamente. – artm

+2

El shebaing incluido '-n' funciona en mi caja Mac OS X 10.8. – jdizzle

+3

@jdizzle: Funciona en OSX, pero no en Linux, y ese es exactamente el problema: no es _portable_. – mklement0

11
while STDIN.gets 
    puts $_ 
end 

while ARGF.gets 
    puts $_ 
end 

Esto se inspira en Perl:

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

Infierno sí, por simplicidad y reabilidad! Oh no, espera, ¿qué es ese '$ _'? Utilice [inglés] (http://www.ruby-doc.org/stdlib-2.0/libdoc/English/rdoc/English.html) en Stack Overflow. – nus

1

voy a añadir que con el fin de utilizar ARGF con parámetros, es necesario borrar ARGV antes de llamar ARGF.each. Esto es porque ARGF tratará cualquier cosa en ARGV como un nombre de archivo y leerá líneas desde allí primero.

He aquí un ejemplo 'tee' aplicación:

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

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

hago algo como esto:

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

Parece mayoría de las respuestas están asumiendo los argumentos son los nombres de archivo que contienen contenido que se va a cat'd el stdin. Debajo de todo se trata solo como argumentos. Si STDIN es del TTY, entonces se ignora.

$ cat tstarg.rb 

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

Cualquiera de los argumentos o stdin pueden estar vacíos o tener datos.

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

rápida y sencilla:

STDIN.gets.chomp == 'YES'

Cuestiones relacionadas