Solved

exec fail

Posted on 2011-03-21
203
605 Views
Last Modified: 2012-06-22
Hi all,
I was asked to write a program that will output each 'command' (separated by pipes) with a separate process along with its process id. While this program will compile together with the makeargv.c (token).
Here is what I have so far:

test.c
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()
{
        char **argp;    // THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
        int i,j,vpret;
        char buf[80];   // command line
        pid_t pid;      // process ids
        while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
        {
                j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS

                for (i=0;i<j;i++)
                        printf("%s process id %d\n",argp[i], pid);
                argp[j]=(char *)0; // replace | with a null
                pid = fork();
                switch (pid) 
                {
                        case -1:
                                err_sys("fork failed");
                                break;
                        case 0: // child
                                execvp(argp[0],argp);
                                write(1,"exec fail\n",10);
                                exit(1);
                        default: // parent
                                waitpid(pid, NULL, 0);
                                write(1,"\nPrompt>",8);
                                break;
                }
        }
        printf(" make argv test exited\n"); //successfully exit
}

Open in new window


and here's the given makeargv.c
#include "makeargv.h"
#include <string.h>
#include <stdlib.h>
/*
* Make argv array (*arvp) for tokens in s which are separated by
* delimiters.   Return -1 on error or the number of tokens otherwise.
*/
int makeargv(char *s, char *delimiters, char ***argvp)
{
  char *t;
  char *snew;
  int numtokens;
  int i;
   /* snew is real start of string after skipping leading delimiters */
  snew = s + strspn(s, delimiters);
                             /* create space for a copy of snew in t */
  if ((t = calloc(strlen(snew) + 1, sizeof(char))) == NULL) {
     *argvp = NULL;
     numtokens = -1;
  } else {                     /* count the number of tokens in snew */
     strcpy(t, snew);
     if (strtok(t, delimiters) == NULL)
        numtokens = 0;
     else
        for (numtokens = 1; strtok(NULL, delimiters) != NULL;
             numtokens++)
             ;
               /* create an argument array to contain ptrs to tokens */
     if ((*argvp = calloc(numtokens + 1, sizeof(char *))) == NULL) {
        free(t);
        numtokens = -1;
     } else {            /* insert pointers to tokens into the array */
        if (numtokens > 0) {
           strcpy(t, snew);
           **argvp = strtok(t, delimiters);
           for (i = 1; i < numtokens + 1; i++)
              *((*argvp) + i) = strtok(NULL, delimiters);
        } else {
          **argvp = NULL;
          free(t);
        }
     }
  }
  return numtokens;
}

Open in new window


the output that I got:

bronco:~> gcc test.c makeargv.c error.c
bronco:~> ./a.out
ls -l | cat test.c | cat test2.c
ls -l  process id 0
 cat test.c  process id 0
 cat test2.c
 process id 0
exec fail

Prompt>ls -l
ls -l
 process id 8051
exec fail

Prompt>ls -l | cat test.c | cat test2.c
ls -l  process id 8052
 cat test.c  process id 8052
 cat test2.c
 process id 8052
exec fail

Prompt>exit
 make argv test exited

Can somebody help me why the process id are all the same and why do i get an "exec fail" at the end everytime? I think i need to use pipe() and dup2() in this program too but I'm not sure what should i do next, can somebody lead me?

Thanks for the help:)
0
Comment
Question by:crazy4s
  • 112
  • 84
  • 7
203 Comments
 

Author Comment

by:crazy4s
ID: 35185881
somehow i search through the net about the dup2 and pipe and i edited some of my code
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()
{
        char **argp;    // THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
        int i,j,vpret;
        char buf[80];   // command line
        pid_t pid;      // process ids
        int fd[2];

        if (pipe(fd)== -1)
                err_sys("pipe error");             // create a pipe

        while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
        {
                j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS

                for (i=0;i<j;i++)
                        printf("%s process id %d\n",argp[i], pid);
                argp[j]=(char *)0;

                if((pid=fork())<0)
                        err_sys("forked badly");  // and now both parent and child have copies

                switch (pid)
                {
                        case -1:
                                err_sys("fork failed");
                                break;
                        case 0: // child
                                close(fd[0]);   // close read end of pipe
                                dup2(fd[1],1);  // make 1 same as write to end of pipe
                                close(fd[1]);   // close excess fd
                                execvp(argp[0],argp);
                                write(1,"exec fail\n",10);
                                exit(1);
                        default: // parent
                                close(fd[1]);   // close write end of pipe
                                dup2(fd[0],0);  // make 0 same as read from end of pipe
                                close(fd[0]);   // close excess fd
                                waitpid(pid, NULL, 0);
                                write(1,"\nPrompt>",8);
                                break;
                }
        }
        printf(" make argv test exited\n"); //successfully exit
}

Open in new window


but the output seems to be wrong somewhere as i get a segmentation fault and all the process ids are 0:(

bronco:~> gcc test.c test2.c error.c
bronco:~> ./a.out
ls -l | cat test.c | cat test2.c
ls -l  process id 0
 cat test.c  process id 0
 cat test2.c
 process id 0

Prompt>exec fail
 process id 8238
exec fail

Prompt>Segmentation fault
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35187656
>> Can somebody help me why the process id are all the same

You print the process id (pid) before you assign a value to it (by calling fork).

Furthermore, you'll need to read up on how fork works : it returns the child's process id to the parent, and 0 to the child.


>> and why do i get an "exec fail" at the end everytime?

You need to read up on what pipes really do.

You can execute ONE command with an exec function, but you cannot use pipes in that call.

For example, "ls -l" is the command "ls" with the argument "-l", and you can use an exec function for it.



Suggested reading :

    http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
0
 

Author Comment

by:crazy4s
ID: 35187826
I've read through all the stuffs but I don't really understand how it works, like which one will be the parent process and which one will be the child process on the command line?

is there any possibility to not execute ONLY ONE command, like several command right before the pipe (|)?
let's say if i have smt like this on the command line
ls -l | cat test.c > file | file2

>>cannot use pipes in that call
hmmm what do you meant by this?

so for the process id, i'm not sure i'm doing in a right way, can i do smt like this
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()  
{
	char **argp; 	// THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
	int i,j,vpret;
	char buf[80]; 	// command line
	pid_t pid; 	// process ids
	int fd[2];

  	if (pipe(fd)== -1) 
		err_sys("pipe error");             // create a pipe 

	while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{
 		j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS
		
  		for (i=0;i<j;i++)
    			printf("%s",argp[i]);	// command to execute
		argp[j]=(char *)0;	// replace | with null

       		pid=fork();

        	switch (pid)
        	{
        		case -1:
				err_sys("fork failed");
				break;
        		case 0: // child
				close(fd[0]);	// close read end of pipe
				dup2(fd[1],1);	// make 1 same as write to end of pipe
				close(fd[1]);	// close excess fd
 				execvp(argp[0],argp);
				write(1,"exec fail\n",10);
				exit(1);
        		default: // parent
				close(fd[1]);	// close write end of pipe
				dup2(fd[0],0);	// make 0 same as read from end of pipe
				close(fd[0]);	// close excess fd
                		waitpid(pid, NULL, 0);
				write(1,"\nPrompt>",8);
				break;
        	}
		printf(" Process ID: %d\n",pid);  // print the process id
	}
	printf(" make argv test exited\n"); //successfully exit
}

Open in new window


well I know is wrong as the output seems to be the same~
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35187879
>> like which one will be the parent process and which one will be the child process on the command line?

If you call fork, then a new process (the child) is created. Both processes (the original, or the parent, and the new, or the child) are (for most intents and purposes) pretty much perfect copies. By checking the return value of fork, you can distinguish between what the child should do, and what the parent should do.


>> is there any possibility to not execute ONLY ONE command, like several command right before the pipe (|)?

If you mean forking off multiple children, and having each child execute different code, then sure, you can do that.


>> >>cannot use pipes in that call
>> hmmm what do you meant by this?

I mean that exec executes code in a process.
Pipes are a way to communicate between different processes, so exec cannot deal with that (since it only deals with one process).
To use pipes, you need to set them up separately. Please refer to the link I posted earlier - it contains an example of how you can do that.
0
 

Author Comment

by:crazy4s
ID: 35187941
so how do we determine which one is the parent process and child process?
is it the right most command will be the parent process and then forking every command to the left and that'll be the child process??? i'm confused~

>>If you mean forking off multiple children, and having each child execute different code, then sure, you can do that.
yes this is what i want, what should i do next?

i read through the fork and execvp func but it doesn't seems to have any pipe explanation there, did i missed out?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35187967
>> so how do we determine which one is the parent process and child process?
>> is it the right most command will be the parent process and then forking every command to the left and that'll be the child process??? i'm confused~

parent or child processes have nothing to do with pipes.

You use fork to create new (child) processes, and you can use pipes to communicate between those processes.



>> i read through the fork and execvp func but it doesn't seems to have any pipe explanation there, did i missed out?

You can do a search on that page for the word "pipe" ;)
0
 

Author Comment

by:crazy4s
ID: 35188006
r u referring to this?
if yes, so do you meant that after i fork i have to use pipes (below) but replace the command with argp[0]???
so basically the pipe does store the whole command (before the |)???
and we need to use pipe to access it???

#include <stdio.h>
main()
{
   FILE *fpipe;
   char *command="ls -l";
   char line[256];

   if ( !(fpipe = (FILE*)popen(command,"r")) )
   {  // If fpipe is NULL
      perror("Problems with pipe");
      exit(1);
   }

   while ( fgets( line, sizeof line, fpipe))
   {
     printf("%s", line);
   }
   pclose(fpipe);
}
                

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35188024
Can you explain to me what you think a pipe is, and how you think it works ?
0
 

Author Comment

by:crazy4s
ID: 35188038
not too sure how it works basically but it creates file descriptor that fd[0] is to read from and fd[1] is to write to???
0
 

Author Comment

by:crazy4s
ID: 35188061
i know is somekind like read from the parent process and write to child process... but i still don't get it where you get the parent process as i know when you call fork that's how the child process (new process) came?
0
 

Author Comment

by:crazy4s
ID: 35189028
hmm i still don't get how is the procedure goes, can someone explain step by step like should I create the pipe first or not, and then when should i use fork, when should i use exec, when to should i use dup2, i'm getting confused around this few system call in my prog! as i know the func of this stuffs but when all come together i dunno which one should come first.

any help will be greatly appreciated:)
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189130
assuming you have

   
pid_t child;
    ...
    child = fork();

Open in new window


then in any following code you can determine whether it is the parent (or not the child) by


   
if (child != 0)
    {
          // the following code will be performed by the parent only

Open in new window


that is - as infinity08 has already told you - cause the fork() returns the child pid in case the call is made by the parent and returns 0 if fork() was called by the child.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189174
but how do we know whether it is parent or child, hmm ok i know it determine by whether the return value is 0 or non 0, but is there other ways to determine that, because i don't quite understand?
0
 

Author Comment

by:crazy4s
ID: 35189252
one more thing  r we able to determine the parent and child based on the command line that the user input...
let's say
prompt>ls -l | cat test.c | cat test2.c
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189291
you already have the decision in the code you posted. look at the switch statement where it has three cases for the return value of fork. -1 is an error. 0 is the child. and != 0 is the parent.

the fork is made to be able to run two processes with one main and one executable file. it is an ambitious concept and not suitable for all purposes and you would need a little bit experience in multiprocessing and multithreading.

in my opinion you better would go with posix_spawn. it would create a new process and would return the pid of that process.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189360
so that's the only way to determine whether it's parent or child.
well because i wanted to understand this and cont doing with the code that i have if possible and the posix_spawn is smt that I haven reach i meant in his lecture.

can somebody lead me on this code, where i did wrong and what should i do next?

#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()
{
        char **argp;    // THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
        int i,j,vpret;
        char buf[80];   // command line
        pid_t pid;      // process ids
        int fd[2];

        if (pipe(fd)== -1)
                err_sys("pipe error");             // create a pipe

        while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
        {
                j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS

                pid=fork();

                for (i=0;i<j;i++)
                        printf("%s\n",argp[i]);

                switch (pid)
                {
                        case -1:
                                err_sys("fork failed");
                                break;
                        case 0: // child
                                close(fd[0]);   // close read end of pipe
                                dup2(fd[1],1);  // make 1 same as write to end of pipe
                                close(fd[1]);   // close excess fd
                                execvp(argp[0],argp);
                                exit(1);
                        default: // parent
                                close(fd[1]);   // close write end of pipe
                                dup2(fd[0],0);  // make 0 same as read from end of pipe
                                close(fd[0]);   // close excess fd
                                waitpid(pid, NULL, 0);
                                write(1,"\nPrompt>",8);
                                break;
                }
		printf("%s process id %d\n",argp[0], pid);
        }

        printf(" make argv test exited\n"); //successfully exit
}

Open in new window


here's the output with error
bronco:~> gcc test.c test2.c error.c
bronco:~> ./a.out
ls -l | cat test.c
ls -l
 cat test.c

ls -l
 cat test.c


Prompt>ls -l  process id 9974
Segmentation fault

0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189364
your input is not more than a string. there are no processes involved. if you ask for input before fork the parent and child share the string variable. if you do it after fork each process has its own input (means the user must do input twice) and if you ask for input in a sequence exclusive for the parent (or child) the string is available only for the parent (or child).

you only would get more processes if you parsed the input for the three comands and spawn a new process for each to execute the individual command. for that the pthread_spawn would be much better than the fork.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189434
so do you meant the pid=fork() should put before the while loop before the arguments?
but even if i tried to put it before the while loop, the output is still nothing difference so i'm sure i'm wrong somewhere else again...

bronco:~> gcc test.c test2.c error.c
bronco:~> ./a.out
ls -l | cat test.c
ls -l
 cat test.c


Prompt>ls -l  process id 10690
Segmentation fault

hmm can u give some example on the pthread_spawn , because it's something that i haven touch before!
0
 

Author Comment

by:crazy4s
ID: 35189448
hmm and what do you meant by inout not more than a string?
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189537
the function is called posix_spawn and not pthread_spawn. sorry for confusion.

i looked at the code (not very deeply) and i mean the fork is not necessary.

the makeargv does what i called 'parsing'. so it doesn't more than splitting the input into three commands. you see that each command will be executed via execvp . so the fork child process not really was used beside of that fd statements which i didn't understand til yet. in my opinion you simply could omit the fork and call the execvp from parent process for each command.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189615
yes the makeargv is to break the command line into token and this test.c is to split the command by |.
hmm do you meant just like this
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()
{
        char **argp;    // THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
        int i,j,vpret;
        char buf[80];   // command line
        pid_t pid;      // process ids

	while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
        {
                j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS

                for (i=0;i<j;i++)
                        printf("%s\n",argp[i]);

                switch (pid)
                {
                        case -1:
                                err_sys("fork failed");
                                break;
                        case 0: // child
				write (1,"exec fail\n", 10);
                                exit(1);
                        default: // parent
                                waitpid(pid, NULL, 0);
                                write(1,"\nPrompt>",8);
                                execvp(argp[0],argp);
                                break;
                }
		printf("%s process id %d\n",argp[0], pid);
        }

        printf(" make argv test exited\n"); //successfully exit
}

Open in new window


and the output

bronco:~> gcc test.c test2.c error.c
bronco:~> ./a.out
ls -l | cat test.c
ls -l
 cat test.c

exec fail
0
 

Author Comment

by:crazy4s
ID: 35189647
but i need to print the process id is it meant that i've to use the posix_spawn?? or can i do smt like pid_t pid; and then smt like pid = getppid()/getpid();
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189761
no, if you omit the fork you don't need the switch on pid either.

you only would use the token to execute a new command.

the problem with that is - and infinity08 already told you - that execvp cannot execute shell commands. you probably would need execute the command by calling

      system(command);

or probably

   execl(<shell path>, "sh", "-c", command, (char*)0);

for both i don't know how you could get the pid from the processes. and executing three commands isn't the same than executing one command with three piped subcommands.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189785
so what should i do in order to execute numerals command with numerals pipes with their process ids? what should i need to do?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35189801
Heh, I'm absent for a bit, and the discussion takes on a different direction.

Anyway, let's get back to my question in http:#35188024

You said in response to that :

>> not too sure how it works basically but it creates file descriptor that fd[0] is to read from and fd[1] is to write to???

>> i know is somekind like read from the parent process and write to child process... but i still don't get it where you get the parent process as i know when you call fork that's how the child process (new process) came?


As I said before : pipes allow communication between two processes (any two processes). There is no parent or child - just two processes. The parent/child terminology is purely related to fork, not to pipes.

Now, a pipe allows one process to send data to another process (through a pipe, so to speak). In order to make this work, you need to create such a "pipe" between the processes, and allow one of the processes to write into the pipe (ie. send data through it), and the other process to read from it (ie. receive data from it).

The shell allows you to create such pipes between two processes by using the '|' character (conveniently named the pipe character). For example :

        ls -l | more

would create two processes (one for the ls command, and onr for the more command). Note that each of these processes can have their own command line arguments (in the example above, the ls command has a -l argument).
A pipe is set up between these processes that connects the standard output of the first command (the ls command) to the standard input of the second command (the more command). Or, in other words, the write end of the pipe is attached to the ls command's standard output, and the read end of the pipe is attached to the more command's standard input.

That way, any output generated by the ls command can be read by the more command.


Does that explain better what pipes are, and how they work ?

Please make sure to ask questions if there's something about this that you still don't understand, because there's no sense in continuing if you don't fully understand what pipes are and how they work.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189830
i never did it myself (at linux), so i don't want to push you to a wrong direction.

but the trick could be to not execute the command directly but to execute the sh (as i posted with the execl call). if you would use the posix_spawn for that you should get returned the pid of the shell executed. i don't know if that is what you want.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189843
ok u did give an example for ls -l | more but will that also works with other arguments (not likely to be related) like ls -l | cat test.c will it still works the same?
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35189883
nice to have you back infinity08 :)

my knowledge about spawning commands at linux programmatically is a bit weak. so i hope you can help with that.

Sara
0
 

Author Comment

by:crazy4s
ID: 35189893
because i'm kind of new to this stuffs, so actually i don't really know which one should i use, but i think the lecturer's example would somehow related to the given assignments as he perform fork(), dup2() and execvp separately in different programs. and so now what he wants us to do  is to output each 'command' (separated by pipes) with a separate process along with its process id.
something like this
prompt> A B C | D E F
A B C process id 41
D E F process id 42

so any explanations and help will be greatly appreciated:)
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35189929
>> ok u did give an example for ls -l | more but will that also works with other arguments (not likely to be related) like ls -l | cat test.c will it still works the same?

Semantically, what that is trying to do, works the same : ie. create two processes, with a pipe between them.

However, this one seems like it is not very useful. The ls command generates standard output - so far so good. But the cat command (as it is used in the example you give) doesn't read anything from standard input, so the pipe is useless.


That shouldn't hold you back though, because our first goal is to understand how pipes work. Do you have any questions about that part ?
0
 

Author Comment

by:crazy4s
ID: 35189950
so the pipe basically works after you've forked a process, am i right?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190000
>>  so the pipe basically works after you've forked a process, am i right?

Forget about forking for now. It's unrelated to how pipes work.

Sure, you might use fork to create the processes that will then communicate through a pipe. But in order to understand pipes, you don't need to consider forking - just the existence of two processes.

Once you are confident that you understand pipes, we can move on from there.
0
 

Author Comment

by:crazy4s
ID: 35190020
then yes, as long as there're two or more processes then the pipe will be used to communicate through this processes!
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190104
Ok. So, say I have two applications. The first writes 2 lines of text on standard output, and when run on its own, would look like this :

        $ app1
        this is the first line
        this is the second line
        $

The second (app2) writes anything it reads from standard input to the file specified as its command-line argument.

Can you explain in your own words what would happen if I execute this on the shell ?

        $ app1 | app2 test.txt
0
 

Author Comment

by:crazy4s
ID: 35190121
it'll writes the two lines that you've entered in app1 into app2 test.txt?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190266
>> it'll writes the two lines that you've entered in app1 into app2 test.txt?

The two lines output by app1 will be written into the test.txt file by app2, because the standard output of app1 was sent to the standard input of app2 through the pipe.

Ok, so far so good. We seem to have pipes covered.

Now, what else do we need in order to solve your assignment. You haven't posted the exact wording of your assignment (could you do so, please), but from the examples you gave, it seems like the pipes are used incorrectly. So, let's clarify that first : what is your exact assignment ?
0
 

Author Comment

by:crazy4s
ID: 35190351
because i'm kind of new to this stuffs, so actually i don't really know which one should i use, but i think the lecturer's example would somehow related to the given assignments as he perform fork(), dup2() and execvp separately in different programs. and so now what he wants us to do  is to output each 'command' (separated by pipes) with a separate process along with its process id.
something like this
prompt> A B C | D E F
A B C process id 41
D E F process id 42

So based on either the 1st/2nd post of my code I have what should I do next or what should I edit in order to get such output?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190416
>> because i'm kind of new to this stuffs, so actually i don't really know which one should i use,

Note that I wasn't talking about your code, but rather about the examples of input you gave, like :

>> Prompt>ls -l | cat test.c | cat test2.c

This example does not seem useful when interpreting the pipes.


About your assignment, could you please post the exact text of the assignment ?
0
 

Author Comment

by:crazy4s
ID: 35190560
hmm what are the text that you need?
because he only gave us this
1)One shell, one forked processes, one pipe, two dup2s, two execl,
 ls -l      |       more
2)execvp a command in the middle of  multi-command user input
3)Write a program that will output each 'command' (separated by pipes) with a separate process along with its process id.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190725
>> because he only gave us this

That's good enough, thanks (note that you hadn't posted that before).

Ok, so since we covered pipes, let's move on to how we create new processes.

We have the main process already (ie. the initial one), and we can use fork to "clone" this process, and create a second (almost identical) process. The former (the original) is called the parent process, and the latter (the forked) is called the child process.

If the fork call succeeded, there will be 2 processes, each executing the same code, starting right after the fork call. We can use the return value of fork to determine which is the parent, and which is the child (as mentioned earlier), so we can have both processes execute different code.

That gives us a way to create a second process.

Note that you can call fork as many times as you want, and each time you'll create a new process in the same way as described above.


Does that make sense ?

Could you write some code that forks off two child processes, and makes them print some output ? Just to get used to how fork works.
0
 

Author Comment

by:crazy4s
ID: 35190858
i'm not sure exactly how to forks off two child processes but do you meant something like that hmm since you want two child process, do we need to call fork twice?

pid_t pid = fork();
switch (pid)
        {
        case -1: printf("bad fork\n");break;
        case 0: // child
                write(1,"something",10);
                pid=fork(); // not sure whether to put here or not?
                break;
        default: // parent
        write(fd[0],"more\nstuff",10);break;
        }

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35190870
oops hmm should be smt like this
pid_t pid = fork();
switch (pid)
        {
        case -1: printf("bad fork\n");break;
        case 0: // child
                write(1,"something",10);
                pid=fork(); // not sure whether to put here or not?
                break;
        default: // parent
                break;
        }

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190876
>>  i'm not sure exactly how to forks off two child processes but do you meant something like that

You seem to have copied that from somewhere, no ?

Could you write some of your own code that creates two child processes, and lets them print out something on standard output (like "This is child process 1" eg.) ?


>> hmm since you want two child process, do we need to call fork twice?

That's correct.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190893
>> pid=fork(); // not sure whether to put here or not?

Think about it. If you want the original process to create two child processes, where do the two fork calls go ?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35190910
Btw, try actually running the code, to see if it does what you expect or not. Just experiment a bit with some test code for forking. Play around with it to get a feeling for it :)
0
 

Author Comment

by:crazy4s
ID: 35191004
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

int main()
{

        pid_t pid = fork();
        if (pid == 0)
        {
                printf("child process 1\n");
        }
        pid = fork();
        if (pid == 0)
        {
                printf("child process 2\n");
        }
}

Open in new window


output

child process 1
child process 2
child process 2

not sure why the 2nd time i fork it prints out twice?
0
 

Author Comment

by:crazy4s
ID: 35191076
hmm regarding why it printed twice is it because the 2nd time i forked, i'm actually forking two processes, the original and the one that it forks (the 1st time), that's why i have child process 2 printed twice?
0
 
LVL 53

Accepted Solution

by:
Infinity08 earned 500 total points
ID: 35191200
>> not sure why the 2nd time i fork it prints out twice?

Remember that the child process is an exact copy of the parent process ?

So, it also executes the same code, starting from the line after the fork call.

Below is an overview of what each process does in that code.  Children starting with a P are started by the parent, children starting with a C are started by the first child. A * means that the code is executed in that process.

As you can see from the diagram, both the PARENT and CHILD P1 are executing the second fork, so each creates another child process. As such, the process tree becomes something like this :

        PARENT
              |---------> CHILD P1
              |                           \------------> CHILD C11
              |
              \---------> CHILD P2
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

int main()                                                 // PARENT        CHILD P1         CHILD P2         CHILD C11
{                                                          // ------        --------         --------         ---------

        pid_t pid = fork();                                //   *
        if (pid == 0)                                      //   *              *
        {                                                  //                  *
                printf("child process 1\n");               //                  *
        }                                                  //                  *
        pid = fork();                                      //   *              *
        if (pid == 0)                                      //   *              *                *                 *
        {                                                  //                                   *                 *
                printf("child process 2\n");               //                                   *                 *
        }                                                  //                                   *                 *
}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35191240
Or to look at the same code in a different light :
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

int main()
{

        pid_t pid = fork();                             // <--- PARENT starts executing from this line until the end of main
        if (pid == 0)                                   // <--- CHILD P1 starts executing from this line until the end of main
        {
                printf("child process 1\n");
        }
        pid = fork();
        if (pid == 0)                                   // <--- CHILD P2 and CHILD C11 start executing from this line until the end of main
        {
                printf("child process 2\n");
        }
}

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35191420
So this means the child process 1 is P1 and child process 2 is P2 and C11... so what should I do so that the parent executes 2 children?
0
 

Author Comment

by:crazy4s
ID: 35191573
Is there any possibility to have the second fork place within here (remove lines starting from 2nd fork)
if (pid!=0)
{
    pid=fork();
    if (pid=0)
       printf("child process 2");
}
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35192277
>> Is there any possibility to have the second fork place within here

You could do that (or have an else block for the parent part).

Or you could include an exit or return call as soon as you want the child to end.
0
 

Author Comment

by:crazy4s
ID: 35192693
so back to the question that you asked me to do, is it correct to do it like that:
but the prob is i'm not sure the output are child P1 and child P2 or not?
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{

        pid_t pid = fork();
        if (pid == 0)
        {
                printf("child process 1\n");
        }
        if (pid != 0)
        {
                pid = fork();
                if (pid == 0)
                {
                        printf("child process 2\n");
                }
        }
 
}

Open in new window



cpfoo@csa04 as4$ gcc t.c
cpfoo@csa04 as4$ ./a.out
child process 1
child process 2
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35192760
>> so back to the question that you asked me to do, is it correct to do it like that:

As in my previous post : yes.

You could also use else instead of if (pid != 0) :

        pid_t pid = fork();
        if (pid == 0) {
            /* the child */
        }
        else {
            /* the parent */
        }

or you could use exit or return to end the child when needed :

        pid_t pid = fork();
        if (pid == 0) {
            /* the child */
            exit(0);
        }
        /* the parent */

Note that you probably also want to check whether fork succeeded without error (in the parent), by checking the return value for -1.
0
 

Author Comment

by:crazy4s
ID: 35192792
yes i know that i should've assign an error msg for that just in case it returns negative value.
so to put an exit(0) at the child can actually exit the child and can it prevents not to being forked again?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35192813
exit(0) ends the process from which it's called from. If you call it from the child, then you end the child process, preventing it from doing anything else, including forking (it's no longer running after all).
0
 

Author Comment

by:crazy4s
ID: 35192820
okay i get what you meant, so what should we do next?
i really appreciate that you teaches me step by step:)
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35192877
So, we've got forking now.

Let's talk about what the exec family of functions does.

execl (and consorts) replaces the current process (the process it's called from) with a new process specified by the arguments.

For example, if you do :

        execl("/bin/ls", "/bin/ls", 0);

you replace the current process with a process that runs /bin/ls

Try replacing one of the child printf's in your last code (from http:#35192693) with the above line to see what happens.
0
 

Author Comment

by:crazy4s
ID: 35192989
i tried to run it and the output is same as the command ls

child process 1
a.out     t.c       t.c.save

is there any possibility for the first 2 arguments in execl to be different? i read the link that you've provided earlier saying that the first argument is the path and the 2nd should be the argv that we input(i'm not sure for this?)
the current process u meant is after the 2nd time fork is it? so instead it forks another child from the parent it actually does the command ls?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193065
>> i tried to run it and the output is same as the command ls

That's to be expected, because /bin/ls is one and the same as the ls command ;)

Notice how one of your child processes now runs /bin/ls instead of the printf ? You can experiment with this further by placing a printf before and after the execl call ... Try to guess what'll happen then, and then test it out to see if you were right.


>> is there any possibility for the first 2 arguments in execl to be different?

In the case of execl, that's not very useful, but we'll get to that later.


>> the current process u meant is after the 2nd time fork is it?

The current process I was referring to, is whatever process you call the exec from. It could be the first child, or the second child, or even the parent.
Whatever process you run exec from will be replaced by a process specified in the arguments.


>> so instead it forks another child from the parent it actually does the command ls?

The fork call is to create a new process.
The exec call is to replace that new process with another. The exec call doesn't create a new process, it just changes an already existing one.
0
 

Author Comment

by:crazy4s
ID: 35193118
before running i was expect it to be smt like this
child process 1
child process 2
a.out     t.c       t.c.save
a.out     t.c       t.c.save //as i thought the 3rd printf will be replaced by the ls command too

int main()
{

        pid_t pid = fork();
        if (pid == 0)
        {
                printf("child process 1\n");
        }
        if (pid != 0)
        {
                pid = fork();
                if (pid == 0)
                {
                        printf("child process 2\n");
                        execl("/bin/ls", "/bin/ls", 0);
                        printf("child process 3\n");
                }
        }

}

Open in new window


but it seems that i've guessed wrongly that it only prints out once for the execl
so is this happened because of the null terminated as the last argument in execl?
is the null terminated matters in this condition?

child process 1
child process 2
a.out     t.c       t.c.save
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193154
>> but it seems that i've guessed wrongly that it only prints out once for the execl

It happens because the moment you call exec, the whole process is replaced with whatever you specify as arguments to the exec call.

So, whatever comes after the exec call is no longer executed. It's no longer there (since it has been replaced by the /bin/ls code in this case).

The first printf is still executed, because it's before the exec call - ie. the original code hasn't been replaced yet.


Now, try moving the execl call to the first child process code. Can you guess what will happen ?
0
 

Author Comment

by:crazy4s
ID: 35193199
yes i guessed it correctly for this time
int main()
{

        pid_t pid = fork();
        if (pid == 0)
        {
                printf("child process 1\n");
                execl("/bin/ls", "/bin/ls", 0);
                printf("nothing\n");
        }
        if (pid != 0)
        {
                pid = fork();
                if (pid == 0)
                {
                        printf("child process 2\n");
                        printf("child process 3\n");
                }
        }
 
}

Open in new window


child process 1
a.out     t.c       t.c.save
child process 2
child process 3

as whatever comes after execl in that current loop only are not executed!
0
 

Author Comment

by:crazy4s
ID: 35193238
one more thing that i wanted to clarify....
so the above code is still works the same is it?
child process 1 and the execl are under P1
child process 2 and child process 3 are actually under P2
am i correct up to this point using the diagram that you've drew ealier?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193283
>> child process 1 and the execl are under P1
>> child process 2 and child process 3 are actually under P2

That's correct. So :
int main()                                                 // PARENT        CHILD P1         CHILD P2
{                                                          // ------        --------         --------

        pid_t pid = fork();                                //   *
        if (pid == 0)                                      //   *              *
        {                                                  //                  *
                printf("child process 1\n");               //                  *
                execl("/bin/ls", "/bin/ls", 0);            //                  *
                printf("nothing\n");                       //                  *
        }                                                  //                  *
        if (pid != 0)                                      //   *              *
        {                                                  //   *
                pid = fork();                              //   *
                if (pid == 0)                              //   *                                *
                {                                          //                                    *
                        printf("child process 2\n");       //                                    *
                        printf("child process 3\n");       //                                    *
                }                                          //                                    *
        }                                                  //   *
 
}

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35193297
okay done so what to do next?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193299
Notice that I included the printf("nothing\n"); line in the P1 process. That is because there is one case in which it can get run. If all goes well, the exec call succeeds, and that printf is never run, but if the exec call fails, then the second printf is still run.
0
 

Author Comment

by:crazy4s
ID: 35193320
yes i know is only when the exec call success and so whatever comes after will be ignored but if it fails then whatever comes after will carry on, right?
0
 

Author Comment

by:crazy4s
ID: 35193347
btw in what case will the execl fails?
and for the 1st and 2nd argument in execl, is that a possibility to be different (currently in your ex both are the same)?
i know the 1st argument should be smt like a path and the 2nd argument is supposed to be some argv, but not really get how this works...
0
 
LVL 53

Assisted Solution

by:Infinity08
Infinity08 earned 500 total points
ID: 35193364
>> okay done so what to do next?

There is one more thing to say about exec, and that is that there are different functions in the exec family. All of them do the same thing (replace an existing process with one specified by the arguments), but they all do it in a slightly different way.

Have a look at the man page for exec :

        man exec

to see which ones are available on your platform.

For example, execv is very similar to execl, except that the arguments are passed as an array instead of separate arguments. So :

        execl("/bin/ls", "/bin/ls", 0);

is equivalent to :

        char* args[2] = { "/bin/ls", 0 };
        execv("/bin/ls", args);

Have a read through the man page, and try to understand why the first two arguments to execl are the same, and why there is a 0 as third argument.
Try to play with passing arguments to /bin/ls to see if you can get it to show the ls output differently (try the -l argument eg.).
0
 

Author Comment

by:crazy4s
ID: 35193550
>>The  pathname specified  in the interpreter file is passed as arg0 to the interpreter.
that's why the first and second argument are the same as the name for the file(command)?

and for the null at the end of the execl is to end the argument list....

i tried to replace ls with -l and the execl fails! because -l is not a command.
hmm can you give an example for execl if it has more than 2 arguments, how will it looks like?
and how does it differ from just using 2 arguments?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193574
>> i tried to replace ls with -l and the execl fails! because -l is not a command.

ls is the command, and -l is the argument.

If you'd execute it on the command line, you'd use :

        ls -l

Try running that, and then try getting the same output using any exec function your prefer (just by passing it one extra argument "-l").
0
 

Author Comment

by:crazy4s
ID: 35193628
ahh that's what i was actually asking... i mistaken it at first
int main()
{

        pid_t pid = fork();
        if (pid == 0)
        {
                printf("child process 1\n");
                execl("/bin/ls", "/bin/ls","-l", 0);
                printf("nothing\n");
        }
        if (pid != 0)
        {
                pid = fork();
                if (pid == 0)
                {
                        printf("child process 2\n");
                        printf("child process 3\n");
                }
        }

}

Open in new window



child process 1
total 32
-rwxr-xr-x   1 cpfoo    ugrad       6280 Mar 22 16:23 a.out
-rw-r--r--   1 cpfoo    ugrad        384 Mar 22 16:23 t.c
-rw-------   1 cpfoo    ugrad        289 Mar 22 15:06 t.c.save
child process 2
child process 3

so the extra argument actually works together with the 1st argument, they compile it in 1 command!
0
 

Author Comment

by:crazy4s
ID: 35193653
hmm but i'm not really know how it works for execvp, can you give some explanations for it too?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193665
>> so the extra argument actually works together with the 1st argument, they compile it in 1 command!

The arguments are arguments to the command. They are passed to exec together with the command itself.

If you want, you can experiment with the other types of exec functions (like execv eg.) to see how they work.

Once you feel comfortable with them, you should have enough information to complete your assignment. So, have a go at splitting up an input string in commands, forking a new child for each of them, and using exec to actually execute the command.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35193696
>> hmm but i'm not really know how it works for execvp, can you give some explanations for it too?

Sure. execvp works in the same way as execv (see my example earlier), except that it doesn't require a complete path for the executable.

Since the /bin directory is in your PATH, with execvp, you can just use "ls" instead of "/bin/ls". Other than that, the way it works is the same as execv.
0
 

Author Comment

by:crazy4s
ID: 35193987
yes i've tried for execv and execvp and it works the same!
btw since argument 1 and 2 have to be the same in execl for execv and execvp there's a null terminated at the end, does it meant to have the same meaning as why execl has a null terminated as the last argument too?
               
 char* args[2] = { "ls", 0 };
 execvp("ls", args);

and regarding my assignment, once i have separate each command by pipes, i need to use fork to fork each command (just like i fork the process but this time i only need to fork 1 child process for each command is it?) then only i execvp the command?
but how do i print out their process ids?
and in this assignment do i need to use dup2()?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35194076
>> does it meant to have the same meaning as why execl has a null terminated as the last argument too?

Yes. The NULL at the end indicates the end of the argument list, and it has to be there.


>> (just like i fork the process but this time i only need to fork 1 child process for each command is it?)

Correct.


>> then only i execvp the command?

Yep.


>> but how do i print out their process ids?

Remember that fork returns the child process id to the parent ? The parent can print the process id for every child it creates.


>> and in this assignment do i need to use dup2()?

Try getting it to work without involving pipes first.

You can then use pipe to create the pipe (see : man 2 pipe), and dup2 to redirect stdin and stdout as appropriate (see : man 2 dup2).
0
 

Author Comment

by:crazy4s
ID: 35194930
hmm can you tell what is this error meant to be (is in makeargv.c)
well at first i compiled this at another machine it was fine, but when i changed to another machine this error pops up???

In file included from /usr/include/string.h:18,
                 from as4b.c:2:
/usr/include/iso/string_iso.h:59: error: syntax error before "extern"
0
 

Author Comment

by:crazy4s
ID: 35195054
i used back the previous machine to compile this file
hmm although i understand how it works but i'm abit confused
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ourhdr.h"

extern int makeargv(char *, char * , char ***);

int main()
{
        char **argp;    // THE POINTER TO THE ARRAY OF POINTERS TO BE FILLED BY
        int i,j;
        char buf[80];   // command line
        pid_t pid;      // process ids
        
	while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
        {
                j=makeargv(buf,"|",&argp); // J = NUMBER OF TOKENS

                for (i=0;i<j;i++)
		{
                        printf("%s\n",argp[i]);
		}
        	pid=fork();
                switch (pid)
                {
                        case -1:
                                err_sys("fork failed");
                                break;
                        case 0: // child
                                execvp(argp[0],argp); //execute the command
				write (1,"exec fail\n", 10); //if execvp fails then print this message
                                exit(1);
                        default: // parent
                                write(1,"\nPrompt>",8);
				printf("%s process id %d\n",argp[0], pid);
                                break;
                }

        }

        printf(" make argv test exited\n"); //successfully exit
}

Open in new window


output:
bronco:~> ./a.out
ls -l | more
ls -l
 more


Prompt>ls -l  process id 13813
exec fail

so at first i've already output the command (separated by pipes) and each command is store in argp(i) so what i need to do next is to fork every command (in order to have a new child process for every command) i'm not sure is it correct to place before the switch pid but i think should be because right after calling the fork we are actually checking whether it is child or not if yes then we've to print the entire argument and then we print the process id at the parent?
but the thing coming out is a little weird... hmm so this>> Prompt>ls -l  process id 13813
is actually the parent id is it?
how can i do so that the Prompt will output right after i hit ./a.out then the user input command line and not input the command line first then only output the prompt (i know this shows that it's the parent) is there any possibility?? or should i just delete the prompt line (because is nothing)???
i hope you don't get confused what i'm asking... hahaha because i'm getting myself confused=P
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35196297
>> hahaha because i'm getting myself confused=P

I can sense that :) So, let's take it a bit more slowly.


I notice that you used the same code as earlier - ie. the code that was causing you problems.

I recommend that you try writing it from scratch with the new knowledge you gained about using fork and exec. It'll work a lot better ;)


Another advice is to split your task up in sub-tasks, and work on each separately. For example :

1) first write some code that gets input from the user in a loop, and displays the input to the user (without doing anything with it).

2) as soon as that works, split up that input into commands, and display the separate commands to the user. Make sure there is no whitespace before and after the commands any more (that includes newline characters).

3) as soon as that works, try splitting up each of the commands into the executable and its arguments, and show them separately to the user (again making sure there's no whitespace).

4) as soon as that works, you can fork off a child for each of the commands, and print the child's process id in the parent.

5) as soon as that works, make the child execute the command (with its arguments).

Take it step by step like this, and don't move to the next step until you're sure that the code works for the current step.

When you encounter problems, just ask :)
0
 

Author Comment

by:crazy4s
ID: 35202504
now i was trying to do separate the commands by pipe but somehow i can only display the first command but not for those after pipes, is there any other ways to do the strtok automatically by not doing it one by one?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i;
	char *command;
        char buf[80];   // command line
	while(strcmp(fgets(buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{
		command = strtok(buf,"|\n");
		printf("%s\n",command);
	}
|

Open in new window


output:
ls -l | more
ls -l
0
 

Author Comment

by:crazy4s
ID: 35202686
hmm i was able to tokenize each of the command but i get a segmentation fault after printing the commands, and i think the problem was actually occur at the for loop, i'm not really sure what to put for the 2nd parameter
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i, j;
	char *command;
        char buf[80];   // command line
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|\n");
		printf("%s\n", command);
		for ( i = 0; i < 80; i++) // NOT SURE WHAT TO PUT AT THE 2ND ONE
		{
			command = strtok(NULL, "|\n");
			printf("%s\n", command);
		}
		
	}
}

Open in new window


output:

cpfoo@csa04 as4$ gcc t.c
cpfoo@csa04 as4$ ./a.out
ls -l | more | more
ls -l
 more
 more
Segmentation Fault (core dumped)

and one more thing i don't really understand >> Make sure there is no whitespace before and after the commands any more
hmm where should this be???
0
 

Author Comment

by:crazy4s
ID: 35202779
the whitespace you meant here is it like in the command line we use to enter like this
ls -l | more << so there's a whitespace appear before the more so we need to eliminate that before and after the pipe, is this what you meant to be?
how can we eliminate those white space only for those that are before and after the pipe?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35204751
Sorry for the delay ...

>>             for ( i = 0; i < 80; i++) // NOT SURE WHAT TO PUT AT THE 2ND ONE

strtok will return NULL as soon as the last token has been read :

        http://www.cplusplus.com/reference/clibrary/cstring/strtok/

so you need to continue this loop until strtok returns NULL, not for a fixed 80 times.


>> ls -l | more << so there's a whitespace appear before the more so we need to eliminate that before and after the pipe, is this what you meant to be?

That is indeed what I meant.


>> how can we eliminate those white space only for those that are before and after the pipe?

When you split up the command into arguments, make sure to use whitespace (spaces, newlines, tabs, etc.) as delimiter for strtok. strtok will treat sequential delimiter characters as one single delimiter.
0
 

Author Comment

by:crazy4s
ID: 35205021
so did you meant to be something like this
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i, j;
	char *command;
        char buf[80];   // command line
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"| \t\n");
		while(command != NULL)
		{
			printf("%s\n",command);
			command = strtok(NULL,"| \t\n");
		}
		
	}
	return 0;
}

Open in new window


output :

bronco:~> ./a.out
ls -l | more than i want | that is
ls
-l
more
than
i
want
that
is

so this is doing what you want for step 2??? but do i need to include | as the delimiters too?
and about step 3 >>try splitting up each of the commands into the executable and its arguments???
how do split the commands again where there're already in token form, is this something to do with execvp???
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205202
It is somewhat what I want, but you still need two levels of strtoks :

The first splits up the line in commands by using | as a delimiter.
The second splits up each command by using whitespace as a delimiter.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205203
>> It is somewhat what I want

typo : It is somewhat what I meant ;)
0
 

Author Comment

by:crazy4s
ID: 35205246
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i, j;
	char *command;
        char buf[80];   // command line
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			command = strtok(NULL,"|");
		}
		command = strtok(buf," \t\n");
		printf("separate by space: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			command = strtok(NULL," \t\n");
		}	
	}
	return 0;
}

Open in new window


do you meant like this... btw what can i do so that when they do the 2nd time strtok for all the lines because it tends to appear only for the first line?

bronco:~> gcc t.c
bronco:~> ./a.out
ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
ls
-l
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205266
The line :

>> printf("%s\n",command);

indicates the location where you have a command.

That is the location where you want to split up the command in arguments.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205270
>> The line :

(the first line obviously)
0
 

Author Comment

by:crazy4s
ID: 35205306
hmm i don't quite get what you meant...
let me get this clear first.... so first you want me to output the command that's separated by | and then secondly you want me to further split the commands by space, is this what you meant?
hmm output should be smt like this????
ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
ls
-l
more
than
that
or
lesser

	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
                        // DO YOU MEANT TO PUT THE STRTOK FOR SPACE HERE??
			command = strtok(NULL,"|");
		}

	}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205354
>> so first you want me to output the command that's separated by | and then secondly you want me to further split the commands by space, is this what you meant?

Yes. Do you understand why that needs to be done ?

Don't just blindly follow my instructions. Understand them first ;)


>>                         // DO YOU MEANT TO PUT THE STRTOK FOR SPACE HERE??

Not exactly. strtok is not re-entrant, so you cannot run multiple strtok loops at once.

What you can do however, is store the separate commands in an array, and when you have placed all commands in the array, you can iterate over it, and for each command use strtok to split it up in arguments using whitespace as a delimiter.
0
 

Author Comment

by:crazy4s
ID: 35205437
yes i understand but i'm just confused when you said to spilt the commands after the first printf...so that's why i want to double confirm....
btw i tried to put into array for the first strtok but i got an warning msg >> t.c:22: warning: assignment makes integer from pointer without a cast

the codes is it something like this...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j;
	char *command;
        char buf[80];   // command line
	char temp [80];
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = &command;
			command = strtok(NULL,"|");
			i++;
		}
		while(fgets((char *)temp, 80, stdin)!=NULL) 
		{
			command = strtok(temp," \t\n");
			printf("separate by space: \n");
			for(j = 0; j < i; j++)
			{
				printf("%s\n",command);
				command = strtok(NULL," \t\n");

			}
		}

	}
	return 0;
}

Open in new window


0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205574
>> yes i understand but i'm just confused when you said to spilt the commands after the first printf...

Ok. I'm sorry for that ;) I can see how my statement caused confusion, because I didn't put much care in its wording ...



>>                   temp [ i ] = &command;

temp is an array of char.

You probably want to make it an array of char* instead :

        char* temp[80] = { 0 };

Also, command is already a char*, so don't take the address of it :

        temp[ i ] = command;


Then :

>>             while(fgets((char *)temp, 80, stdin)!=NULL)

since temp is an array of char*, you just need to iterate over it, and use strtok for each of the char*'s in the array.
0
 

Author Comment

by:crazy4s
ID: 35205602
is ok:)
>>since temp is an array of char*, you just need to iterate over it, and use strtok for each of the char*'s in the array.
so do you meant what i'm doing is correct, i just need to repeat what i did strtok for the first time but i got an warning msg again:(
t.c:28: warning: passing argument 1 of 'strtok' from incompatible pointer type
>>                   command = strtok(temp," \t\n");

while(fgets((char *)temp, 80, stdin)!=NULL) 
		{
			command = strtok(temp," \t\n");
			printf("separate by space: \n");
			for(j = 0; j < i; j++)
			{
				printf("%s\n",command);
				command = strtok(NULL," \t\n");

			}
		}

Open in new window



0
 

Author Comment

by:crazy4s
ID: 35205619
hmm i solved the warning msg but it doesn't print out anything for second while loop
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j;
	char *command;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}
		while(fgets((char *)temp, 80, stdin)!=NULL) 
		{
			command = strtok((char *)temp," \t\n");
			printf("separate by space: \n");
			for(j = 0; j < i; j++)
			{
				printf("%s\n",command);
				command = strtok(NULL," \t\n");

			}
		}

	}
	return 0;
}

Open in new window


output:

ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205632
Why are you using fgets ?

fgets is used to read data from standard input (ie. have the user enter a string).
0
 

Author Comment

by:crazy4s
ID: 35205651
yeah you're right.
but what should i do to strtok the array?
can i do just like this, remove the fgets line??? but i know is wrong because it gives me a segmentation fault...
ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
???????????
Segmentation fault

>>
            command1 = strtok((char *)temp," \t\n");
            printf("separate by space: \n");
            for(j = 0; j < i; j++)
            {
                  printf("%s\n",command1);
                  command1 = strtok(NULL," \t\n");

            }
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205668
>> but what should i do to strtok the array?

You don't strtok the array.

You iterate over each item in the array, and for each of them (which has the type char*), you can use strtok to tokenize it.
0
 

Author Comment

by:crazy4s
ID: 35205699
hmm i tried using like this but how can i do so that i can go numerous time for the 2nd strtok
            printf("separate by space: \n");
            for(j = 0; j < i; j++)
            {
                  command1 = strtok((char *)temp[j]," \t\n");
                  printf("%s\n",command1);

                  command1 = strtok(NULL," \t\n");
                  printf("%s\n",command1);

            }
currently the output is :

ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
ls
-l
more
than  << that is missing because strtok only runs once after the first one
or
lesser
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 53

Expert Comment

by:Infinity08
ID: 35205708
That looks better.

>> hmm i tried using like this but how can i do so that i can go numerous time for the 2nd strtok

Put it in a loop - just like you did for the command.

Btw, instead of using command1, why not use a more descriptive variable name, like argument ?
0
 

Author Comment

by:crazy4s
ID: 35205745
i tried to put in a while loop but i got a segmentation fault
ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
ls
-l
Segmentation fault

		
printf("separate by space: \n");
	for(j = 0; j < i; j++)
	{
			argument = strtok((char *)temp[j]," \t\n");
			printf("%s\n",argument);
			while (argument != NULL)
			{

				argument = strtok(NULL," \t\n");
				printf("%s\n",argument);
			}

	}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205755
Inside that while loop, the printf is supposed to come before the strtok.

Because you want to print the current token, not the newly returned that might be NULL.
0
 

Author Comment

by:crazy4s
ID: 35205780

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}
		

	}
	return 0;
}

Open in new window


output
ls -l | more than that | or lesser
separate by |:
ls -l
 more than that
 or lesser

separate by space:
ls
-l
more
than
that
or
lesser

arhh i missed that mistakes again...ok now i've successfully separate all the arguments... so what should i do next is it to fork the command... but i need to fork (ls -l) , (more than that), (or lesser) separately right? how do i so that i make sure these commands and it's argument are together?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35205807
So, now, you have split the input up in commands, and you have split each command up into its arguments.

You can now indeed fork off a child process for each of the commands. Don't do the exec just yet - just the forking (and show the process id together with the command).
0
 

Author Comment

by:crazy4s
ID: 35205833
i know in each array of the command we have those arguments in it hmm but how do i fork those arguments in the array together like ls -l??? i'm abit blur here...
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206328
>>  i know in each array of the command we have those arguments in it hmm but how do i fork those arguments in the array together like ls -l??? i'm abit blur here...

You don't fork arguments. Remember that forking creates a new process ? That's all it does. The arguments are not used for it. That's for later, when we will add exec.

Have a read back through this thread for the discussion we had on how forking works.
0
 

Author Comment

by:crazy4s
ID: 35206449
hmm yes i know when i do fork it creates a new process but how is it related to those commands??
do i just do the normal fork after all the strtok stuffs?? can you give some example so is easier for me to understand?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206464
>> hmm yes i know when i do fork it creates a new process but how is it related to those commands??

For every command, we need to create a new child process.

So, we start by doing just that : create a new child process for every command. Don't try actually running (exec-ing) the command just yet - that's for later. For now, just create a new child process, and print the process id for it, (together with the command) to the screen to show that it works.
0
 

Author Comment

by:crazy4s
ID: 35206516
so do i just do the normal fork like what you call me to try earlier... hmm like that?
but since we have let's say more than one command does it meant that i've to fork numerous times too??
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}
		

	}

	pid_t pid = fork();  //i'm not really sure how should i fork a command???
        switch (pid)
        {
        	case -1:
			printf("fork failed");
			break;
        	case 0: // child
			exit(0);
        	default: // parent
			printf("%s process id %d\n", command, pid);
			break;
        }
	return 0;
}

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35206545
as earlier you want me to try to fork 2 child process, that's from one parent which is the original shell right? but when comes to commands i don't really know how should i fork every command so if we have to 2 commands so we'll have 2 new child process (ls -l | more)??
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206627
As I said before : don't just follow my instructions blindly. Try to understand them first.

You have been given an assignment. The goal of this thread is to help you complete that assignment.

Now, if you look at the assignment, what is it that needs to happen ? And how would you do that ? How do the steps I mentioned earlier fit into that goal ?
0
 

Author Comment

by:crazy4s
ID: 35206675
yes i was trying my best to understand but i just can't get it how it works to fork the commands, can you explain in more details or maybe like diagrams or examples will helps me to understand better because i'm quite weak in imagination?? hope you don't mind:)
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206747
You don't "fork commands". That doesn't make sense.

What you do is : for every command, you fork a child process. It doesn't mean that the child process depends on what the command is - it just means that you want as many child processes as there are commands. Nothing more, nothing less.
0
 

Author Comment

by:crazy4s
ID: 35206815
ahh so in this means i don't fork the commands but i fork the no of commands given???
is that right if i do it like this>>>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}
		

	}

	pid_t pid;
	for (k = 0; k < i; k++)
	{
		pid = fork();
		if (pid == 0)
		{
			exit(0);
		}
		else if (pid < 0) 
		{
			printf("fork failed");
		}
		else
		{
			printf("%s process id %d\n", command, pid);
		}
	}
        
	return 0;
}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206957
You're starting to get the idea.

Now, there are still a few problems. For example :

(a) you have an array of commands, but you don't use it when printing the process id of the forked process.

(b) you already have a loop that iterates over the array of commands. You probably want to place the fork code in that existing loop, rather than create a new one. That way you'll be able to use the tokenized arguments in the child process (in the next step).
0
 

Author Comment

by:crazy4s
ID: 35206960
i tried editing my code again and again and finally i get this result...hmm but the command is not displaying in full they just display the first one??
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}
		
	for (k = 0; k < i; k++)
	{
		pid = fork();
		if (pid == 0)
		{
			exit(0);
		}
		else if (pid < 0) 
		{
			printf("fork failed");
			exit(1);
		}
		else
		{
			printf("%s process id %d\n", temp[k], pid);
		}
	}
        }
	return 0;
}

Open in new window



ls -l | more
separate by |:
ls -l
 more

separate by space:
ls
-l
more
ls process id 22346
 more process id 22347
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206971
>> hmm but the command is not displaying in full they just display the first one??

See (a) in my previous post
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35206991
Sorry - that's not right. I meant (b).

The "problem" you describe is because you've already used strtok on the command, so you've already split it up in tokens.

If you do the fork in the already existing loop, (before splitting up the command in arguments), you'll have better luck.
0
 

Author Comment

by:crazy4s
ID: 35207014
is getting better but i don't understand why the 2nd command tends to have a next line after it??
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}

		
		for (k = 0; k < i; k++)
		{
			pid = fork();
			if (pid == 0)
			{
				exit(0);
			}
			else if (pid < 0) 
			{
				printf("fork failed");
				exit(1);
			}
			else
			{
				printf("%s process id %d\n", temp[k], pid);
			}
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window

ls -l | more
separate by |:
ls -l
 more

ls -l  process id 22412
 more
 process id 22413
separate by space:
ls
-l
more
0
 

Author Comment

by:crazy4s
ID: 35207026
and is there any ways to eliminate the space before "more" or just leave it?
0
 

Author Comment

by:crazy4s
ID: 35207039
cont the previous post .. because we're going to do the execvp later that will print out the commands and the process id
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35207061
>> is getting better but i don't understand why the 2nd command tends to have a next line after it??

>> and is there any ways to eliminate the space before "more" or just leave it?

That is the reason I mentioned in step 2 to remove whitespace before and after the commands.

Don't do that with strtok though - take care of that by other means.

For leading whitespace, you can just increment the command pointer to point past it, and for trailing whitespace, you can set them to '\0'
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35207072
Btw, you still have the fork in its own loop.

Why not include the fork in the already existing loop (the loop where you split up the commands into arguments) ?
0
 

Author Comment

by:crazy4s
ID: 35207109
you meant like this??
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
		}


		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			pid = fork();
			if (pid == 0)
			{
				exit(0);
			}
			else if (pid < 0) 
			{
				printf("fork failed");
				exit(1);
			}
			else
			{
				printf("%s process id %d\n", temp[j], pid);
			}
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window


because the output is kind of weird that's why i use another loop...

ls -l | more
separate by |:
ls -l
 more

separate by space:
ls -l  process id 22523
ls
-l
 more
 process id 22524
more

>>For leading whitespace, you can just increment the command pointer to point past it, and for trailing whitespace, you can set them to '\0'
i don't really get what this means, how do i increment the command pointer and for after replace with null?
0
 

Author Comment

by:crazy4s
ID: 35207139
>>Don't do that with strtok though - take care of that by other means.
hmm you meant which one the first(command) or the second one(arguments)??
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35207322
>> because the output is kind of weird that's why i use another loop...

The output is only weird because there is still whitespace at the beginning and end of the commands.


>> >>Don't do that with strtok though - take care of that by other means.
>> hmm you meant which one the first(command) or the second one(arguments)??

We're talking about the whitespace in front and after the commands, not the arguments.



>> >>For leading whitespace, you can just increment the command pointer to point past it, and for trailing whitespace, you can set them to '\0'
>> i don't really get what this means, how do i increment the command pointer and for after replace with null?

If you have a pointer to the first character in a string, then incrementing that pointer will make it point to the second character in the string. Play around a bit with code like this in a separate project, and try to understand why you get that output :
char str[] = "Example string";

char* p = str;
ptintf("%s\n", p);

++p;
ptintf("%s\n", p);

p[6] = '\0';
ptintf("%s\n", p);

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35207535
hmm i tried to play around with it, and try to add ++command in the command loop but i got a segmentation fault after it...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			i++;
			++command;
		}


		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			pid = fork();
			if (pid == 0)
			{
				exit(0);
			}
			else if (pid < 0) 
			{
				printf("fork failed");
				exit(1);
			}
			else
			{
				printf("%s process id %d\n", temp[j], pid);
			}
			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window


if i put before this line >> command = strtok(NULL,"|"); // nothing difference still have the white space
but if put after this line, yes it remove the white space but i got a segmentation fault.

ls -l | more
separate by |:
ls -l
more

Segmentation fault

0
 

Author Comment

by:crazy4s
ID: 35207854
here i added in the null at the end of each command but well i know the segmentation will still be there~
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	char *command;
	char *argument;
        char buf[80];   // command line
        char* temp[80] = { 0 };
	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // USER COMMAND
	{

		command = strtok(buf,"|");
		printf("separate by |: \n");
		while(command != NULL)
		{
			command[strlen(command)-1] = '\0';
			printf("%s\n",command);
			temp [i] = command;
			command = strtok(NULL,"|");
			++command;
			i++;
		}

		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			pid = fork();
			if (pid == 0)
			{
				exit(0);
			}
			else if (pid < 0) 
			{
				printf("fork failed");
				exit(1);
			}
			else
			{
				printf("%s process id %d\n", temp[j], pid);
			}

			argument = strtok((char *)temp[j]," \t\n");

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window



ls -l | more than that | or lesser
separate by |:
ls -l
more than that
or lesser
Segmentation fault
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35208345
The segmentation fault is because you increment a NULL pointer.

Don't do that. Only increment a valid (non-NULL) pointer.

Also : don't just increment it blindly. Only increment it if there's actually a whitespace character in the beginning. And note that there might be more than one whitespace character, so you might have to increment more than once.
0
 

Author Comment

by:crazy4s
ID: 35208479
Hmm but I increment the pointer  after the strtok so right after the pipe should be a space right the null pointer that I put is at the end of each command??? Hmm maybe I mistaken smt...
ahhh then can I put a strcmp if given the char of the command is a space then we'll increment it??? Will that works?? This means that I'll compare till I get a non-space char????
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35208541
>> Hmm but I increment the pointer  after the strtok

What if strtok returned NULL ?


>> so right after the pipe should be a space right

What if the user didn't type a space there ? What if he typed two spaces ? What if he typed a tab ?


>> ahhh then can I put a strcmp if given the char of the command is a space then we'll increment it??? Will that works?? This means that I'll compare till I get a non-space char????

Have a look at this function :

        http://www.cplusplus.com/reference/clibrary/cstring/strspn/
0
 

Author Comment

by:crazy4s
ID: 35208650
So do you intend to want me to compare the commands and the arguments...hmm but I don't really get it because the strspn only if they have the same char but how do they space in between those arguments???
sorry for my slowness...
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35208691
Just think about it carefully. If you have a string that has whitespace at the beginning, you first need to know how many characters of whitespace there are at the beginning (you can use strspn for that), and then you skip that many characters, by incrementing the pointer.

Does that make sense ?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35208695
If it doesn't, please play around a bit with the sample code I provided in http:#35207322, and use strings that have whitespace at the start. Then see what you can do with strspn, and how you can make it display the string without the initial whitespace.
0
 

Author Comment

by:crazy4s
ID: 35209660
hmm i was trying to play around with this>>
char str[] = " Example string";

char str2[] = "Example string";
int i;
  i = strspn (str, str2);
  printf ("The length of initial number is %d.\n",i);

the output for i was 15, hmm i would like to ask one thing does this 15 includes the null byte at the end of the string, if yes that means is not calculating the space at the beginning?
and in my case which one do i use to compare with the command >>
strspn (command, "not sure which one to compare");
0
 

Author Comment

by:crazy4s
ID: 35209867
is that possible to do like that>>

char space[]=" ";
while(command != NULL)
            {
                  command[strlen(command)-1] = '\0';
                  printf("%s\n",command);
                  temp (i)= command;
                  command = strtok(NULL,"|");
                  nos = strspn (command, space); // compare the command with space
                                                                              // and returns how many spaces before the 1st non-  
                                                                              // space character
                  for (s = 0; s < nos; s++)
                        ++command;
                  i++;
            }

output
ls -l |    more
separate by |:
ls -l
more

Segmentation fault

but it seems not working because i got a segmentation fault again this seems that the increment is a null pointer again... hmmm what can i do so that after the pipe is not a null??
0
 

Author Comment

by:crazy4s
ID: 35210117
this is the latest code that can removes any spaces or tabs before the command but i still can't solve the null pointer that the strtok returned at the end of the command line where no more commands are found?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	int nos;
	int s;
	char *command;
	char *argument;	
        char buf[80];   // command line
        char* temp[80] = {0};	// an array to store the commands
	char space[] = " \t";	// remove space or tab before the command, if any

	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // user command line
	{

		command = strtok(buf,"|");		// tokenize the command line by |
		printf("separate by |: \n");
		while(command != NULL)
		{
			//command[strlen(command)-1] = '\0';
			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any

			i++;				// increment array
		}


		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			pid = fork();
			if (pid == 0)			// child
			{
				exit(0);
			}
			else if (pid < 0) 		// error
			{
				printf("fork failed");
				exit(1);
			}
			else				// parent
			{
				printf("%s process id %d\n", temp[j], pid);	// print the command and the process id 
			}

			argument = strtok((char *)temp[j]," \t\n");	// tokenize the command into arguments

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window



ls -l |         more
separate by |:
ls -l
more

Segmentation fault
0
 

Author Comment

by:crazy4s
ID: 35211018
i'm not sure whether i can do smt like this >> nos = strspn (command[strlen(command)-1], space);       
so this will automatically eliminate the null terminated byte, but i was just making a guess... but is not working because there's a warning msg >> passing argument 1 of 'strspn' makes pointer from integer without a cast....
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35211788
The segmentation fault is because strtok returns NULL when there are no more tokens, but you still try to use that NULL pointer.

Removing the whitespace has to happen for each token, so it has to happen at the location where you print the current token (probably before you print it), not after you get the next token.
0
 

Author Comment

by:crazy4s
ID: 35211965
yes i know the strtok will return a null pointer at the end of the command line where no more commands are found...
so are you saying that the prob actually is in this line >>command = strtok(NULL,"|"); // i shouldn't put a null there??? but if i don't put null what should i put??? hmmm
0
 

Author Comment

by:crazy4s
ID: 35211974
hmm i shifted the remove spaces to before printf and it compile with no segmentation fault is just that the same prob appear is when printing the id for the 2nd command it'll print it next line:

output:
ls -l | more
separate by |:
ls -l
more

separate by space:
ls -l  process id 25496
ls
-l
more
 process id 25497
more
/*
 * Chai Phei Foo
 * CS 2240
 * Assignment 4
 * March 24 2011
 * Purpose: To output each 'command' (separated by pipes) with a 
 *	   separate process along with its process id
 * Usage: $ gcc as4.c
 *        $ ./a.out
 *        ls -l | more (any commands that the user which to enter)
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
	int i = 0;
	int j, k;
	int nos;
	int s;
	char *command;
	char *argument;	
        char buf[80];   // command line
        char* temp[80] = {0};	// an array to store the commands
	char space[] = " \t";	// remove space or tab before the command, if any

	pid_t pid;
	while(strcmp(fgets((char *)buf, 80, stdin), "exit\n")!=0)  // user command line
	{
		//command[strlen(command)-1];
		command = strtok(buf,"|");		// tokenize the command line by |
		printf("separate by |: \n");
		while(command != NULL)
		{
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			i++;				// increment array
		}


		printf("separate by space: \n");
		for(j = 0; j < i; j++)
		{
			pid = fork();
			if (pid == 0)			// child
			{
				exit(0);
			}
			else if (pid < 0) 		// error
			{
				printf("fork failed");
				exit(1);
			}
			else				// parent
			{
				printf("%s process id %d\n", temp[j], pid);	// print the command and the process id 
			}

			argument = strtok((char *)temp[j]," \t\n");	// tokenize the command into arguments

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}

		}

        }
	return 0;
}

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35211995
so i think the problem is still revolving around the null pointer at the end of the command line that causes the last argument to printed like this, is it?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35213048
>> hmm i shifted the remove spaces to before printf and it compile with no segmentation fault

That was what I meant indeed ;)


>> is just that the same prob appear is when printing the id for the 2nd command it'll print it next line:

Because you have only removed the spaces and tabs at the beginning of the line. There could also be whitespace at the end of the line.
And spaces, and tabs aren't the only type of whitespace - there are also newlines etc.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35213050
>> at the beginning of the line.

typo : at the beginning of the command

and :

>> at the end of the line.

at the end of the command.
0
 

Author Comment

by:crazy4s
ID: 35213868
oh yeah i get what you meant, hmmm so i added this into it
		while(command != NULL)
		{
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			if (pos = strcspn (command, nl))// return the position of new line
				command[pos] = '\0';	// replace it with
			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			i++;				// increment array
		}

Open in new window


output

ls -l | more
separate by |:
ls -l
more
separate by space:
ls -l  process id 27244
ls
-l
more process id 27245
more

so what can i do for next?
0
 

Author Comment

by:crazy4s
ID: 35213914
i was trying to figure out to remove any extra spaces behind at the command but if i put a space into my char nl[] it automatically eliminates the -l when printing...what should i do?

	char nl[] = "\t\n"; // i only put tab and nl, if is found

		while(command != NULL)
		{
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			if (pos = strcspn (command, nl))// return the position of new line
				command[pos] = '\0';	// replace it with
			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			i++;				// increment array
		}

Open in new window


0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35213965
>>                   if (pos = strcspn (command, nl))// return the position of new line

That is only looking for newlines. What if there's other whitespace at the end ?

Note that in the more general case of whitespace, you can't use strcspn, because you only want to remove whitespace at the end.

That explains this :

>> but if i put a space into my char nl[] it automatically eliminates the -l when printing...what should i do?


So, instead of using strcspn, just look at the last character of the string and check if it's whitespace. If so, shorten the string, and check again.
0
 

Author Comment

by:crazy4s
ID: 35214067
hmm i tried to put smt like this but it doesn't work?? i know i missed smt out but just can't figure out what is it?
        int len;
	int no = 1;
	char * pos;
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			len = strlen(command);
			pos = command + len - no;	
			if (strcmp (pos, nl) == 0)	// return the position of new line
			{	
				no++;
				pos = command + len - no;
			}
				
			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			i++;

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35214601
strcmp is for comparing strings.

You want to compare a single character.
0
 

Author Comment

by:crazy4s
ID: 35214637
so do you meant i have to use strrchr... but when i check on how to use this, i was a bit confuse and so i didn't use it, can you explain this? thanks.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35214657
You could simply compare the character without using a function :

        if (c == ' ') {  /* it's a space */  }

etc.
0
 

Author Comment

by:crazy4s
ID: 35214828
hmmm like this ....  but i got a warning msg for this line >> if (last == ' ')
t.c:50: warning: comparison between pointer and integer
not sure how can i solve this...
        int no = 1;
	int pos;
	char* last;
	char space[] = " \t";	// remove space or tab before the command, if any
	char nl[] = "\t\n"; //remove any new lines, if any

while(command != NULL)
		{
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			if (pos = strcspn (command, nl))// return the position of new line
				command[pos] = '\0';	// replace it with			
			len = strlen(command);
			last = command + len - no;	
			if (last == ' ')	// return the position of new line
			{	
				last = '\0';
				no++;
				last = command + len - no;
			}

			printf("%s\n",command);
			temp [i] = command;		// store each commands into the array
			command = strtok(NULL,"|");
			i++;				// increment array
		}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35214868
last is a char*, not a char.
0
 

Author Comment

by:crazy4s
ID: 35215048
hmm i added in char las; but there's no difference, how can i get the char that i'm pointing to?

                  nos = strspn (command, space);       // return the number of spaces or tab before the command
                  for (s = 0; s < nos; s++)
                        ++command;            // increment the pointer of command to remove the spaces or tabs, if any
                  if (pos = strcspn (command, nl))// return the position of new line
                        command[pos] = '\0';      // replace it with                  
                  len = strlen(command);
                  last = command + len - no;      
                  if (las == ' ')      // return the position of new line
                  {      
                        las = '\0';
                        no++;
                        last = command + len - no;
                  }
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215314
If you have a pointer, how do you get the object you're pointing to ?
0
 

Author Comment

by:crazy4s
ID: 35215326
hmm using command[ last position]???
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215408
You could use the [] operator with the index 0, yes, but more directly, you can simply dereference the pointer :
char str[] = "something";

char* ptr = str;

char c = *ptr;           /* <--- dereference the pointer to get the object it's pointing to (in this case a char) */

printf("%c\n", c);

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215434
Given that you seem to struggle with very basic things, it's probably a good idea to first get a good basis in C, before you try to tackle more complicated stuff, like your current assignment.

Have a read through these tutorials eg., and make sure you understand every little detail in them. Play around with some test code to get a good feeling for each of the subjects in the tutorial :

        http://www.cprogramming.com/tutorial.html#ctutorial
0
 

Author Comment

by:crazy4s
ID: 35215496
hmm i tried it but no difference they still prints out the space, is there still anything wrong with my code..
        char *command;
	char *argument;	
	char space[] = " \t";	// remove space or tab before the command, if any
	char nl[] = "\t\n"; //remove any new lines, if any
	char* last = command;
	char las = *last;

		while(command != NULL)
		{
			nos = strspn (command, space); 	// return the number of spaces or tab before the command
			for (s = 0; s < nos; s++)
				++command;		// increment the pointer of command to remove the spaces or tabs, if any
			if (pos = strcspn (command, nl))// return the position of new line
				command[pos] = '\0';	// replace it with			
			len = strlen(command);
			last = command + len - no;	
			if (las == ' ')	// return the position of new line
			{	
				las = '\0';
				no++;
				last = command + len - no;
			}

Open in new window

0
 

Author Comment

by:crazy4s
ID: 35215761
oh yeah i solve the problem!!! so what should i do next?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215876
If you feel you don't need to go through the tutorials and learn some more of the basics (I really recommend you do), and if you feel that the result of the current step is acceptable, then you can move on to the next step :)
0
 

Author Comment

by:crazy4s
ID: 35215901
this is the result i got:
 A B C | D E F     | G H I      
separate by |:
A B C
D E F
G H I
Command and its process id:
A B C process id 19257
D E F process id 19258
G H I process id 19259

so what should i do next?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215913
>> so what should i do next?

See my previous post ;)
0
 

Author Comment

by:crazy4s
ID: 35215953
>>make the child execute the command (with its arguments).
hmm do we use exec fun at the child?? can we print the pid at the child too?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35215969
>> hmm do we use exec fun at the child?? can we print the pid at the child too?

Do you remember our earlier discussion about how to use fork and exec ? Please read back through that if you have forgotten certain things.
0
 

Author Comment

by:crazy4s
ID: 35216035
i tried to put an execvp inside the for loop but id doesn't print out the command only the process id at the parent... did i use the execvp correctly???
for(j = 0; j < i; j++)			// fork each command
		{
			pid = fork();
			if (pid == 0)			// child
			{
				execvp(temp[j],temp);
				exit(0);
			}
			else if (pid < 0) 		// error
			{
				printf("fork failed");
				exit(1);
			}
			else				// parent
			{
				printf("process id %d\n", pid);	// print the command and the process id 
			}
			
			argument = strtok((char *)temp[j]," \t\n");	// tokenize the command into arguments

			while (argument != NULL)
			{
				printf("%s\n",argument);
				argument = strtok(NULL," \t\n");
			}


		}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35216074
Think about what you're trying to do here ... The arguments you're passing to execvp : what do they contain ? Is that what you want ?
0
 

Author Comment

by:crazy4s
ID: 35216091
hmm i was thinking why do we need the arguments as we already have the commands store in the temp array right, can't we straight away use the command?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35216141
These are things we already discussed back when we were talking about how to use exec. Each of the exec functions has very specific needs for its parameters.
0
 

Author Comment

by:crazy4s
ID: 35216291
hmm i got no idea what should i put into the execvp func, do i put the argument as the parameter of the execvp func?? execvp(argument,temp); //i'm just guessing around
can you lead me if this is wrong?
and btw the for loop for the strtok for argument is after the fork process, is that fine??
0
 

Author Comment

by:crazy4s
ID: 35216337
but somehow it prints the parent first... and the command is splitted into argument...is not printing together... is there any possibility to print the child first then the parent(for the process id)???

process id 19338
ls
-l
process id 19339
more
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 35216474
>> //i'm just guessing around