Thread: Implement Redirection for a simple shell

  1. #1
    Registered User
    Join Date
    Feb 2009
    Posts
    8

    Implement Redirection for a simple shell

    Hello I am very new to C and am having a hard time getting this to work. Yes this is a homework assignment if anyone was wondering, but I don't think I am doing this the right way....basically what is happening is stdout is never getting set back to the display and anything i type is getting written to the out.txt file. So if i have ls > out.txt, this works fine....but if i do something like
    cat test.txt.....that also gets written to out.txt and i want it to be written to the display.... here is my code thus far.....any guidance in helping me better understand this is much appreciated, along with any helpful C tips

    Code:
    /* a really bad approach to the parsing problem */
    	int i;
    	// scan argv for any file redirection symbols.
    	for (i=0; i< sizeof(argv); i++)
    	{
    		int pid;
    		FILE * fd;
    		if (argv[i] != NULL)
    		{
    			// if we encounter the symbol for output redirection execute everything infront of it with the launch app then copy its contents
    			// to the given name of the file.
    			if (strcmp( ">", argv[i] ) == 0)
    			{
    			
    				pid = fork();
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i] = NULL;
    				// open file to write to.
    				fd = fopen(argv[i+1],"w");
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i+1] = NULL;
    				// set stdout to file
    				dup2(fileno(fd), fileno(stdout));\
    				// run the application
    				launch_app(argc, argv);
    				// copy the contents of stdout to file.
    				cfile(fd, stdout);
    				// set stdout back to normal...which doesn't work...have also tried freopen, and system(exec >/dev/tty </dev/tty)
    				dup2(fileno(stdout), fileno(fd));
    				// close the file discriptor.
    				fclose(fd);
    				// return 0 so all commands in front of > will get run by launch_app
    				return 1;
    				
    			} // end if >
    			// if we encounter a symbol fo input redirection then copy the file to stdin and execute the commands infront of <
    			else if (strcmp( "<", argv[i] ) == 0)
    			{
    				pid = fork();
    				//probably not the way to do this but don't want anything including and after < to get executed with command.
    				argv[i] = NULL;
    				fd = fopen(argv[i+1], "r");
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i+1] = NULL;
    				// set stdin to file
    				dup2(fileno(fd), fileno(stdin));
    				// copy the contents of the file to stdin
    				cfile(stdin, fd);
    				// launch the application
    				launch_app(argc, argv);
    				// close fd since it is now stdin
    				fclose(fd);
    				// return 0 so all commands in fron of < will get run by launch_app with stdin now as the given file
    				return 1;
    			} // end if <			
    			else
    			{
    				system("exec >/dev/tty </dev/tty");
    			}			
    		} // end if argv[i] != null
    	} // end for

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    You might want to wait for a second opinion but I think there are some VERY FUNDAMENTAL errors in your use of fork() here:
    Quote Originally Posted by koooee View Post
    Code:
    /* a really bad approach to the parsing problem */
    	
    				pid = fork();
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i] = NULL;
    				// open file to write to.
    				fd = fopen(argv[i+1],"w");
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i+1] = NULL;
    				// set stdout to file
    				dup2(fileno(fd), fileno(stdout));\
    				// run the application
    				launch_app(argc, argv);
    				// copy the contents of stdout to file.
    				cfile(fd, stdout);
    				// set stdout back to normal...which doesn't work...have also tried freopen, and system(exec >/dev/tty </dev/tty)
    This fork is almost over. Since each process has it's own stdout, what happens to this one is sort of irrelevant by now. However, that might not be clear to you because the all the stdouts go to the console. Unless you redirect them.
    Code:
    				dup2(fileno(stdout), fileno(fd));
    				// close the file discriptor.
    				fclose(fd);
    				// return 0 so all commands in front of > will get run by launch_app
    				return 1;
    				
    			} // end if >
    I also don't think you should be using return inside fork() unless you have something special set-up in the calling function() -- because it doesn't return to the original process. There is a whole second version of the program in effect. If you want to fork() to do something in parallel, you should almost certainly use exit () to finish (only the child dies).
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Here's a simple illustration of what I think you want:
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void testfunc () {
    	puts("hello world");
    }
    
    
    int main () {
    	FILE *test=fopen("tmp.txt", "w");
    	int fd=fileno(test),pid;
    	pid=fork();
    	if (pid==0) {
    		dup2(fd,1);
    		testfunc();
    		exit (0);
    	} 
    	testfunc();
    	return 0;
    }
    The result is the first testfunc() call prints "hello world" in tmp.txt, and the second one appears on the screen. If I had used return here, it would be the same thing because the forked process is in main. But if I'd forked in a subfunction and returned, there would be two "hello worlds" in tmp.txt because the child process has

    1) it's own main() to return to
    2) it's stdout redirected to the file.

    Code:
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void testfunc () {
    	puts("hello world");
    }
    
    
    void forkfunc(int fd) {
    	int pid=fork();
    	if (pid==0) {
    		dup2(fd,1);
    		testfunc();        /* first print to file */
    		return;   /* child returns with stdout redirected */
    	} 
    	else return;  /* parent returns with stdout as is */
    }
    
    int main () {
    	FILE *test=fopen("tmp.txt", "w");
    	int fd=fileno(test);
    	forkfunc(fd);
    	testfunc();   /* after the fork, parent returns and prints to stdout */
    	                            /* child returns and prints to file AGAIN */
            return 0;      
    }
    Check it out -- both these examples do exactly what I just described.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    As a final note, you CAN make a copy of stdout before you dup2 it, and then get it back (which means you don't need fork at all):
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void testfunc () {
    	printf("hello world\n");
    }
    
    int main () {
    	FILE *test=fopen("tmp.txt", "w");
    	int fd=fileno(test), copy=dup(1);
    	dup2(fd,1);
    	testfunc();
    	fflush(stdout);
    	dup2(copy,1);
    	testfunc();
    	return 0;
    }
    Again, this writes the first time to the file and the second time to the screen. fflush() is necessary because C stream output is buffered, and without it the first "hello world" is held in that buffer and ends up on the screen with the other one.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    Feb 2009
    Posts
    8
    Quote Originally Posted by MK27 View Post
    The result is the first testfunc() call prints "hello world" in tmp.txt, and the second one appears on the screen. If I had used return here, it would be the same thing because the forked process is in main. But if I'd forked in a subfunction and returned, there would be two "hello worlds" in tmp.txt because the child process has

    1) it's own main() to return to
    2) it's stdout redirected to the file.
    First of all thank you so much for all of your help you are really helping me understand this better....I really appreciate it. Although if you don't mind answering another question, I am confused about this piece of code i have
    Code:
    if (strcmp( ">", argv[i] ) == 0)
    			{
    			
    				int pid = fork();
    				if( pid == 0);
    				{
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i] = NULL;
    				// open file to write to.
    				FILE * fd = fopen(argv[i+1],"w");
    				// probably not the way to do this but don't want anything including and after > to get executed with command.
    				argv[i+1] = NULL;
    				// set stdout to file
    				dup2(fileno(fd), fileno(stdout));
    				// run the application
    				launch_app(argc, argv);
    				// exit child process
    				exit(0);
    				}
    				// return 0 so all commands in front of > will get run by launch_app
    				return 0;
    				
    			} // end if >
    now when i make the call to ls > out.txt it exits my shell??? it still writes to out.txt and if i run the shell again i can call cat < out.txt and it works fine. But, if pid == 0 shouldn't the current process be the child so the call to exit(0) would quit the child process and return to the parent?? I don't know why that is exiting the shell?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Request for comments
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 01-02-2004, 10:33 AM
  2. simple shell
    By ivandn in forum Linux Programming
    Replies: 3
    Last Post: 11-04-2002, 06:03 PM
  3. simple shell coding
    By estranged in forum C Programming
    Replies: 6
    Last Post: 09-30-2002, 05:59 AM
  4. simple shell (c), fgets problem
    By razza in forum Linux Programming
    Replies: 6
    Last Post: 05-26-2002, 10:44 PM
  5. How to make a simple shell?
    By ranger in forum Linux Programming
    Replies: 0
    Last Post: 03-31-2002, 03:09 AM