Figure 1: Piping makeupper with 2 other processes
Introduction
A typical Linux (or UNIX) terminal application, like cat command, provides three ways to input data:
This article demonstrates how to create a small application (makeupper) that allows those three ways of inputting data.
Note: The source code should compile without errors in every UNIX flavor or Linux distribution.
Terminal Input/Output
In Linux and all other UNIX flavors (like IBM-AIX), there is no difference between reading data from a file and reading data from a terminal or between writing data to a file and writing data to screen - specially if the data consists only of strings of characters.
A terminal provides three I/O ports:
- standard input: by default is the keyboard
- standard output: by default is the terminal screen
- standard error: by default is the terminal screen
There are two sets of functions to handle those I/O ports:
- The low-level I/O system calls: that use file descriptors to access standard input, output and error.
- The standard I/O library: that use streams implemented as a pointer to FILE* structure (
#include <stdio.h>
).
Figure 2: Low-level system calls and standard I/O library functions
Note: You can get more information about low-level system calls: read, write, open, select, close.
The source code contains a small application named test.c that illustrates the use of low-level system calls and standard I/O library functions by printing a string
to terminal screen (standard output). The source code is the simplest possible:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main ( int _argc, char *_argv[] )
{
char *ptr = "This is an output test!\n";
printf( ptr );
fprintf( stdout, ptr );
write( 1, ptr, strlen(ptr) );
return 0;
}
To build test.c by using the C compiler:
cc -o test test.c
When you execute ./test, you get the following output:
Figure 3: ./test output
You see? The string "This is an output test!" pointed by ptr is printed to screen (standard output) by using three different functions:
printf
: Part of standard I/O library that by default always prints a string
to standard output fprintf
: Part of standard I/O library. Notice the first argument, stdout
stream redirect ptr string
to standard output (or screen). write
: Low-level system call. Notice the first argument, 1
which is the file descriptor for standard output.
Makeupper Sample
There are two more samples in the source code: makeupper.c and makeupper2.c. They do the same thing. The difference is that makeupper.c uses low-level system calls and makeupper2.c uses standard I/O library.
makeupper
sample is very simple. Its purpose is to capitalize letters in inputted data and print to standard output. However, it demonstrates how to read from standard input and from a file too. The first sample, makeupper.c, uses low-level system calls:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main ( int _argc, char *_argv[] )
{
int fd_main = 0;
char ch = 0;
fd_main = _argc == 1 ? 0 : open(_argv[1], O_RDONLY);
if ( fd_main == -1 )
{
perror("input");
exit(-1);
}
while ( read( fd_main, &ch , 1 ) )
{
ch = toupper(ch);
write( 1, &ch, 1 );
}
close(fd_main);
return 0;
}
Notice the line that reads:
fd_main = _argc == 1 ? 0 : open(_argv[1], O_RDONLY);
If no file path argument is passed then fd_main
assumes standard input descriptor. open
system call is used to open a file when it is passed as argument.
makeupper2.c uses standard I/O library functions instead of system calls:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main ( int _argc, char *_argv[] )
{
FILE *f_file = NULL;
char ch = 0;
f_file = _argc == 1 ? stdin : fopen(_argv[1], "r");
if ( f_file == NULL )
{
perror("input");
exit(-1);
}
while ( !feof(f_file) )
if ( (ch = fgetc(f_file)) > 0 )
fputc( toupper(ch), stdout );
fclose(f_file);
return 0;
}
To build both samples:
By executing ./makeupper (or ./makeupper2) without arguments, it starts in Canonical Mode. After you press enter, makeupper
receives the line. To quit Canonical Mode, you should press CTRL+D or CTRL+\:
Figure 4: ./makeupper - canonical mode
You can also pass a file path:
Figure 5: ./makeupper - printing test.c contents to standard output
Or makeupper can receive the output from a process in its standard input:
Figure 6: ./makeupper2 - ls -ltr output is redirected from screen (standard output) to standard input of makeupper2 (via PIPE)
Redirecting I/O
You can redirect standard output and error to files or devices (/dev/...). To illustrate that type and compile the following code (you can name it test2.c):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( int _argc, char *_argv[] )
{
char *p1 = "printed to standard output via stdlib.\n";
char *p2 = "printed to standard error via stdlib.\n";
char *p3 = "printed to standard output via low-level calls.\n";
char *p4 = "printed to standard error low-level calls.\n";
fprintf( stdout, p1 );
fprintf( stderr, p2 );
write ( 1, p3, strlen(p3) );
write ( 2, p4, strlen(p4) );
return 0;
}
There are four strings pointed for four pointers. p1
and p3
are printed to standard output and p2
and p4
are printed to standard error.
To build it:
cc -o test2 test.c
By executing ./test2, we have:
- ./test2 - you get all strings printed to screen.
- ./test2 >file.txt - redirects standard output to file.txt. Notice only
p1
and p3
strings are saved in file.txt. - ./test2 2>file.txt redirects standard error to file.txt. Notice only
p2
and p4
strings are saved in file.txt. - ./test2 >file.txt 2>&1 redirects both output and error to file.txt. Notice the trick 2>&1 (means redirect standard error to standard output).
If you want to append to existing file: ./test2 >>file.txt or ./test2 >>file.txt 2>&1.
Hope this helps.
History
- 14th October, 2008: First version