Thread: Combine execvp result

  1. #1
    Registered User
    Join Date
    Sep 2007
    Posts
    17

    Combine execvp result

    Hi. I have problems with the execvp command. I would like to run multiple commands. For example:
    >ls | wc
    Right now it works fine if I write:
    Code:
    har *argv[2] = {"ls",NULL};
    execvp("ls", argv);
    But how do I add the "wc" command to this?
    Have tried things like:
    Code:
    char *argv[3] = {"wc","ls",NULL};
    execvp("ls", argv);
    and
    Code:
    char *argv[3] = {"wc",[ls result],NULL};
    execvp("wc", argv);
    But cant get it to work.
    Any ideas?

  2. #2
    Registered User
    Join Date
    Jan 2010
    Posts
    18
    Hey,

    once the exec*(2) call is successful, exec*(2) won't return to your "original" program.

    To realize the command "ls | wc", you could fork(2) two processes and connect them by a pipe(2) and finally execute exec*(2) for each of the two new processes.

    - Andi -

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Yeah, in fact you cannot use a pipe with a single exec call. The easiest way around this is to just write a shell script containing your command:

    Code:
    #!/bin/bash
    
    ls | wc
    Give it a name and call it with exec.

    There maybe something you can work out using popen(), along the lines of what ForzaItalia2006 suggests, but just using a shell script is a much better idea.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Actually, here's an idea:
    Code:
    #!/bin/bash
    
    if [ $# != 2 ]; then
    	exit;
    fi
    
    $1 | $2
    Christen this "pipe" (since there is no such standard shell command) and you can use it:
    Code:
    char *args[] = {"pipe", "ls", "wc", NULL};
    execvp("pipe", args);
    You can replace ls and wc with whatever you want.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Quote Originally Posted by ForzaItalia2006 View Post
    Hey,

    once the exec*(2) call is successful, exec*(2) won't return to your "original" program.

    To realize the command "ls | wc", you could fork(2) two processes and connect them by a pipe(2) and finally execute exec*(2) for each of the two new processes.

    - Andi -
    Yes. That is what I have tried to do. Have a working fork together with pipe to send the result from execvp to the parent process. But when I have executed "ls" for the first process I need to use this somehow for the second process.
    Tried to run:
    Code:
    execvp("wc", [result from child],NULL)
    But this gives:
    wc: : No such file or directory
    Any tips what could be wrong?

  6. #6
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Quote Originally Posted by MK27 View Post
    Actually, here's an idea:
    Code:
    #!/bin/bash
    
    if [ $# != 2 ]; then
    	exit;
    fi
    
    $1 | $2
    Christen this "pipe" (since there is no such standard shell command) and you can use it:
    Code:
    char *args[] = {"pipe", "ls", "wc", NULL};
    execvp("pipe", args);
    You can replace ls and wc with whatever you want.
    Seems nice. But I would prefer not to use scripting. Any hint on how it would look in C?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    You also need to look up the pipe(2) system call as well, along with close() and dup().
    A pipe gives you two connected file descriptors, one for reading (will become a stdin), and one for writing (will become a stdout).


    Essentially, you do this
    - create a pipe
    - call fork() to create a separate child process
    --- in the child process (which will become say 'wc'
    ---- close stdin
    ---- dup() one end of the pipe to being the new stdin for the child process
    ---- close other fd's that are of no interest.
    ---- exec the 'wc' command
    - call fork() again in the parent to create another child
    --- in this child process, we will run 'ls'
    ---- close stdout
    ---- dup() the other end of the pipe to being stdout
    ---- close other fd's that are of no interest.
    ---- exec the 'ls' command
    - wait() for both child processes to exit





    Try using strace with a command line version so you can see most (if not all) of this happening.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Quote Originally Posted by Salem View Post
    You also need to look up the pipe(2) system call as well, along with close() and dup().
    A pipe gives you two connected file descriptors, one for reading (will become a stdin), and one for writing (will become a stdout).


    Essentially, you do this
    - create a pipe
    - call fork() to create a separate child process
    --- in the child process (which will become say 'wc'
    ---- close stdin
    ---- dup() one end of the pipe to being the new stdin for the child process
    ---- close other fd's that are of no interest.
    ---- exec the 'wc' command
    - call fork() again in the parent to create another child
    --- in this child process, we will run 'ls'
    ---- close stdout
    ---- dup() the other end of the pipe to being stdout
    ---- close other fd's that are of no interest.
    ---- exec the 'ls' command
    - wait() for both child processes to exit





    Try using strace with a command line version so you can see most (if not all) of this happening.
    I might be wrong but it looks to me that it would be executed as two separate commands by doing that way. Or?
    "---- exec the 'wc' command"
    will run and die before we go on to the
    "---- exec the 'ls' command"
    By typing "ls | wc" you should actually run wc on the result from ls. But it is maybe what happens with what you suggested?

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    also check popen() -- I think it is a little higher level version of what Salem describes.

    vis,
    Code:
    execvp("wc", [result from child],NULL)
    You at least need the first part of **args to match that first param, which is just a path, ie, "wc, "wc", "result from child" altho I'm dubious about this method.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Registered User
    Join Date
    Jan 2010
    Posts
    18
    Quote Originally Posted by Hunter_wow View Post
    I might be wrong but it looks to me that it would be executed as two separate commands by doing that way. Or?
    "---- exec the 'wc' command"
    will run and die before we go on to the
    "---- exec the 'ls' command"
    By typing "ls | wc" you should actually run wc on the result from ls. But it is maybe what happens with what you suggested?
    Yes, you execute two separate commands, just like a shell would do. But, you connect both programs by the pipe. Though, to clarify what salem already explained:

    ls -- write --> | PIPE | -- read --> wc

    Regarding ls, by using close(1)/dup(pipe[1]), stdout is redirected to the writing part of the pipe. In turn, regarding wc, by using close(0), dup(pipe[0]), stdin is redirected to the reading part of the pipe. So, that what ls prints to "stdout"/pipe is read by wc via stdin/pipe.

    Hope that is clear,
    - Andi -

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Here's a C version of that shell script. Just turn it into a function.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main (int argc, char *argv[]) {
    	char byte;
    	FILE *lsin = popen(argv[1],"r"),
    		*wcout = popen(argv[2],"w");
    	
    	while (fread(&byte,1,1,lsin)) fwrite(&byte,1,1,wcout);
    
    	pclose(lsin);
    	pclose(wcout);
    
    	return 0;
    }
    ./a.out ls wc
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Quote Originally Posted by MK27 View Post
    Here's a C version of that shell script. Just turn it into a function.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main (int argc, char *argv[]) {
    	char byte;
    	FILE *lsin = popen(argv[1],"r"),
    		*wcout = popen(argv[2],"w");
    	
    	while (fread(&byte,1,1,lsin)) fwrite(&byte,1,1,wcout);
    
    	pclose(lsin);
    	pclose(wcout);
    
    	return 0;
    }
    ./a.out ls wc
    Seems to work great. Thanks a lot mate.

  13. #13
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Quote Originally Posted by Salem View Post
    You also need to look up the pipe(2) system call as well, along with close() and dup().
    A pipe gives you two connected file descriptors, one for reading (will become a stdin), and one for writing (will become a stdout).


    Essentially, you do this
    - create a pipe
    - call fork() to create a separate child process
    --- in the child process (which will become say 'wc'
    ---- close stdin
    ---- dup() one end of the pipe to being the new stdin for the child process
    ---- close other fd's that are of no interest.
    ---- exec the 'wc' command
    - call fork() again in the parent to create another child
    --- in this child process, we will run 'ls'
    ---- close stdout
    ---- dup() the other end of the pipe to being stdout
    ---- close other fd's that are of no interest.
    ---- exec the 'ls' command
    - wait() for both child processes to exit





    Try using strace with a command line version so you can see most (if not all) of this happening.
    Hello again . I have restarted the problem but having a hard time getting it to work without the popen. It seems like wc gets the wrong result from ls,
    wc: standard input: Bad file descriptor
    Any ideas why?
    Code:
    //create a pipe
    int pipep2[2];
    pipe(pipep2);
    int pread2 = pipep2[0];
    int pwrite2 = pipep2[1];
    
    // call fork() to create a separate child process
    int PID2 = fork();
    if(!PID2){//child
    	close(0); //close stdin
    	// dup() one end of the pipe to being the new stdin for the child process
    	dup(pwrite2);
    	// exec the 'wc' command
    	char *cmd1[10] = {"wc",NULL};
    	execvp("wc", cmd1);
    }
    else if(PID2)//parent
    {
    	int pipep[2];
    	pipe(pipep);
    	int pread = pipep[0];
    	int pwrite = pipep[1];
    	//call fork() again in the parent to create another child
    	int PID = fork();
    	if(!PID){//sub-child
    		close(1); //close stdout
    		//dup() the other end of the pipe to being stdout
    		dup(pwrite);
    		close(pread);
    		// exec the 'ls' command
    		char *cmd2[10] = {"ls",NULL};
    		execvp("ls", cmd2);
    	}
    }
    Thanks in advance!

  14. #14
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    You're creating two pipes, and you only need one.
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #define PIPE_RD 0
    #define PIPE_WR 1
    
    int main(){
      int     pipes[2];
      pid_t   p1, p2;
    
      pipe(pipes);
      p1 = fork();
      if ( p1 == 0 ) {
        // wc reading
        char  *cmd[] = { "wc", NULL };
        close( STDIN_FILENO );
        dup( pipes[PIPE_RD] );
        close( pipes[PIPE_WR] );
        execvp( cmd[0], cmd );
      }
      if ( p1 != 0 ) {
        p2 = fork();
        if ( p2 == 0 ) {
          // ls writing
          char  *cmd[] = { "ls", NULL };
          close( STDOUT_FILENO );
          dup( pipes[PIPE_WR] );
          close( pipes[PIPE_RD] );
          execvp( cmd[0], cmd );
        }
      }
    
      return 0;
    }
    
    
    $ gcc foo.c
    $ ./a.out 
         17      17     141
    $ ls | wc
         17      17     141
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  15. #15
    Registered User
    Join Date
    Sep 2007
    Posts
    17
    Thanks Salem! Just one more thing. If I add a loop to do that twice it seems like the result are not printed out directly. Instead it puts it into the buffer until the loop/main are finished before it prints it out.
    Any idea how I can fflush() or something similar after execvp to get the result directly?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Buidl Library with ./configure script
    By Jardon in forum C Programming
    Replies: 6
    Last Post: 07-24-2009, 09:36 AM
  2. Inserting a swf file in a windows application
    By face_master in forum Windows Programming
    Replies: 12
    Last Post: 05-03-2009, 11:29 AM
  3. Need help with basic calculation program.
    By StateofMind in forum C Programming
    Replies: 18
    Last Post: 03-06-2009, 01:44 AM
  4. Storing the result of execvp to a char array in C
    By kponenation in forum C Programming
    Replies: 1
    Last Post: 12-14-2005, 11:43 PM
  5. Output problems with structures
    By Gkitty in forum C Programming
    Replies: 1
    Last Post: 12-16-2002, 05:27 AM