[comp.unix.questions] shell in programs

ping@cubmol.bio.columbia.edu (Shiping Zhang) (01/12/91)

In article <!_P+_!_@irie.ais.org> garath@ais.org (Scott Grosch) writes:
>     If I make a C program on this account, is there any way to have the 
>program execute a shell command if a certain part of the program is reached?

How about system(3)?

-ping

mike@bria.UUCP (Michael Stefanik) (01/12/91)

In article <!_P+_!_@irie.ais.org> ais.org!garath (Scott Grosch) writes:
>
>     If I make a C program on this account, is there any way to have the 
>program execute a shell command if a certain part of the program is reached?
>

A quick and dirty way is to use the system() subroutine, ie:

	system("cp myfile yourfile");

Another way is to use the fork() exec() yourself, such as:

	if ( fork() )
		wait(NULL);
	else
		execl("/bin/sh","sh","-c","cp myfile yourfile");
-- 
Michael Stefanik, Systems Engineer (JOAT), Briareus Corporation
UUCP: ...!uunet!bria!mike
--
technoignorami (tek'no-ig'no-ram`i) a group of individuals that are constantly
found to be saying things like "Well, it works on my DOS machine ..."

guy@auspex.auspex.com (Guy Harris) (01/15/91)

>Another way is to use the fork() exec() yourself, such as:
>
>	if ( fork() )
>		wait(NULL);
>	else
>		execl("/bin/sh","sh","-c","cp myfile yourfile");

Which doesn't buy you very much over "system()" - in fact, it may buy
you less; "system()":

	1) ignores SIGINT and SIGQUIT, which may be a win if the user
	   ^C's the program;

	2) exits if the "execl" fails;

	3) properly provides the terminating "(char *)NULL" argument to
	   "execl"'s argument list;

	4) keeps waiting until the newly-created process exits, so that
	   if you have other child processes (sometimes some shells give
	   you child processes that you *don't* create yourself, so even
	   if your program hasn't forked before, it may have child
	   processes if it's part of a pipeline);

	5) picks up the exit status from the command.

2) and 3) may just be due to the code being a quick-and-dirty example,
but *real* code should do both.  5) isn't a problem if the program
doesn't care whether the command succeeds - but make sure you *really*
have no reason to care, i.e. the program can get its work done
regardless of whether the command succeeds.  1) may not be a problem. 
4), however, is something people *have* been bitten by.

Another advantage of using "system()" is that you don't have to write
code to worry about all of the above.

The one advantage to *not* using it is that you may want to run the
command under an effective user ID other than the one your program is
running under (for example, if your program is set-UID, has an
interactive interface, and lets the user run an arbitrary command), or
may want to change its environment variables, or something like that. 
(If you just want to capture its output into a pipe, or send output down
a pipe to be its input, take a look at "popen()", bearing in mind that
it doesn't do anything with the user IDs either.)

guy@auspex.auspex.com (Guy Harris) (01/17/91)

>Another advantage to *not* using system() is
>  "There is an enormous amount of overhead associated with each system
>   call. The starting program has to fork and exec the shell, and then
>   the shell has to fork and exec the requested program..." [1]

Yes, but if the person wants to run an arbitrary shell command - and
that's what it sounded like the original poster wanted to do - you have
to run the shell anyway (or precisely duplicate its actions, which seems
like a waste).

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (01/17/91)

In article <5302@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
: >Another advantage to *not* using system() is
: >  "There is an enormous amount of overhead associated with each system
: >   call. The starting program has to fork and exec the shell, and then
: >   the shell has to fork and exec the requested program..." [1]
: 
: Yes, but if the person wants to run an arbitrary shell command - and
: that's what it sounded like the original poster wanted to do - you have
: to run the shell anyway (or precisely duplicate its actions, which seems
: like a waste).

One typical tradeoff is to use the shell if the command contains any shell
metacharacters, and execute the program directly otherwise.  Perl and many
make programs do this.

Larry Wall
lwall@jpl-devvax.jpl.nasa.gov

guy@auspex.auspex.com (Guy Harris) (01/19/91)

>No big problem if it doesn't.  Just catch the ENOEXEC and do the shell call
>you would have done anyway.

Or, if you also want to run a command and have the system search $PATH
to see which one to run, if appropriate, rather than having to give a
full pathname, use "execvp()" which does the path search *and* the
catching of ENOEXEC for you....