views:

128

answers:

4

How can I execute a shell script from C in Linux?

+6  A: 

You can use system:

system("/usr/local/bin/foo.sh");

This will block while executing it using sh -c, then return the status code.

Matthew Flaschen
+1 Don't forget to include `<stdlib.h>` ( http://linux.die.net/man/3/system )
pmg
... assuming it has executable bit and shebang line. In general, `system("/bin/sh /usr/local/bin/foo.sh");`. Yes, this actually calls the shell twice.
Roman Cheplyaka
+1  A: 

If you need more fine-grade control, you can also go the fork pipe exec route. This will allow your application to retrieve the data outputted from the shell script.

doron
+2  A: 

If you're ok with POSIX, you can also use popen()/pclose() ( http://linux.die.net/man/3/popen )

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

int main(void) {
/* ls -al | grep '^d' */
  FILE *pp;
  pp = popen("ls -al", "r");
  if (pp != NULL) {
    while (1) {
      char *line;
      char buf[1000];
      line = fgets(buf, sizeof buf, pp);
      if (line == NULL) break;
      if (line[0] == 'd') printf("%s", line); /* line includes '\n' */
    }
    pclose(pp);
  }
  return 0;
}
pmg
+6  A: 

It depends on what you want to do with the script (or any other program you want to run).

If you just want to run the script system is the easiest thing to do, but it does some other stuff too, including running a shell and having it run the command (/bin/sh under most *nix).

If you want to either feed the shell script via its standard input or consume its standard output you can use popen (and pclose) to set up a pipe. This also uses the shell (/bin/sh under most *nix) to run the command.

Both of these are library functions that do a lot under the hood, but if they don't meet your needs (or you just want to experiment and learn) you can also use system calls directly. This also allows you do avoid having the shell (/bin/sh) run your command for you.

The system calls of interest are fork, execve, and waitpid. You may want to use one of the library wrappers around execve (type man 3 exec for a list of them). You may also want to use one of the other wait functions (man 2 wait has them all). Additionally you may be interested in the system calls clone and vfork which are related to fork.

fork duplicates the current program, where the only main difference is that the new process gets 0 returned from the call to fork. The parent process gets the new process's process id (or an error) returned.

execve replaces the current program with a new program (keeping the same process id).

waitpid is used by a parent process to wait on a particular child process to finish.

Having the fork and execve steps separate allows programs to do some setup for the new process before it is created (without messing up itself). These include changing standard input, output, and stderr to be different files than the parent process used, changing the user or group of the process, closing files that the child won't need, changing the session, or changing the environmental variables.

You may also be interested in the pipe and dup2 system calls. pipe creates a pipe (with both an input and an output file descriptor). dup2 duplicates a file descriptor as a specific file descriptor (dup is similar but duplicates a file descriptor to the lowest available file descriptor).

nategoose
It's also worth noting that fork() is quite cheap since the memory is copy-on-write and not duplicated as soon as the program forks.
CodeninjaTim
I'm a big fan of fork/exec - it lets you avoid environment-based uncertaintly about what's going to run. But can you use it on shell scripts directly? As you say, by using exec, you "avoid having the shell (/bin/sh) run your command for you"; don't you thus need to exec /bin/sh to run a shell script?
Tom Anderson
@Tom Anderson: If the shell script has execution permissions set for the effective user and has an appropriate shabang first line listing a file which the effective user also has permission to execute and also is not itself a script of some sort then the kernel will call the file listed on the shabang line with the script file. If the script's shabang were `#!/bin/csh` then `/bin/sh` would not be run in the fork/exec scenario. If it were `#!/bin/sh` then it would in the fork/exec scenario, but with the `system` version `/bin/sh` would be run twice for this script.
nategoose