Redirecting stdin and stdout? - c

So im trying to redirect the I/O to read command from file then when user runs the output command it will print the compiled command to output file.
For example on the terminal:
./run 2 < test.txt // This would take file using dup and take the input
Then when you want to output the compile:
./run 1 > output.txt // and it would put into an output file
So far i know how to output to a file but my problem is with the input. how do i get the command from the file using the dup2() function? I tried researching this but no luck.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
char inputForOutput[100];
void functionOutput(int argc, char **argv){
int ofd; //Init of file desc.
ofd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY);
dup2(ofd, 1);//Duplicates to stdout
system("ls");//Copies commnd given to output_file
}
//Function is called when argument number is == 1
void functionInput(int argc, char **argv){
FILE *ifd;
printf("\n %s \n ", argv[2]);
ifd = fopen(argv[2] , "r");
if (ifd == NULL){
perror("No file found");
exit(1);
}
fscanf(ifd,"%s",inputForOutput);
printf("\n**%s**\n",inputForOutput);
}
int main(int argc, char **argv)
{
int output;
int input;
output = strcmp("1", argv[1]);
input = strcmp("2" ,argv[1]);
if (output == 0 ) { //Fail safe for number of arguments
functionOutput(argc, argv);
}
else if ( input == 0){
functionInput(argc, argv);
}
else{
fprintf(stderr, "How to use: %s function output_file\n", argv[0]); // FAIL SAFE IF INPUT DOES NOT MATCH BOTH FUNCTIONS
}
return 0;
}

To redirect input and output, use this format
myprogram > out.txt < in.txt //read from in.txt, write to out.txt
myprogram < in.txt > out.txt //read from in.txt, write to out.txt
myprogram < in.txt //redirect stdin only
myprogram > out.txt //redirect stdout only
myprogram //no redirection
...
This should work with any program. Example:
int main(void)
{
char buf[1000];
if(fgets(buf, sizeof(buf), stdin))
printf("write: %s\n", buf);
return 0;
}
To redirect stdin/stdout in the program, use the standard method
freopen("output.txt", "w", stdout);
printf("Testing...");
fclose(stdout);
freopen("input.txt", "r", stdin);
char buf[100];
fgets(buf, sizeof(buf), stdin);
fclose(stdin);
Alternatively, set FILE *fin = stdin; FILE* fout = stdout; to redirect the opposite way.
Next, to write a program using argv elements, always test argc first. The code below shows an example.
#include <stdio.h>
#include <string.h>
int redirect(int argc, char **argv, int *index)
{
//no more redirection!
if(*index >= argc)
return 1;
//not enough parameters
if(*index + 1 >= argc)
{
printf("wrong usage\n");
return 0;
}
if(strcmp(argv[*index], "<") == 0)
{
*index++; //next parameter is to redirect input
if(!freopen(argv[*index], "r", stdin))
printf("error, redirect input failed");
}
else if(strcmp(argv[*index], ">") == 0)
{
*index++; //next parameter is to redirect output
if(!freopen(argv[*index], "w", stdout))
printf("error, redirect output failed");
}
else
{
printf("wrong usage\n");
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
int index = 1;
if(!redirect(argc, argv, &index))
return 1;
if(!redirect(argc, argv, &index))
return 1;
//read
char buf[1000];
if(fgets(buf, sizeof(buf), stdin))
{
//write
printf("write: %s\n", buf);
}
fclose(stdin);
fclose(stdout);
return 0;
}

With functionOutput() you have a good first attempt at capturing the output of a system command to a file. Actually, that is the function called when the first argument is 1, so you might want to update your comment. Also, you're creating a file with the name stored in argv[1], which we already know is 1 so it's probably not doing what you expect, and you probably want:
ofd = open(argv[2], O_CREAT|O_TRUNC|O_WRONLY);
With functionInput() you're reading the first non-whitespace entry from the file. If you're telling it to read the file which you output using the functionOutput() function, that is likely to be (some of) the name of the first file which was listed by ls.
I'm finding it unclear what you're wanting to do which isn't that. If you want to find out what the command was which you ran to generate the output, that information is not available from the file itself, because you didn't write it there. If that's what you want, you may want to consider writing the command as the first line of the file, followed by the output. Then when you read it, you can assume that the first line is the command run, followed by the output of that command.

If I understand your question, and you want to run your program in essentially two different modes, (1) you want to take input if there is input to be taken on stdin; and (2) if there is no input waiting, you want to do an output, then select/pselect or poll are what you are looking for.
For example select allows you to check whether there is input ready to be read on a file descriptor (or set of descriptors) and it will return the number of descriptors with input waiting (or -1 and set errno on error). You could simply use the STDIN_FILENO (a/k/a fd 0) to check if there is input on stdin, e.g.
#include <stdio.h>
#include <unistd.h> /* for STDIN_FILENO */
#include <sys/select.h> /* for pselect */
int input (int filedes)
{
fd_set set;
/* declare/initialize zero timeout */
struct timespec timeout = { .tv_sec = 0 };
/* Initialize the file descriptor set. */
FD_ZERO (&set);
FD_SET (filedes, &set);
/* check whether input is ready on filedes */
return pselect (filedes + 1, &set, NULL, NULL, &timeout, NULL);
}
int main (void)
{
if (input (STDIN_FILENO))
puts ("doing input routine");
else
puts ("doing output routine");
return 0;
}
(note: from the man page "select() uses a timeout that is a struct timeval (with seconds and microseconds), while pselect() uses a struct timespec (with seconds and nanoseconds).")
Example Use/Output
$ ./bin/select_peekstdin < file
doing input routine
$ ./bin/select_peekstdin
doing output routine

Related

Redirect output of command line to a variable in C

Is there a way to redirect output of a command line which returns integer as an output to a variable in C?
for example, if the command is "cmd", then is there a way to redirect its output (an integer) and store it in variable in C?
I tried using popen and fgets but it seems to be working only with characters. Any suggestions?
It works perfectly fine with popen and fgets:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
const char *cmd = argc > 1 ? argv[1] : "echo 42";
char buf[32];
FILE *fp = popen(cmd, "r");
if( fp == NULL ){
perror("popen");
return 1;
}
if( fgets(buf, sizeof buf, fp) == buf ){
int v = strtol(buf, NULL, 10);
printf("read: %d\n", v);
}
return 0;
}
If you want to convert a character string from the standard input, you could use fgets and then use atoi to convert the input to an integer.
If you want to convert the output of a command, let's say ls and store the output of the command to a variable, you could learn about fork, dup2, pipe, and exec function family.
More about this topic on this tutorial : Capture the output of a child in C. This tutorial also provide an example with popen if you want to keep things "high level".
Here is an even simpler example using popen() and fscanf():
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE *fp = popen("date '+%s'", "r");
long seconds;
if (fp == NULL) {
fprintf(stderr, "popen failed: %s\n", strerror(errno));
return 1;
}
if (fscanf(fp, "%ld", &seconds) == 1) {
printf("epoch seconds: %ld\n", seconds);
pclose(fp);
return 0;
} else {
fprintf(stderr, "invalid program output\n");
pclose(fp);
return 1;
}
}

How do I get stdin length from a pipe? echo "hello" | ./get_stdin_size

I installed an application and its command line can do:
command -input 1.txt
command < 1.txt
echo "hello" | command
and output something. I don't have the source code and want to implement that behaviour too.
What I've tried is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0){
rewind(stdin);
printf("stdin has data\n");
char buffer[100];
fgets(buffer, sizeof buffer, stdin);
printf("stdin data are: %s\n", buffer);
}else{
if (argc < 2){
printf("no cmd arguments\n");
return -1;
}else{
printf("command line argument: %s\n", argv[1]);
FILE* fp = fopen(argv[1], "r");
if (fp == NULL){
printf("NULL fp pointer\n");
return -1;
}
char a[100] = {0};
fgets(a, sizeof a, fp);
printf("first line of file: %s\n", a);
}
}
return 0;
}
But the problem is that pipes are not seekable. So ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0) doesn't fit all cases.
One solution that I think of is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if (argc > 1){
//open file with argv[1] as filename
//read data from disk file
}else{
//read data from stdin
if(stdin is file){
//get file size
//read data from stdin
}else if(stdin is pipe){
//get pipe size
//read data from stdin
}
}
return 0;
}
I have 2 problems with this code:
Is there a ispipe() function which works like isatty(fileno(stdin))? I need to tell if stdin is a pipe.
How do I get the stdin size/length from a pipe? Apparently I can't use:
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin));
As #Peter pointed out in the comment, I should not try to get the stdin size from a pipe beforehand, then how do I know it reaches the end? Could anyone gives me an minimum example about this "stream-based processing"?
You can use the fstat() syscall to tell if standard input is a pipe (Either anonymous or named), or a file (And if a file, find its size):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
struct stat s;
if (fstat(STDIN_FILENO, &s) < 0) {
perror("fstat");
return EXIT_FAILURE;
}
switch (s.st_mode & S_IFMT) {
case S_IFIFO:
puts("standard input is a pipe.");
break;
case S_IFREG:
printf("standard input is a file that is %ld bytes in size.\n",
(long)s.st_size);
break;
case S_IFCHR:
if (isatty(STDIN_FILENO)) {
puts("standard input is a terminal.");
} else {
puts("standard input is a character device.");
}
break;
default:
puts("standard input is something else.");
}
return 0;
}
Example:
$ gcc testpipe.c
$ cat testpipe.c | ./a.out
standard input is a pipe.
$ ./a.out < testpipe.c
standard input is a file that is 525 bytes in size.
$ ./a.out
standard input is a terminal.
The only way to be sure that you won't recieve more data from a pipe is when it is closed (SIGPIPE signal).
Thus, as stated in comments, allocating/reading the right of memory is challenging with pipes, since they can be infinite (e.g. /dev/random). You have to make hypothesis or use extra data in order to handle the pipe.
Depending on your use case, these strategies can be one of:
Sending the data length at the beginning of the message. This can be like: echo -e'\x05\x00\x00\x00Hello'|./myprog. With that strategy, it is trivial to read the pipe but it requieres that you know the total size of the input before you start sending it.
Allocating and reading a limited amount of data/time. If you recieve than PIPE_MAX_SIZE bytes or you wait more than TIMEOUT_PIPE, close the pipe and handle the possibly incomplete message.
Handle the message block by block. If your message follows a regular pattern, you can read it this way and handle blocks sequentially until you reach the end of the message. This also allows you to discard previous buffer to read unlimited amount of data that would not fit in memory.

popen: intercepting user's input

I have a code which runs bc thru popen(). I can intercept the calculator's output and prepend it with "Output=" text. But how can I intercept what user's is writing to bc?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *in;
char buff[512];
if(!(in = popen("bc", "r"))){
exit(1);
}
while(fgets(buff, sizeof(buff), in)!=NULL){
printf("Output = %s", buff);
}
pclose(in);
return 0;
}
You can combine bc and echo with a pipe: echo '12*4' | bc
Example typing 12*4:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
FILE *in;
char buff[512];
char cmd[512];
while (fgets(buff, sizeof(buff), stdin)!=NULL){
strcpy(cmd, "echo '");
strcat(cmd, buff);
strcat(cmd, "' | bc");
if(!(in = popen(cmd, "r"))){
exit(1);
}
fgets(buff, sizeof(buff), in);
printf("output:%s", buff);
}
pclose(in);
return 0;
}
Output:
david#debian:~$ ./demo
12*4
output:48
You need to use pipe() and fork/exec(). However, manual piping is quite complex:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
int write_pipe[2], read_pipe[2];
pipe(read_pipe); pipe(write_pipe);
#define PARENT_READ read_pipe[0]
#define CHILD_WRITE read_pipe[1]
#define CHILD_READ write_pipe[0]
#define PARENT_WRITE write_pipe[1]
int child = fork();
if (child == 0) { /* in child */
close(PARENT_WRITE);
close(PARENT_READ);
dup2(CHILD_READ, 0); close(CHILD_READ);
dup2(CHILD_WRITE, 1); close(CHILD_WRITE);
execl("/usr/bin/bc", "/usr/bin/bc");
} else { /* in parent */
close(CHILD_READ);
close(CHILD_WRITE);
write(PARENT_WRITE, "2+3\n", 4);
char buff[512];
int output_len=read(PARENT_READ, buff, sizeof(buff));
write(1, buff, output_len);
close(PARENT_READ);
}
return 0;
}
What you're looking to do is to start a subprocess, then simultaneously:
When activity occurs on standard input, execute some function on that input before passing it to the subprocess.
When activity occurs on the subprocess output, execute some function on that output before passing it to standard output.
The system call that allows you to wait for activity on two handles is called poll, but before we do that, we need to create the handles and start the subprocess:
int a[2], b[2];
if(pipe(a)==-1)abort(); // for communicating with subprocess input
if(pipe(b)==-1)abort(); // for communicating with subprocess output
switch(fork()) {
case -1: abort();
case 0: dup2(a[0],0), dup2(b[1],1), execlp("/usr/bin/bc", "bc", 0); exit(1);
};
Note how pipe works: Data written to fildes[1] appears on (i.e., can be read from) fildes[0]. This means we want to read from the standard output of our subprocess, b[0] and write to the standard input of our subprocess a[1].
Before we do that, we can use the poll instruction to wait for activity on either standard input (fd #0), or the subprocess output (b[0]):
for(;;) {
struct pollfd p[2]={0};
p[0].fd = 0; p[1].fd = b[0];
p[0].events = p[1].events = POLLIN;
while (poll(p,2,-1) <= 0);
At this point, there is activity on at least one of these file descriptors. You can see which one by examining the .revents member.
if(p[0].revents & POLLIN) {
r = read(0, buffer, sizeof(buffer));
write(a[1], buffer, r); // check for errors, or perhaps modify buffer
}
if(p[1].revents & POLLIN) {
r = read(b[0], buffer, sizeof(buffer));
write(1, buffer, r); // check for errors, or perhaps modify buffer
}
Note especially we use the opposite member a[1] and b[0] from the member we dup2'd onto the subprocesses standard input (0) and standard output (1).
At this point you can loop back up to poll again:
}
Disconnects (like EOF, program crash, etc) will be presented as read() returning 0, so watch carefully for this case, and break; out of the loop if so desired.

Simulate the Linux command tee in C

I have to do the simulation of the command tee in C for Linux. How does tee work internally? It looks like a T-shaped pipe, so should I use a pipe? Is there a special kind of pipe?
tee takes stdin and copies the data stream to stdout as well as a file given as an option, it can be used in many very different situations.
An implementation in C is quite simple, just make a program that copies all data from stdin to stdout, but also use the same output statements for stdout on a file that you opened based on the command line argument.
basically in pseudo code:
file f = open(argv[1])
while (! end of file stdin) {
buffer = read stdin
write stdout buffer
write f buffer
}
close(f)
Note that you don't really have to do anything with pipes, your shell will sort out the pipes, the program only has to copy data from one stream to two others.
I finished the program!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
main(int argc, char *argv[]){
FILE *fp, *fp1;
char buffer;
if(argc != 4){
printf("\nError");
printf("\nSintaxis: tee [archivo1] [archivo2]\n");
exit(0);
}
if(strcmp(argv[1], "tee") == 0){
fp = fopen(argv[2], "r");
fp1 = fopen(argv[3], "w");
printf("\Content in %s:\n", argv[2]);
while(!feof(fp)){
buffer = fgetc(fp);
fputc(buffer, fp1);
printf("%c", buffer);
}
printf("\n\n%s received %s\n", argv[3], argv[2]);
fclose(fp);
fclose(fp1);
}
else
printf("\nThe first argument have to be tee\n");
}
Here is some code I wrote about 20 years ago to implement TEE in Windows. I have been using this with various batch files since then. Note the flush command at the end of each line.
#include <stdio.h>
#include <share.h>
int main (int argc, char * argv[])
{
if (argc < 2 )
{
printf ("Error: No output file name given, example: theCmd 2>&1 |ltee outputFileName \n");
return 1;
}
FILE *Out = _fsopen(argv[argc-1], "a", _SH_DENYWR);
if (NULL == Out)
{
char buf[300];
sprintf_s(buf, 300, "Error openning %s", argv[argc-1]);
perror(buf);
return 1;
}
int ch;
while ( EOF != (ch=getchar()))
{
putchar(ch);
putc(ch, Out);
if ( '\n' == ch )
fflush(Out);
}
_flushall();
fclose(Out);
return 0;
}

Read from file or stdin

I am writing a utility which accepts either a filename, or reads from stdin.
I would like to know the most robust / fastest way of checking to see if stdin exists (data is being piped to the program) and if so reading that data in. If it doesn't exist, the processing will take place on the filename given. I have tried using the following the test for size of stdin but I believe since it's a stream and not an actual file, it's not working as I suspected it would and it's always printing -1. I know I could always read the input 1 character at a time while != EOF but I would like a more generic solution so I could end up with either a fd or a FILE* if stdin exists so the rest of the program will function seamlessly. I would also like to be able to know its size, pending the stream has been closed by the previous program.
long getSizeOfInput(FILE *input){
long retvalue = 0;
fseek(input, 0L, SEEK_END);
retvalue = ftell(input);
fseek(input, 0L, SEEK_SET);
return retvalue;
}
int main(int argc, char **argv) {
printf("Size of stdin: %ld\n", getSizeOfInput(stdin));
exit(0);
}
Terminal:
$ echo "hi!" | myprog
Size of stdin: -1
You're thinking it wrong.
What you are trying to do:
If stdin exists use it, else check whether the user supplied a filename.
What you should be doing instead:
If the user supplies a filename, then use the filename. Else use stdin.
You cannot know the total length of an incoming stream unless you read it all and keep it buffered. You just cannot seek backwards into pipes. This is a limitation of how pipes work. Pipes are not suitable for all tasks and sometimes intermediate files are required.
First, ask the program to tell you what is wrong by checking the errno, which is set on failure, such as during fseek or ftell.
Others (tonio & LatinSuD) have explained the mistake with handling stdin versus checking for a filename. Namely, first check argc (argument count) to see if there are any command line parameters specified if (argc > 1), treating - as a special case meaning stdin.
If no parameters are specified, then assume input is (going) to come from stdin, which is a stream not file, and the fseek function fails on it.
In the case of a stream, where you cannot use file-on-disk oriented library functions (i.e. fseek and ftell), you simply have to count the number of bytes read (including trailing newline characters) until receiving EOF (end-of-file).
For usage with large files you could speed it up by using fgets to a char array for more efficient reading of the bytes in a (text) file. For a binary file you need to use fopen(const char* filename, "rb") and use fread instead of fgetc/fgets.
You could also check the for feof(stdin) / ferror(stdin) when using the byte-counting method to detect any errors when reading from a stream.
The sample below should be C99 compliant and portable.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
long getSizeOfInput(FILE *input){
long retvalue = 0;
int c;
if (input != stdin) {
if (-1 == fseek(input, 0L, SEEK_END)) {
fprintf(stderr, "Error seek end: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == (retvalue = ftell(input))) {
fprintf(stderr, "ftell failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == fseek(input, 0L, SEEK_SET)) {
fprintf(stderr, "Error seek start: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
} else {
/* for stdin, we need to read in the entire stream until EOF */
while (EOF != (c = fgetc(input))) {
retvalue++;
}
}
return retvalue;
}
int main(int argc, char **argv) {
FILE *input;
if (argc > 1) {
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"r");
if (NULL == input) {
fprintf(stderr, "Unable to open '%s': %s\n",
argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
}
} else {
input = stdin;
}
printf("Size of file: %ld\n", getSizeOfInput(input));
return EXIT_SUCCESS;
}
You may want to look at how this is done in the cat utility, for example.
See code here.
If there is no filename as argument, or it is "-", then stdin is used for input.
stdin will be there, even if no data is pushed to it (but then, your read call may wait forever).
You can just read from stdin unless the user supply a filename ?
If not, treat the special "filename" - as meaning "read from stdin". The user would have to start the program like cat file | myprogram - if he wants to pipe data to it, and myprogam file if he wants it to read from a file.
int main(int argc,char *argv[] ) {
FILE *input;
if(argc != 2) {
usage();
return 1;
}
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"rb");
//check for errors
}
If you're on *nix, you can check whether stdin is a fifo:
struct stat st_info;
if(fstat(0,&st_info) != 0)
//error
}
if(S_ISFIFO(st_info.st_mode)) {
//stdin is a pipe
}
Though that won't handle the user doing myprogram <file
You can also check if stdin is a terminal/console
if(isatty(0)) {
//stdin is a terminal
}
Just testing for end of file with feof would do, I think.
Note that what you want is to know if stdin is connected to a terminal or not, not if it exists. It always exists but when you use the shell to pipe something into it or read a file, it is not connected to a terminal.
You can check that a file descriptor is connected to a terminal via the termios.h functions:
#include <termios.h>
#include <stdbool.h>
bool stdin_is_a_pipe(void)
{
struct termios t;
return (tcgetattr(STDIN_FILENO, &t) < 0);
}
This will try to fetch the terminal attributes of stdin. If it is not connected to a pipe, it is attached to a tty and the tcgetattr function call will succeed. In order to detect a pipe, we check for tcgetattr failure.

Resources