[comp.unix.questions] How to issue a C SHELL command within a C program

jian@kuhub.cc.ukans.edu (08/15/90)

How can I issue a statment that executes a C SHELL command within a C program?
I would appreciate any helps.

Jian Q. Li
jian@kuhub.cc.ukans.edu

ambarish@gandhi.UUCP (Ambarish Malpani) (08/16/90)

In article <25279.26c7fd35@kuhub.cc.ukans.edu> jian@kuhub.cc.ukans.edu writes:
>How can I issue a statment that executes a C SHELL command within a C program?
>I would appreciate any helps.
>
>Jian Q. Li
>jian@kuhub.cc.ukans.edu


Try using the system() library routine


eg. system("pwd");

Ambarish

sun!valid!ambarish

george@hls0.hls.oz (George Turczynski) (08/17/90)

In article <25279.26c7fd35@kuhub.cc.ukans.edu>, jian@kuhub.cc.ukans.edu writes:
> How can I issue a statment that executes a C SHELL command within a C program?

This works under SunOS 4.0.3 (and probably others):

	system("/bin/csh -c \"<command>\"");

system() calls /bin/sh, which in this case starts a `csh' to perform
<command>.  Note the \" around <command>.  Here is a quick example:

main()
{
	system("/bin/csh -c \"/bin/echo This command run by /bin/csh !\"");
	exit(0);
}

-- 
| George P. J. Turczynski.          |---------------------------------------------------- 
| Computer Systems Engineer.        | ACSnet: george@highland.oz | I can't speak for the |
| Highland Logic Pty. Ltd.          | Phone: +61 48 683490       | company, I can barely |
| Suite 1, 348-354 Argyle St        | Fax:   +61 48 683474       | speak for myself...   |
| Moss Vale. NSW. Australia. 2577   |---------------------------------------------------- 

guy@auspex.auspex.com (Guy Harris) (08/21/90)

>This works under SunOS 4.0.3 (and probably others):

It should work on any UNIX system where "/bin/csh" refers to the C
shell.  (If it doesn't, either that system's C shell is broken, its
"system()" implementation is broken, or something either or both of them
use is broken.)

>	system("/bin/csh -c \"<command>\"");
>
>system() calls /bin/sh, which in this case starts a `csh' to perform
><command>.  Note the \" around <command>.

Which means if <command> *itself* contains double-quotes, you have some
more quoting to do....

roger@gtisqr.uucp (Roger Droz) (08/29/90)

There have been some excellent responses to this question.  Our system
has expired the article that first mentioned using execlp(), which I
thought was not only the best solution, but a fine example of techinical
writing as well.

I too, like to use execlp() because of the efficiency of directly
invoking the shell of choice without invoking the Bourne shell first,
and because it gets around the quoting problem mentioned by
Guy Harris in <3923@auspex.auspex.com>.

The two cents I want to add to this thread is that the parent process should
disable the INT and QUIT signals, so that the user can type ^C (or your
favorite INT signal key) to kill the child without killing the parent.

The following code is taken from my patches to MicroEMACS 3.10e, which I
plan to release on the world in the next week or so:

/** Callout to system to perform command **/
/** RLD 07-24-90: modified to use $SHELL */
/** cmd == "" means interactive shell */
int callout(cmd)
char * cmd;			/* Command to execute	*/
{
	int status;
	char *sh;		/* Shell program. */
	void (*oldsigint)(), (*oldsigquit)();
	extern char *getenv();

	/* Get shell path */
	sh = getenv("SHELL");
	if (!sh)
#if BSD || SUN
		sh = "/bin/csh";
#endif /* BSD || SUN */
#if USG || SMOS || HPUX || XENIX
		sh = "/bin/sh";
#endif /* USG || SMOS || HPUX || XENIX */

	if (fork() == 0) {
		/* Child decides what to do. */
		if (*cmd != '\0')
			/* Do a command. */
			execlp(sh, sh, "-c", cmd, NULL);
		else
			/* Fork an interactive shell */
			execlp(sh, sh, NULL);
		exit(1); /* Child shouldn't get here. */	
	}

#if USG || SMOS || HPUX || XENIX
	/* Parent disables signals and waits. */
	oldsigint = signal(SIGINT, SIG_IGN);
	oldsigquit = signal(SIGQUIT, SIG_IGN);
	wait(&status);
	signal(SIGINT, oldsigint);
	signal(SIGQUIT, oldsigquit);
#else /* BSD || SUN */
	/* BSD should disable signals while waiting for child too,  */
	/* but I can't remember how. -- RLD 08-28-90		    */
	wait(&status);
#endif /* USG || SMOS || HPUX || XENIX */

}
____________
               Roger Droz       UUCP: uw-beaver!gtisqr!roger
()       ()    Maverick MICRoSystems / Global Technology International
 (_______)     Mukilteo, WA 
  (     )      
   |   |       Disclaimer: "We're all mavericks here: 
   |   |                    Each of us has our own opinions,
   (___)                    and the company has yet different ones!"

guy@auspex.auspex.com (Guy Harris) (08/30/90)

>	if (!sh)
>#if BSD || SUN
>		sh = "/bin/csh";
>#endif /* BSD || SUN */
>#if USG || SMOS || HPUX || XENIX
>		sh = "/bin/sh";
>#endif /* USG || SMOS || HPUX || XENIX */

This isn't really necessary.  Some of the systems in the latter category
*do* have the C shell, and some of the users in systems in the former
category don't use it if they have an alternative they like better.  In
addition, most UNIX systems these days arrange for the SHELL environment
variable to be set.  Just doing

	if (!sh)
		sh = "/bin/sh";

shouldn't annoy any C shell users, and removes some ugly #ifdefs.  (BTW,
even on BSD and SunOS systems, if the user's "/etc/passwd" entry doesn't
specify a shell, they get "/bin/sh", not "/bin/csh".)

>#if USG || SMOS || HPUX || XENIX
>	/* Parent disables signals and waits. */
>	oldsigint = signal(SIGINT, SIG_IGN);
>	oldsigquit = signal(SIGQUIT, SIG_IGN);
>	wait(&status);
>	signal(SIGINT, oldsigint);
>	signal(SIGQUIT, oldsigquit);
>#else /* BSD || SUN */
>	/* BSD should disable signals while waiting for child too,  */
>	/* but I can't remember how. -- RLD 08-28-90		    */
>	wait(&status);
>#endif /* USG || SMOS || HPUX || XENIX */

BSD does it exactly the same way the other systems do.

On BSD and SunOS systems - and on some systems that fall into the first
category - you can block, rather than ignore, the signal, so that if the
user types ^C it'll be "held" until they unblock it.

If you don't want to do that, though, the same calls to "signal()" you
use for the other systems will do the job on BSD and SunOS. 

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/31/90)

In article <3985@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>	if (!sh)
>		sh = "/bin/sh";

Incidentally, while /bin/sh should always be used to execute commands
from a program, when you spawn an interactive subshell you should use
the valuse of the SHELL environment variable.  Similar for EDITOR and
PAGER.  Users are much more fussy about their interactive environments
and are therefore much more likely to notice if you don't give them
their preferences there.

guy@auspex.auspex.com (Guy Harris) (09/02/90)

>Incidentally, while /bin/sh should always be used to execute commands
>from a program, when you spawn an interactive subshell you should use
>the valuse of the SHELL environment variable.

This should also be done if running a *user-supplied* command.

Some examples of the rules:

1) if your program is, say, running an "rm -rf" on some directory to
   remove it, it should use "system()" and do it from "/bin/sh".  The
   program knows what behavior it wants from that command, and doesn't
   want the user's preferences getting in its way.

2) if the user typed "!" or whatever the "escape to shell" command is,
   it should use whatever shell SHELL specifies.

3) if the user typed "!egrep mmedea /etc/passwd" or something like that,
   i.e. a shell escape to run one command, it should use whatever shell
   SHELL specifies.

4) if the user is telling the program to run some command and grab input
   from it, or send output to it (for instance, running a region of a
   file through a filter in an editor), it should use whatever shell
   SHELL specifies.

etc..