Praktik terbaik dengan STDIN di Ruby?

307

Saya ingin berurusan dengan input baris perintah di Ruby:

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

Apa cara terbaik untuk melakukannya? Secara khusus saya ingin berurusan dengan STDIN kosong, dan saya berharap untuk solusi yang elegan.

#!/usr/bin/env ruby

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

ARGV.each do |b|
    puts b
end
griflet
sumber
5
Hanya sebuah catatan kecil: dua baris perintah pertama yang Anda berikan persis sama dari sudut pandang myprog.rb: input.txtfile dilampirkan ke stdin ; shell mengelola ini untuk Anda.
Mei
6
^^ ini sering disebut sebagai "penggunaan kucing yang tidak berguna", Anda akan sering melihatnya.
Steve Kehlet
18
@SteveKehlet namun saya percaya itu lebih cerdik disebut sebagai "penyalahgunaan kucing"
OneChillDude

Jawaban:

403

Berikut adalah beberapa hal yang saya temukan dalam koleksi Ruby yang tidak jelas.

Jadi, di Ruby, implementasi no-bells sederhana dari perintah Unix catadalah:

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

ARGFadalah teman Anda dalam hal input; itu adalah file virtual yang mendapatkan semua input dari file bernama atau semua dari 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

Syukurlah kami tidak mendapatkan operator berlian di Ruby, tetapi kami memang mendapatkan ARGFsebagai penggantinya. Meski tidak jelas, ternyata itu bermanfaat. Pertimbangkan program ini, yang menambahkan header hak cipta di tempat (berkat Perlism lain, -i) ke setiap file yang disebutkan pada baris perintah:

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

Kredit untuk:

Jonke
sumber
12
ARGF adalah jalan yang harus ditempuh. Ini dibangun Ruby untuk menangani file dan stdin secara menyeluruh.
Pistos
1
(Melihat ini dan memikirkan Anda) adalah kredit itu: blog.nicksieger.com/articles/2007/10/06/…
deau
Itu sangat bagus. Hari saya akan lengkap jika ada pola yang bagus untuk mensimulasikan cara kerja AWK (dengan interlokasi nol atau minimal). :-)
akan
Mungkin harus dicatat bahwa idxakan menjadi "nomor baris" dalam file virtual yang menggabungkan semua input, bukan nomor baris untuk setiap file individu.
Alec Jacobson
Perhatikan #!/usr/bin/env ruby -ibaris ini tidak berfungsi di Linux: stackoverflow.com/q/4303128/735926
bfontaine
43

Ruby menyediakan cara lain untuk menangani STDIN: Bendera -n. Ini memperlakukan seluruh program Anda sebagai berada di dalam loop di atas STDIN, (termasuk file yang dilewatkan sebagai argumen baris perintah). Lihat misalnya skrip 1-baris berikut:

#!/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
Bill Caputo
sumber
8
Shebang tiga bagian #!/usr/bin/env ruby -ntidak akan berfungsi, karena "ruby -n" akan diteruskan ke / usr / bin / env sebagai satu-satunya argumen. Lihat jawaban ini untuk lebih jelasnya. Script akan bekerja jika dijalankan ruby -n script.rbsecara eksplisit.
artm
5
@ jdizzle: Ini berfungsi di OSX, tetapi tidak di Linux - dan itulah masalahnya: ini tidak portabel .
mklement0
32

Saya tidak yakin apa yang Anda butuhkan, tetapi saya akan menggunakan sesuatu seperti ini:

#!/usr/bin/env ruby

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

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

Perhatikan bahwa karena array ARGV kosong sebelum pertama gets , Ruby tidak akan mencoba menafsirkan argumen sebagai file teks untuk dibaca (perilaku diwarisi dari Perl).

Jika stdin kosong atau tidak ada argumen, tidak ada yang dicetak.

Beberapa kasus uji:

$ 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!
Damir Zekić
sumber
18

Mungkin sesuatu seperti ini?

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

Contoh:

> 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
Magnus Holm
sumber
stdin tidak perlu berupa teks. Notorius not text adalah misalnya semacam kompres / uncompress. (Each_line agak hanya mempersiapkan untuk ascii). Each_byte mungkin?
Jonke
12
while STDIN.gets
  puts $_
end

while ARGF.gets
  puts $_
end

Ini terinspirasi oleh Perl:

while(<STDIN>){
  print "$_\n"
}
texasbruce
sumber
4
Sial ya, untuk kesederhanaan dan keterbacaan! Oh tidak, tunggu, apa itu '$ _'? Silakan gunakan bahasa Inggris di Stack Overflow!
3

Cepat dan sederhana:

STDIN.gets.chomp == 'YES'

Jose Alban
sumber
1

Saya akan menambahkan itu untuk digunakan ARGFdengan parameter, Anda harus menghapus ARGVsebelum memanggil ARGF.each. Ini karena ARGFakan memperlakukan apa pun ARGVsebagai nama file dan membaca baris dari sana terlebih dahulu.

Berikut ini contoh penerapan 'tee':

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

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

Saya melakukan sesuatu seperti ini:

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

Tampaknya sebagian besar jawaban dengan asumsi argumen adalah nama file yang berisi konten yang akan di cat ke stdin. Di bawah ini semua diperlakukan hanya sebagai argumen. Jika STDIN dari TTY, maka diabaikan.

$ cat tstarg.rb

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

Argumen atau stdin dapat kosong atau memiliki data.

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