Best practices with STDIN in Ruby? [closed]

I want to deal with the command line input in Ruby:

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

What is the best way to do it? In particular I want to deal with blank STDIN, and I hope for an elegant solution.

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

10 Answers

Following are some things I found in my collection of obscure Ruby.

So, in Ruby, a simple no-bells implementation of the Unix command cat would be:

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

ARGF is your friend when it comes to input; it is a virtual file that gets all input from named files or all from 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 

Thank goodness we didn’t get the diamond operator in Ruby, but we did get ARGF as a replacement. Though obscure, it actually turns out to be useful. Consider this program, which prepends copyright headers in-place (thanks to another Perlism, -i) to every file mentioned on the command-line:

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

Credit to:

6

Ruby provides another way to handle STDIN: The -n flag. It treats your entire program as being inside a loop over STDIN, (including files passed as command line args). See e.g. the following 1-line script:

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

I am not quite sure what you need, but I would use something like this:

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

Note that because ARGV array is empty before first gets, Ruby won't try to interpret argument as text file from which to read (behaviour inherited from Perl).

If stdin is empty or there is no arguments, nothing is printed.

Few test cases:

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

Something like this perhaps?

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

Example:

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

This is inspired by Perl:

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

Quick and simple:

STDIN.gets.chomp == 'YES'

I'll add that in order to use ARGF with parameters, you need to clear ARGV before calling ARGF.each. This is because ARGF will treat anything in ARGV as a filename and read lines from there first.

Here's an example 'tee' implementation:

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

I do something like this :

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

You can also use STDIN.each_line, and STDIN.each_line.to_a to get it as an array.

e.g.

STDIN.each_line do |line| puts line end 

It seems most answers are assuming the arguments are filenames containing content to be cat'd to the stdin. Below everything is treated as just arguments. If STDIN is from the TTY, then it is ignored.

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

Either arguments or stdin can be empty or have data.

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

You Might Also Like