gets.chomp VS $stdin.gets.chomp
Published on 02 Dec 2016
by Alexander Garber
Whilst working on Exercise 15 of Learn Ruby the Hard Way, the author prompted me to ponder the difference between gets.chomp and $stdin.gets.chomp.
The simplest explanation I found for the difference between gets.chomp and $stdin.gets.chomp is this, from Stack Overflow:
The simplest explanation I found for the difference between gets.chomp and $stdin.gets.chomp is this, from Stack Overflow:
gets.chomp() = read ARGV firstA further clarification:
STDIN.gets.chomp() = read user's input
because if there is stuff in ARGV, the default gets method tries to treat the first one as a file and read from that. To read from the user's input (i.e., stdin) in such a situation, you have to use it STDIN.gets explicitly.
Source Code from Exercise 15 of Learn Ruby the Hard Way:
Take note that I put gets.chomp and $stdin.gets.chomp and uncommented the one I wanted to test.
# Get the input file
filename = ARGV.first
# Declare a variable to open the input file
txt = open(filename)
# Output the contents of the input file
puts "Here's your file #{filename}"
print txt.read
# Ask the user to name the input file, implicitly in the local directory
print "Type the filename again: "
# Use stdin to obtain the name of the input file from the local directory
file_again = $stdin.gets.chomp
# file_again = gets.chomp
# Declare another variable to open the input file again
txt_again = open(file_again)
# Output the contents of the input file again
print txt_again.read
To this I added two text files for testing:
- ex_15_sample.txt
- ex_15_sample_2.txt
ex_15_sample.txt:
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
ex_15_sample_2.txt
THIS IS STUFF I TYPED INTO A FILE.
IT IS REALLY COOL STUFF.
LOTS AND LOTS OF FUN TO HAVE IN HERE.
Scenario 1: $stdin.gets.chomp
Script:
# Get the input file
filename = ARGV.first
# Declare a variable to open the input file
txt = open(filename)
# Output the contents of the input file
puts "Here's your file #{filename}"
print txt.read
# Ask the user to name the input file, implicitly in the local directory
print "Type the filename again: "
# Use stdin to obtain the name of the input file from the local directory
file_again = $stdin.gets.chomp
# file_again = gets.chomp
# Declare another variable to open the input file again
txt_again = open(file_again)
# Output the contents of the input file again
print txt_again.read
Command in terminal:
$ ruby ex15.rb ex_15_sample.txt
Output:
Here's your file ex_15_sample.txt
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
Type the filename again: ex_15_sample_2.txt
THIS IS STUFF I TYPED INTO A FILE.
IT IS REALLY COOL STUFF.
LOTS AND LOTS OF FUN TO HAVE IN HERE.
Scenario 2: gets.chomp
Script:
# Get the input file
filename = ARGV.first
# Declare a variable to open the input file
txt = open(filename)
# Output the contents of the input file
puts "Here's your file #{filename}"
print txt.read
# Ask the user to name the input file, implicitly in the local directory
print "Type the filename again: "
# Use stdin to obtain the name of the input file from the local directory
# file_again = $stdin.gets.chomp
file_again = gets.chomp
# Declare another variable to open the input file again
txt_again = open(file_again)
# Output the contents of the input file again
print txt_again.read
Command in terminal:
$ ruby ex15.rb ex_15_sample.txt
Output:
Here's your file ex_15_sample.txt
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
Type the filename again: ex15.rb:20:in `initialize': No such file or directory @ rb_sysopen - This is stuff I typed into a file. (Errno::ENOENT)
from ex15.rb:20:in `open'
from ex15.rb:20:in `<main>'
Let's look a little more closely at what went wrong.
The first part of the script took the argument from the commandline and read the contents of the file ex_15_sample.txt -- great!
Then it printed "Type the filename again." -- again, that's what we want.
However, when it got to gets.chomp, it attempted to take as an input the first line of the contents of the file denoted by ARGV.
Let's look at the contents of the text file in question:
ex_15_sample.txt:
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
Do you see what Ruby tried to do here? gets.chomp already has the filename from ARGV, but instead of reading the filename, it reads the first line of the file.
Scenario 3: gets.chomp with a hacked input file
If gets.chomp take the first line of the file named by ARGV, what would happen if I hack the text file and put the name of the second file in the first line?
ex_15_sample_hacked.txt:
ex_15_sample_2.txt
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
Script:
# Get the input file
filename = ARGV.first
# Declare a variable to open the input file
txt = open(filename)
# Output the contents of the input file
puts "Here's your file #{filename}"
print txt.read
# Ask the user to name the input file, implicitly in the local directory
print "Type the filename again: "
# Use stdin to obtain the name of the input file from the local directory
# file_again = $stdin.gets.chomp
file_again = gets.chomp
# Declare another variable to open the input file again
txt_again = open(file_again)
# Output the contents of the input file again
print txt_again.read
Command in terminal:
$ ruby ex15.rb ex_15_sample_hacked.txt
Output:
Here's your file ex_15_sample_hacked.txt
ex_15_sample_2.txt
This is stuff I typed into a file.
It is really cool stuff.
Lots and lots of fun to have in here.
Type the filename again: THIS IS STUFF I TYPED INTO A FILE.
IT IS REALLY COOL STUFF.
LOTS AND LOTS OF FUN TO HAVE IN HERE.
Observations:
- The first part of the script read the input file from ARGV.
- The second part of the script read as input the first line of the input file from ARGV.
Conclusions
- If you want to prompt the user for input, use $stdin.gets.chomp.
- If you want to trick the program into using the first line of the first file supplied to ARGV, gets.chomp is an option, but a better way to do it would simply be to declare the variable in your code.
all tags
activerecord android annoyances api apt arch array artix atom az3w backend bash blog browser bug callback career cli cloud code coding config configuration cp crud css database db design devops django email erp filter fugitive gif gist git gnome grep hebrew http ide isbn-fetcher iso javascript job search js kanban kanban\ kindle koans linux logger manjaro map markdown microservices mobi mtp neovim nodejs packages pastbin patch post python rails reduce refactoring rest routes rspec ruby scripting security sed shell sql string_replacement study tdd terminal testing version_control vim walkthrough workflow