IO Redirection

Programming Workshop 2 (CSCI 1061U)

Faisal Qureshi

Faculty of Science, Ontario Tech University

http://vclab.science.uoit.ca


Unix Bash Shell allows I/O redirection via <, >> and > commands. Redirection allows us to send output from a file, command, program, or script to another file, command, program, or script. In Unix the following three files are always open:

Redirecting stdout to a file

Use commands > and >>.

Command >

output  > filename

Truncates file “filename” to zero-length; if file doesn’t exist, create a zero-length file. All stdout commands, such as printf, cout, etc. will write to the file “filename”. Here output refers to any output from a file, command, program, or script.

Example

ls > directory-listing.txt

Command >>

output >> filename

Creates a zero-legnth file if one doesn’t already exist. If a file exists, contents will be appended to it. All stdout commands, such as printf, cout, etc. will write to the file “filename”. Here output refers to any output from a file, command, program, or script.

Example

ls > directory-listing.txt; ls >> directory-listing.txt

Other uses

Using :

: > filename

: is a dummy placeholder that produces no output. The effect of this command is similar to touch filename.

Example

: > foo.txt

Single-line IO redirections

These are often used within scripts to send single line outputs to a file.

1> filename

Sends stdout to filename. Similarly use 1>>.

2> filename

Sends stderr to filename. Similarly use 2>>.

&> filename

Sends both stdout and stderr to filename. Similarly use &>>.

File descriptors

We can use <> to open a file for reading and writing and assign a file descriptor to it

echo newton > foo.txt
exec 7<> foo.txt

File descriptor 7 now points to file foo.txt.

Cases M>N and M>&N

M>N

Output from file descriptor M goes to filename N.

M>&N

Output from file descriptor M goes to file descriptor N.

Examples
2>&1

Redirects stderr to stdout, i.e., error messages are also sent out to stdout.

i>&j

Redirects output of file descriptor i to file descriptor j.

>&j 

All output to stdout will no go to file descriptor j.

Redirecting file contents to stdin

Command <

program < filename

Contents in the file are provided to stdin functions (scanf, cin, etc.) in the program.

Consider the following program (prog.cpp).

#include <iostream>
using namespace std;

int main()
{
    int n;
    char c;

    cout << "Enter an integer: ";
    cin >> n;
    cout << "Enter a char: ";
    cin >> c;
    
    cout << "integer is " << n << " and character is " << c << endl;
    
    return 0;
}

Assume we have the following file (input.txt)

232 r

When we execute the program as follows, the output is:

$ g++ prog.cpp -o prog
$ ./prog < input.txt
Enter an integer: 
Enter a char:
integer is 232 and character is r
$

Notice that the user didn’t have to enter an integer or a character. Despite the fact that these items needed to be entered via stdin–since cin was used to read it. Instead the < command tied file “input.txt” to the stdin. Consequently, cin in the program took the required values (an integer and a character) from this file.

This is a great way to automate program testing. Since user doesn’t have to respond to any prompts from the program.

Commandline arguments

Note that ./prog < input.txt doesn’t mean that < and input.txt are passed as command line arguments. If the intention is to pass these items as command line arguments then use the following command ./prog \< input.txt. The backslash character \ is the escape character that we use to tell the Bash shell that < should be treated as a character and not as a means to redirect a file to stdin.

Using Unix pipe (|) command

This is a general purpose command chaining tool. It is similar to >.

cat notes.txt | my-prog > output.txt

Command cat reads the contents of notes.txt file and sends these to my-prog. The output of my-prog is sent to the output.txt file.

Using IO redirection for automatic testing

Consider the following piece of code that sums two numbers and prints the result.

#include <iostream>

using namespace std;

int main()
{
    int a, b;
    cin >> a >> b;
    cout << a+b;
    return;
}

We can test this program by executing it and entering two numbers. The program prints the output and if the output is what we expect, we assume that the program is correct. We can automate this process as follows:

Step 1

Create an input file

input1.txt

2 4

Step 2

Create an output file

output1.txt

6

The output file contains the correct answer.

Step 3

Execute the program as follows

> ./sum < input.txt > program-output.txt

Step 4

Now compare the output of the program with the expected output.

> diff output1.txt program-output.txt

If diff commands returns nothing, all is well. Otherwise there is an issue that needs attention.

How to read in the entire contents of a file of unknown size?

Consider the following situation

$ myprog < in.txt > out.txt

myprog reads in in.txt, processes its contents, and writes the result to out.txt. In this case, we do not know that size of in.txt. Through io redirection the contents of in.txt file will be available from std::cin. Similarly, any items written out to std::cout will be sent to file out.txt. The key question is when to stop reading from std::cin. The program should read everything from in.txt. Once the contents in in.txt have been exhausted, it shouldn’t attempt to read anything more from std::cin.

Check out the code below that attempts to implement the functionality of unix cat command.

// myprog.cpp
//
// Faisal Qureshi
//
// Use g++ -std=c++11 myprog.cpp
#include <iostream>
#include <fstream>
#include <future>
#include <thread>
#include <chrono>

static bool checkStream()
{    
    char c;
    std::cin >> c;
    std::cin.putback(c);
    return true;
}

int main(int argc, char** argv)
{
    if (argc == 2) {
        // Case 1
        // If reading from a file (commandline argument)

        std::ifstream fs;
        fs.open(argv[1]);
        if (fs.is_open()) {
            char buf[5];

            while(1) {
                fs.read(buf, 4);
                int ncount = fs.gcount();
                if (fs.gcount() > 0) {
                    buf[ncount] = '\0';
                    std::cout << buf;
                }
                if (fs.eof()) {
                    break;
                }
            }

            fs.close();
        }   
    }
    else {
        // Case 2 and 3
        // If reading from cin (io redirection)

        // Painful
        std::chrono::microseconds timeout(1000);
        bool check = false; 
        std::future<bool> future = std::async(checkStream);
        if (future.wait_for(timeout) == std::future_status::ready)
            check = future.get();

        if (!check) {
            // Case 2
            exit(0);
        }

        // Less painful
        // Case 2
        std::cin >> std::ws;
        do {
            char c = std::cin.peek();
            if (std::cin.eof()) {
                break;
            }

            std::cin.read(&c, 1);
            std::cout << c;
        } while (true);        
    }

    return 0;
}

Note that this file needs to be compiled using the following command

$ g++ -std=c++11 myprog.cpp -o myprog

The painful bits of this file is due to the fact that there is no way to check if cin has some data that can be read without blocking. In theory, cin::peek() should be non-blocking, but it isn’t. So we use threading mechanism to attempt to read from cin. This allows the system to note block. Note that this program can be invoked as follows.

Case 1

The program reads the file specified on the command line and prints its contents.

$ ./myprog story.txt

Case 2

The program reads from cin, which has items that can be read. Through IO redirection, OS has attached the file story.txt to cin.

$ ./myprog < story.txt

or

$ cat story.txt | ./myprog

Case 3

The program reads from cin, which has no items that can be read. Without threadhing (the painful bit), the program will wait forever for something to appear at cin.

$ ./myprog

References