How to restore standard output's file descriptor? - c

I need help to understand the file descriptors
So here is my code:
int main()
{
char ch;
close(1);
//now opening a file so that it gets the lowest possible fd i.e. 1
int fd=open("txt",O_RDWR);
//check..
printf("first printtf is executed\n");
scanf("%c",&ch);
printf("ur value is %c\n",ch);
printf("second printf is executed\n");
return 0;
}
in the above program, I tried to redirect the output of printf to the txt file rather than the standard output, i.e. the terminal.
But how to restore the standard output file descriptor so that the printf again works as normal for the second case, i.e the second printtf should give output to the terminal only..

The simplest way to do this would be to duplicate the output descriptor before closing it. You must look at dup.

Before you close it I think you want to dup() it.
When you need it back, you can dup() the dup.
dup will always use the lowest descriptor
int out = dup(1);
close(1);
int fd = open();
...
close(fd);
dup(out);
close(out);
Warning: this is from memory and untested ;-)

Related

How to redirect stdout to a file and then restore stdout back?

Here is my code and I can't get it to work.
int pfd = open("file", O_WRONLY, 0777);
int saved = dup(1);
close(1);
dup(pfd);
close(pfd);
printf("This goes into file\n");
// restore it back
dup2(saved, 1);
close(saved);
printf("this goes to stdout");
I have added some edits to my code.
You need to check the return values of your function calls. For most functions, you should check for error conditions. Doing so might have revealed the problem that if you want open() to create the requested file in the event that it does not initially exist, then you need to add the O_CREAT flag.
But that's not your main problem here -- you are dealing with a buffering issue. The output from the first printf() is buffered in memory, so even though file descriptor 1 refers to your file at the time that printf() is called, the data you write do not immediately get flushed to the destination file. You then restore the original stdout file handle, so when the data are actually flushed, they go to the (restored) original stdout. Solve this by fflush()ing before switching stdout back:
int pfd = open("file", O_WRONLY | O_CREAT, 0777);
int saved = dup(1);
close(1);
dup(pfd);
close(pfd);
printf("This goes into file\n");
fflush(stdout); // <-- THIS
// restore it back
dup2(saved, 1);
close(saved);
printf("this goes to stdout");
Note also that dup2() is cleaner and safer for duping a file descriptor onto a specific file descriptor number. You do that when you restore, but you should also do it for the initial redirection.

Why doesn't dup2 occur in sequential order?

Here is a code snippet.
int saved_stdout = dup(1);
int fd = open("file.txt", O_WRONLY | O_CREAT, 0640);
close(1);
dup(fd);
close(fd);
printf("This text should go into the file\n");
//restore stdout
dup2(saved_stdout, 1);
printf("stdout restore");
I am trying to learn about dup and dup2. So I initially connected my file.txt to stdout. So whenever I use printf, I should be writing to file.txt instead of stdout. But I want to restore it back as well once I am done with this usage, so I use dup2 at the end as well.
The problem is that the text "This text should go into the file\n" never actually goes into the file, but gets printed on stdout. Why so? I straced for it, only to find that dup2 call occurs before that printf("This text..."); statement, why so?
The problem may be due to output buffering. stdout is fully buffered if it's not writing to a terminal, so when you redirect it to the file with dup() it will be buffered. Try flushing the output after the printf().
printf("This text should go into the file\n");
fflush(stdout);
I removed my prior answer as it's wrong.... But when you use printf() you're using the FILE * for stdout, inside of which there's a descriptor or something pointing to the terminal. Changing fd 1 is apparently not changing that.
I'm getting inconsistent results with testing, so giving up here. I just want to add that putting this line just after the open() makes it work for me, which highlights the obscure behaviour:
printf("fileno is %i\n", fileno(stdout));
Don't mix FILE * operations such as printf() with file descriptor manipulation. You should use write() with the descriptor.

How to redirect stdout and stdin in a given file using argv in C

I want to redirect stdout and stdin in a specific file which would be given in argv array.
For instance when I enter a command like - ./shell ls > test
it should be redirected to the "test" file, now I am bit confuse because without writing any code it automatically redirects to that file, I want to do it manually, secondly, when I enter a command like- ./shell ls < test, the stdin should be redirected. I tried to find a file name and ">" or "<" sign using argv[argc-1] and argv[argc-2], but it seems that when I use ">" and a filename afterwards, the output prints(the arguments before ">" "<" sing) in that file instead of getting that name and a sign.
Basically, I am creating a shell command using execvp() and fork().
Here is my code, I am able to redirect stdout in a static file.
void call_system(char *argv[],int argc)
{
int pid;
int status=0;
signal(SIGCHLD, SIG_IGN);
int background;
/*two process are created*/
pid=fork();
background = 0;
if(pid<0)
{
fprintf(stderr,"unsuccessful fork /n");
exit(EXIT_SUCCESS);
}
else if(pid==0)
{
//system(argv[1]);
/*argument will be executed*/
freopen("CON","w",stdout);
char *bname;
char *path2 = strdup(*argv);
bname = basename(path2);
execvp(bname, argv);
fclose (stdout);
}
else if(pid>0)
{
/*it will wait untill the child process doesn't finish*/
//waitpid(pid,&status,0);
wait(&status);
//int tempid;
//tempid=waitpid(pid,&status,WNOHANG);
//while(tempid!= pid);// no blocking wait
if(!WIFEXITED(status) || WEXITSTATUS(status))
printf("error");
exit(EXIT_SUCCESS);
}
}
Try using dup() or dup2() or dup3().
The dup() system call creates a copy of the file descriptor oldfd,
using the lowest-numbered unused descriptor for the new descriptor.
File *fp=fopen(argv[1],"r");
int fd=fileno(fp);
dup2(fd,0); //dup2(fd,STDIN_FILENO) redirect file stream to input stream
scanf("%s",buff); //reading from file.
Similarly output can also be redirected.From manual these informations may be useful
On program startup, the integer file descriptors associated with the
streams stdin, stdout, and stderr are 0, 1, and 2, respectively. The
preprocessor symbols STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO
are defined with these values in <unistd.h>.
Suppose you want to redirect stdout to this file.
dup2(fd,1);//dup2(fd,STDOUT_FILENO)
printf("%s",buff); //this will write it to the file.
stdio redirection is handled by the shell, not the launched program. The relevant syscalls are pipe, open and dup2, the later of the two is used to redirect the stdio filedescriptors into the pipe or file to be read from or written to.

opening descriptor and closing , why does it matter?

i have the following code
it prints to the screen: haha
to the file :
haha
hello
Father finished
if i remove line 6 and 7 , I get different results
why?
int main()
{
// creates a new file having full read/write permissions
int fd = open("myfile", O_RDWR|O_CREAT, 0666);
write(fd, "haha\n", 5);
close(fd); // line 6
fd = open("myfile", O_RDWR); // line 7
close(0);
close(1);
dup(fd);
dup(fd);
if (fork() == 0)
{
char s[100];
dup(fd);
scanf("%s", s);
printf("hello\n");
write(2, s, strlen(s));
return 0;
}
wait(NULL);
printf("Father finished\n");
close(fd);
return 0;
}
Try to comment out the scanf(), recompile and rerun. The scanf() trying to read beyond EOF might be doing something in the stdio library internal buffers that is causing this issue in printf() buffer not being flushed at the time of process _exit. Just a guess...
A file descriptor has only one position which is used both for writing and reading. When you write to the file in line 4 the position is advanced past what was just written, so the descriptor's position is at the end of the file. Calling close and open has the effect of resetting the position to the beginning of file (among other things).
You could replace the calls to close and open with lseek(fd, 0, SEEK_SET) to have the same effect, without closing and reopening the file.
Also, you should not mix the stdio functions scanf, printf and low level functions such as write. The results of the program will be unpredictable because of buffering in the stdio functions.

C language. Read from stdout

I have some troubles with a library function.
I have to write some C code that uses a library function which prints on the screen its internal steps.
I am not interested to its return value, but only to printed steps.
So, I think I have to read from standard output and to copy read strings in a buffer.
I already tried fscanf and dup2 but I can't read from standard output. Please, could anyone help me?
An expanded version of the previous answer, without using files, and capturing stdout in a pipe, instead:
#include <stdio.h>
#include <unistd.h>
main()
{
int stdout_bk; //is fd for stdout backup
printf("this is before redirection\n");
stdout_bk = dup(fileno(stdout));
int pipefd[2];
pipe2(pipefd, 0); // O_NONBLOCK);
// What used to be stdout will now go to the pipe.
dup2(pipefd[1], fileno(stdout));
printf("this is printed much later!\n");
fflush(stdout);//flushall();
write(pipefd[1], "good-bye", 9); // null-terminated string!
close(pipefd[1]);
dup2(stdout_bk, fileno(stdout));//restore
printf("this is now\n");
char buf[101];
read(pipefd[0], buf, 100);
printf("got this from the pipe >>>%s<<<\n", buf);
}
Generates the following output:
this is before redirection
this is now
got this from the pipe >>>this is printed much later!
good-bye<<<
You should be able to open a pipe, dup the write end into stdout and then read from the read-end of the pipe, something like the following, with error checking:
int fds[2];
pipe(fds);
dup2(fds[1], stdout);
read(fds[0], buf, buf_sz);
FILE *fp;
int stdout_bk;//is fd for stdout backup
stdout_bk = dup(fileno(stdout));
fp=fopen("temp.txt","w");//file out, after read from file
dup2(fileno(fp), fileno(stdout));
/* ... */
fflush(stdout);//flushall();
fclose(fp);
dup2(stdout_bk, fileno(stdout));//restore
I'm assuming you meant the standard input. Another possible function is gets, use man gets to understand how it works (pretty simple). Please show your code and explain where you failed for a better answer.

Resources