35

In linux, I would like to write a C program that launches another program. When the program runs, the shell will be waiting for you to input a command that you have defined in you program. This command will launch the second program.

For example, assume there is a simple C program called "hello" in the same directory as the invoking program. The "hello" program prints the output "hello, world". The first program would be run and the user would input the command "hello." The "hello" program would be executed and "hello, world." would be output to the shell.

I have done some search, and people suggested the "fork()" and "exec()" functions. Others said to use "system()". I have no knowledge about these functions. How do I call these functions? Are they appropriate to use?

Example code with explanations would be most helpful. Other answers are also welcome. Your help is greatly appreciated.

keparo
  • 33,450
  • 13
  • 60
  • 66
wa-ha
  • 359
  • 1
  • 3
  • 4

6 Answers6

41
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */

int main()
{
    /*Spawn a child to run the program.*/
    pid_t pid=fork();
    if (pid==0) { /* child process */
        static char *argv[]={"echo","Foo is my name.",NULL};
        execv("/bin/echo",argv);
        exit(127); /* only if execv fails */
    }
    else { /* pid!=0; parent process */
        waitpid(pid,0,0); /* wait for child to exit */
    }
    return 0;
}
Svisstack
  • 16,203
  • 6
  • 66
  • 100
  • Why is `echo` in the `argv` variable and in the `execv` call? – User Oct 05 '16 at 19:43
  • 4
    @User, because it's argv[0], i.e. *program name*. – 0andriy Jan 09 '17 at 23:57
  • The redundancy might be reduced by deriving the first argument to `execv` from the first element in the `argv` array, or vice versa? – Qiang Xu Jul 28 '22 at 19:20
  • I am so confused by this. Does `execv` have some sort of conditional built into it that conditionally launched the next line of code? or does execv terminate execution at itself for some reason if it succeeds? Are we launching a second instance of yourself? What is the point of that? We basically just create 2 threads, one waits for the second to finish executing, and the second does what we want the program to do? – Jonathon Oct 22 '22 at 15:23
  • I would add a check to the code above to see if `pid` is -1. If it is -1 , then the fork creation failed and the program should exit. See https://en.wikipedia.org/wiki/Fork_(system_call) – RobK May 09 '23 at 15:53
  • You do not need to create a second process using `fork()`! Just run `exec()`. When `execv()` is called, the process from which it was called is terminated and replaced by the new process in the allocated portion of memory and with the same process ID. If you want to execute additional statements from the parent program after the second (or child) program has finished, use `system()` instead. – RobK May 09 '23 at 18:25
14

If you are new to fork, graphical representation about fork and exec might be helpful to you.

Depiction of fork()

  +-------------+  
  |main program |  
  +-------------+    (fork())
        |___________________________  
  +-------------+                   |
  |main program |           +-------------+ 
  +-------------+           |main program |
        |                   +-------------+  
  +-------------+                   |        (exec())
  |main program |           +-------------+
  +-------------+           |hello program|
                            +-------------+  

As you might have already read in a tutorial, after calling fork() a duplicate copy of the existing program is created, and the exec() after that replaces that copy with the new program, which you pass to it as arguments. Two execution units for two programs will be running after fork().

Zorawar
  • 6,505
  • 2
  • 23
  • 41
Terminal
  • 1,969
  • 5
  • 21
  • 37
7

Won't system() be enough for you?

/* ... */
if (!strcmp(cmd, "hello")) system("hello.exe");
/* ... */
pmg
  • 106,608
  • 13
  • 126
  • 198
  • 3
    it's linux, so it is likely that no exe is found. Though, it can be any name. – Vladimir Ivanov Mar 28 '11 at 14:30
  • @Vladimir: the OP states the program is "hello.c". It most likely is not -- I just wanted to point him in the right direction when he tries my snippet :) – pmg Mar 28 '11 at 14:33
  • 2
    @vissi: `` is probably the C++ version; the C version is declared in `` – pmg Mar 28 '11 at 14:34
  • thanks for your quick response, could you explain a little bit more about the "system()" function, or could you show me another example of calling this function ? – wa-ha Mar 28 '11 at 15:14
  • 1
    @charlie: you can click the system in my post to go to a description of the function. Basically what you put inside the parenthesis is what you'd write in the shell. Your program executes that and waits until it ends. – pmg Mar 28 '11 at 15:23
0

I have done some search, and people suggested the fork() and exec() functions. Others said to use system(). I have no knowledge about these functions. How do I call these functions? Are they appropriate to use?

Yes. Read first the documentation (man page), e.g. of fork(2), exec(3), system(3). Quite probably you have that documentation locally on your computer, using man(1). Notice that system uses sh (thru bash(1) or dash(1)), because it is fork-ing, execve(2)-ing and waitpid(2)-ing the /bin/sh POSIX shell.

I think that fork is difficult to understand because on success it returns "twice". I won't try to explain it here (I'll need many pages for that). I recommend reading fork (system call) wikipage at first. Then, read some good Linux programming book, e.g Advanced Linux Programming (freely downloadable).

Read also about Virtual Address Space, and proc(5).

You could also read Operating Systems: Three Easy Pieces for a more general view.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

For the most simple case you should two compiled programs in one directory:

> ls
.
hello
second

In second program you just need to call system("hello");

Vladimir Ivanov
  • 42,730
  • 18
  • 77
  • 103
  • 3
    Things don't work that way in *nix environments. system(...) invokes a shell (/bin/sh actually), which will look in the search PATH for executables ($PATH environment variable). Since one should not place the working directory (./) in PATH for security reasons, placing the callee programm in the same directory as the caller will not work. This is different from Windows, where the working directory is included in the search. – datenwolf Mar 28 '11 at 16:59
0

The answer to your question depends on whether you want to execute additional statements in your parent program after executing the second (or child) program.

If you want to execute additional statements in your parent program after executing your second (or child) program, I would use system().

If you do NOT need to execute additional statements from the parent program after executing your second (or child) program, I would just use execv(). A second process will NOT be created. The parent program will terminate and the second program will run with the same process ID as the parent program.

P.S. In this scenario, you could also use system(). But if you use system(), two processes with different process IDs will be created - one process ID for the main program and a second process ID for the second program.

In your example, you can use either the system() function or the execv() function.

Here is a simple self-explanatory example using system():

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello from the parent process\n");

    if ( system("echo 'Hello from the child process' > test.txt") != 0 )
    {
        printf("ERROR executing system() command\n");
    }

    if ( system("cat test.txt") != 0 )
    {
        printf("ERROR executing system() command\n");
    }

    printf("Executing from the parent process again!\n");
}

Here is the output:

Grinchs-MBP:Downloads rob$ ./parent3
Hello from the parent process
Hello from the child process
Executing from the parent process again!

Remember to include the stdlib.h header file, if you intend to use system().

If you do NOT need to execute additional statements in your parent program after the child or second program has finished, I would just use execv() in the parent program.

In this case, you do not need to create a fork using fork(). You just need to use execv(). If you want to use execv(), remember to include the unistd.h header file.

When execv() is called, the process from which it was called is terminated and replaced by the new process in the allocated portion of memory and with the same ID.

If the execv() executes successfully, it does not return. If it returns, it is because an error occurs. In other words, execv() should be the last statement you intend to execute from your parent program since execv() will only return to your parent program in case of an error.

Again -- if you want to execute another program from your parent program and want to execute additional statements in the parent program after the second (or child) program has finished, then I would use system() instead.

Here is a simple example using execv():

Parent Process Code

#include  <stdio.h>
#include  <unistd.h>
#include  <errno.h>

int main ()
{
printf ("I am the parent process\n\n");

char *arg_Ptr[4];
arg_Ptr[0] = "child.c";
arg_Ptr[1] = "Hello from the child process";
arg_Ptr[2] = "Good-Bye from the child process!";
arg_Ptr[3] = NULL;

execv("/Users/rob/Downloads/child.bin", arg_Ptr);
printf ( "Error:  %i\n", errno);
}

Child Process Code (Saved at /Users/rob/Downloads/child.bin).

#include  <stdio.h>

int main(int argc, char *argv[])
{
 printf ("I am the child process\n");

 printf ("Argument 1: %s\n", argv[1]);
 printf ("Argument 2: %s\n", argv[2]);
 printf ("Argument 3: %s\n", argv[3]); 
}

Here is the Output

Grinchs-MBP:Downloads rob$ ./parent 
I am the parent process

I am the child process
Argument 1: Hello from the child process
Argument 2: Good-Bye from the child process!
Argument 3: (null)
RobK
  • 156
  • 1
  • 9