Kernel#Gets Attempts to Read File Instead of Standard Input

Kernel#gets attempts to read file instead of standard input

If there are arguments, the default gets method tries to treat the first one as a file and read from that. To read from $stdin in such a situation, you have to use it explicitly:

print "Say something! "
input = $stdin.gets

File Descriptor 0


read(0, buffer, 1024)

is a system call, a call into kernel code. The kernel's implementation of read will dispatch to the terminal (or pseudo-terminal) device driver, which will wait until you've either typed 1024 characters, a newline, or an EOF marker, Ctrl+D.

then that must mean shells line buffer their command's, right?

The buffering is performed in the terminal driver, if the terminal is set to the right mode. Otherwise, the program will just wait until 1024 bytes are entered.

Furthermore, why can we not use lseek() on standard input

You can if stdin is a regular file. You just can't seek on a terminal, because that would require the terminal driver to remember all data that passed through the terminal device since it was created.

wc command with no FILE argument (reading from standard input)

Quoting man 3 stdin:

       Under  normal circumstances every UNIX program has three streams opened
for it when it starts up, one for input, one for output, and one for
printing diagnostic or error messages. These are typically attached to
the user's terminal (see tty(4)) but might instead refer to files or
other devices, depending on what the parent process chose to set up.
(See also the "Redirection" section of sh(1).)

The input stream is referred to as "standard input"; the output stream
is referred to as "standard output"; and the error stream is referred
to as "standard error". These terms are abbreviated to form the sym‐
bols used to refer to these files, namely stdin, stdout, and stderr.

so basically when you type wc it is waiting for input, for example you can type bla followed by Ctrl+d and wc will terminate and print the stats:

wc
bla
1 1 4

same result as:

echo 'bla' | wc 
1 1 4

in this case stdout of echo command is sent to stdin of wc

from man 7 pipe:

       pipe()  creates  a pipe, a unidirectional data channel that can be used
for interprocess communication. Data written to the write end of the pipe is buffered by the
kernel until it is read from the read end of the pipe.

Difference between $stdin and others gets.chomp

When invoking gets without specifying the stream, we are invoking Kernel#gets and when stream is specified we are invoking IO#gets

I believe Kernel#gets wraps around IO#gets under the hood but before it needs to create an IO object, that IO object will be a concatenation of all command line arguments(ARGV) ( it will try to open every single one as file in positional order) and them read from it. If there are no elements left on ARGV it will then read from stdin.

That behavior can be tested with this simple snippet:

while gets
puts $_
end

When running ruby myscript.rb without parameters it will read stdin, if parameters are specified it will try to open as files and read from it, it will works similarly to cat command .

That stream could be tty, socket, file etc:

stream = File.open('/etc/passwd','r')

puts stream.gets

stream.close

A simple http client using sockets:

require 'socket'

s = TCPSocket.new 'icanhazip.com',80

s.puts <<~EOF
GET / HTTP/1.1
Host: icanhazip.com

EOF

while line = s.gets
puts line
end

s.close

More about Kernel#gets here

More about IO#gets

Confused about stdin, stdout and stderr?

Standard input - this is the file handle that your process reads to get information from you.

Standard output - your process writes conventional output to this file handle.

Standard error - your process writes diagnostic output to this file handle.

That's about as dumbed-down as I can make it :-)

Of course, that's mostly by convention. There's nothing stopping you from writing your diagnostic information to standard output if you wish. You can even close the three file handles totally and open your own files for I/O.

When your process starts, it should already have these handles open and it can just read from and/or write to them.

By default, they're probably connected to your terminal device (e.g., /dev/tty) but shells will allow you to set up connections between these handles and specific files and/or devices (or even pipelines to other processes) before your process starts (some of the manipulations possible are rather clever).

An example being:

my_prog <inputfile 2>errorfile | grep XYZ

which will:

  • create a process for my_prog.
  • open inputfile as your standard input (file handle 0).
  • open errorfile as your standard error (file handle 2).
  • create another process for grep.
  • attach the standard output of my_prog to the standard input of grep.

Re your comment:

When I open these files in /dev folder, how come I never get to see the output of a process running?

It's because they're not normal files. While UNIX presents everything as a file in a file system somewhere, that doesn't make it so at the lowest levels. Most files in the /dev hierarchy are either character or block devices, effectively a device driver. They don't have a size but they do have a major and minor device number.

When you open them, you're connected to the device driver rather than a physical file, and the device driver is smart enough to know that separate processes should be handled separately.

The same is true for the Linux /proc filesystem. Those aren't real files, just tightly controlled gateways to kernel information.

Why is gets throwing an error when arguments are passed to my ruby script?

Ruby will automatically treat unparsed arguments as filenames, then open and read the files making the input available to ARGF ($<). By default, gets reads from ARGF. To bypass that:

$stdin.gets

It has been suggested that you could use STDIN instead of $stdin, but it's usually better to use $stdin.

Additionally, after you capture the input you want from ARGV, you can use:

ARGV.clear

Then you'll be free to gets without it reading from files you may not have intended to read.



Related Topics



Leave a reply



Submit