if (out) {
out = 0;
int fd = open(output_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) perror("open for writing");
if (dup2(fd, STDOUT_FILENO) < 0) { perror("dup2"); abort();}
close(fd);
}
if (-1 == execvp(argv[0], argv)) {
perror("execvp");
printf(" (couldn't find command)\n");
}
exit(1);
I am trying to redirect output back into terminal after redirecting it to a separate file. At the moment, this works when I call:
$ ls > file.txt
However, when I call
$ ls -l
right afterwards, it continues to direct its output into file.txt, rather than the terminal.
You cannot get the original standard output back after closing all copies of it.
Normally, if you are going to exec another program with redirected output, you first fork() a child, and apply the redirection only in the child, then have the child terminate immediately if the exec fails. The parent's streams are unaffected that way, so there is no need to restore them. This is what shells do when they run external commands.
But to answer the question posed, if you want to be able to redirect the standard output and then later restore it, you need to make a copy of it first ...
int stdout_save = dup(STDOUT_FILENO);
Then perform your redirection. When ready to restore, just copy the saved file descriptor back, and close the dupe.
dup2(stdout_save, STDOUT_FILENO);
close(stdout_save);
Do not neglect to perform proper checks on the return values of these function calls; I have omitted that from the example code for clarity and brevity.
Related
Will EXECVP system call supports IO redirections
That means is this give desired output
char *args[]={"cat","A.txt","B.txt",">","C.txt",NULL};
execvp(args[0],args);
I mean will the data in A.txt and B.txt goes to C.txt
If no why ?
UPD : I have asked two doubts in comment please clarify it
This is technically not an answer to your question, that has been answered in the comments. But an explanation to how you can do redirection with execvp
When starting a new program with execvp, it will inherit the current file descriptor. So if you setup file descriptor 1 (which is used for stdout)
to be redirected to "C.txt" before calling execvp, the new program will
write to "C.txt":
// Open "C.txt" for writing, creating it if it doesn't exist, or
// clearing its content if it does exist.
// `mode` should be set as appropriate
//
int fd = creat("C.txt", mode);
if (fd == -1)
{
// Swap out error handling to suit your needs
perror("open failed");
exit(EXIT_FAILURE);
}
// We want new process to have "C.txt" on file descriptor 1
if (dup2(fd, 1) == -1)
{
perror("dup failed");
exit(EXIT_FAILURE);
}
// "C.txt" is now writable on file descriptor 1, so we don't need the
// old one. Actually, the old one could confuse the new executable.
close(fd);
// We can now execute new program. It will inherit current open
// file descriptors and it will see "C.txt" on file descriptor 1
char *args[]={"cat","A.txt","B.txt",NULL};
execvp(args[0],args);
// If we reach this point, `execvp` failed.
perror("execvp failed");
exit(EXIT_FAILURE);
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.
I am creating a shell code. Basically, I want to redirect stdin file to stdout file. For instance when I enter a command like sort < hello.c > t.txt, then the hello.c file should be copied in new mentioned file called t.txt.
Here is my code, I am able to redirect output of other commands, when I type ls > t.txt. However, I don't have any idea about redirecting one file's input to other file using dup2.
Here is my code, I am only posting the loop, as this is where I have to create the logic.
int in, out;
for (i = 0; i < arridx; i++) {
if(strcmp( array[i],"<")==0)
{
in = open(array[i+1], O_RDONLY);
array[i]=0;
// array[i+1]=0;
}
if(strcmp( array[i],">")==0)
{
out = open(array[i+1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
array[i]=0;
// array[i+1]=0;
}
}
dup2(in, 0);
dup2(out, 1);
// close unused file descriptors
close(in);
close(out);
Input array would be like
array[0]="sort"
array[1]="<"
array[2]="hello.c"
array[3]=">"
array[4]="t.txt"
In fact, whenever you run something like :
command < hello.c > t.txt
The redirection will take place presuming command is your argv[0] with no of arguments as 1, and redirection taking place by the shell.
However, on another point, going through your program, if redirection is used
not from command prompt but by array contents only,
int dup2(int oldfd, int newfd); - creates a copy of the file descriptor oldfd.
In your case,
dup2(in, 0);
dup2(out, 1);
0 and 1 stands for stdin and stdout file descriptors respectively. So, if you would like to redirect your input to be taken from the stdin instead of hello.c (file opened as in) and output to be taken from the stdout instead of t.txt (file opened as out), then shouldn't be other way round i.e.
dup2(0, in);
dup2(1, out);
I m writing a basic shell in C, I have all the redirect operators implemented, however, when I try to redirect "cd" I run into this problem :
cd works perfectly without any output redirection, but
when I m trying to do something like this :
cd inexistant_directory > output_file
the output file is not created, in bash, running that command does redirect the stdout, as I previously stated redirection operators with external commands work good
when I encounter cd command, I call
char*path = get_path(parameters); //implemented by me, works on rest of the cases
int ret =chdir(path);
I don't call this in the child process but in parent(shell process itself)
What am I doing wrong ?
Thank you,
PS : The OS on which I run this is Ubuntu 12.10 however, the code is POSIX compliant
LE: I can't post the whole code as it goes to around 600 lines,
here's my logic tho
if(internal_command) {
//do quit, exit or cd
} else if (variable_assignemt){
//do stuff
} else {
// external command
pid = fork();
if(pid == -1) {
//crash
} else if (pid == 0) {
do_redirects()
call_external_cmd
}
default :
wait(pid, &status);
So, I think that to solve the issue I need to redirect stdout in parrent(shell process)
and restore it after command is executed
Not redirecting stdout in parent process (shell) was indeed the cause of
cd's unwanted behaviour, my solution was the following:
if(we_have_out_redirection == 1) {
if(out != NULL) {
char *outParrent = out;
fflush(stdout);
outBackup = dup(STDOUT_FILENO); //I save stdout for future restoration
int fd = open(outParrent, O_WRONLY | O_CREAT | O_TRUNC, 0644); //open output file
int rc;
rc = dup2(fd, STDOUT_FILENO); //redirect stdout
retval = chdir(out); //execute cd command
//restore stdout
close(fd);
fflush(stdout);
rc = dup2(outBackup, 1);
close(outBackup);
}
}
Thanks to Jake223 for pointing out I forgot to redirect in parrent !
Continuing on this problem, but I'll reiterate:
For a homework assignment I have to write a basic shell including redirection. The program uses readline to prompt for input, parses the input string, and breaks it down into the executable name, the arguments, and the input/output file(s), if applicable. After parsing the string, it forks and the child execv()'s to the executable that was passed in. I'm using dup2() to change the file descriptors after the fork and before the execv, but am having a problem once the program has execv'd to the new executable. If in my shell I run ls > foo.out, I get: ls: write error: Bad file descriptor
Here is the code for my child process (this is after fork()):
int _child(struct command *c){
int ret;
/* When given `ls > foo.out`, these next two lines output:
** c->infile is (null)
** c->outfile is foo.out
*/
printf("c->infile is %s\n",c->infile);
printf("c->outfile is %s\n",c->outfile);
if(c->infile){
int fd = open( c->infile, O_RDONLY);
int _fd_dup = dup2(fd,0);
close(0);
if(_fd_dup != 0){
fprintf(stderr, "Failed to redirect command input.\n");
return 0;
}
}
if(c->outfile){
int fd = open( c->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
int _fd_dup = dup2(fd,1);
close(1);
if(_fd_dup != 1){
fprintf(stderr, "Failed to redirect command output.\n");
return 0;
}
}
I do not get the "Failed to redirect command output." error. As I mentioned, this is a homework assignment so I'm not looking for anyone to fix this, but rather point me in the right direction.
The problem is in this bit of code:
int _fd_dup = dup2(fd,1);
close(1);
You should be closing fd, not 1. You have the same problem in the fd 0 case, too.