Programming Workshop 2 (CSCI 1061U)
Faculty of Science, Ontario Tech University
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:
stdin – keyboardstdout – terminal (any messages intended for terminal
or console.)stderr – error messages (terminal)stdout to
a fileUse commands > and >>.
output > filenameTruncates 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.
ls > directory-listing.txtoutput >> filenameCreates 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.
ls > directory-listing.txt; ls >> directory-listing.txt:: > filename: is a dummy placeholder that produces no output. The
effect of this command is similar to touch filename.
: > foo.txtThese are often used within scripts to send single line outputs to a file.
1> filenameSends stdout to filename. Similarly use
1>>.
2> filenameSends stderr to filename. Similarly use
2>>.
&> filenameSends both stdout and stderr to
filename. Similarly use &>>.
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.txtFile descriptor 7 now points to file
foo.txt.
M>N and
M>&NM>NOutput from file descriptor M goes to filename
N.
M>&NOutput from file descriptor M goes to file descriptor
N.
2>&1Redirects stderr to stdout, i.e., error
messages are also sent out to stdout.
i>&jRedirects output of file descriptor i to file descriptor j.
>&j All output to stdout will no go to file descriptor j.
stdinprogram < filenameContents 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 rWhen 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.
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.
This is a general purpose command chaining tool. It is similar to
>.
cat notes.txt | my-prog > output.txtCommand 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.
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:
Create an input file
input1.txt
2 4Create an output file
output1.txt
6The output file contains the correct answer.
Execute the program as follows
> ./sum < input.txt > program-output.txtNow compare the output of the program with the expected output.
> diff output1.txt program-output.txtIf diff commands returns nothing, all is well. Otherwise
there is an issue that needs attention.
Consider the following situation
$ myprog < in.txt > out.txtmyprog 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 myprogThe 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.
The program reads the file specified on the command line and prints its contents.
$ ./myprog story.txtThe 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.txtor
$ cat story.txt | ./myprogThe 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