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 > 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.
ls > directory-listing.txt
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.
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.txt
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 &>>
.
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
.
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
.
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.
stdin
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;
<< "Enter an integer: ";
cout >> n;
cin << "Enter a char: ";
cout >> c;
cin
<< "integer is " << n << " and character is " << c << endl;
cout
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.
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.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.
Consider the following piece of code that sums two numbers and prints the result.
#include <iostream>
using namespace std;
int main()
{
int a, b;
>> a >> b;
cin << a+b;
cout 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 4
Create an output file
output1.txt
6
The output file contains the correct answer.
Execute the program as follows
> ./sum < input.txt > program-output.txt
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.
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;
.open(argv[1]);
fsif (fs.is_open()) {
char buf[5];
while(1) {
.read(buf, 4);
fsint ncount = fs.gcount();
if (fs.gcount() > 0) {
[ncount] = '\0';
bufstd::cout << buf;
}
if (fs.eof()) {
break;
}
}
.close();
fs}
}
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)
= future.get();
check
if (!check) {
// Case 2
(0);
exit}
// 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.
The program reads the file specified on the command line and prints its contents.
$ ./myprog story.txt
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
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