lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Rena once stated:
> On Mon, Apr 23, 2012 at 10:52, Jay Carlson <nop@nop.com> wrote:
> > On Mon, Apr 23, 2012 at 9:25 AM, Robert Klemme
> >
> > IMO on Unix if you want to play the part of a shell, the problem comes
> > down to embedding a reasonable domain-specific quasiquotation syntax
> > for constructing process invocation structures. Which would you rather
> > write?
> >
> >  proc{'find', "/usr", "-name", pattern, "-print0"}.pipe{'xargs', '-0', 'du'}()
> >
> >  find /usr -name "$pattern" -print0 | xargs -0 du
> >
> >  find /usr -name $pattern -print0 | xargs -0 du
> 
> My thought would be something a little more verbose, like:
> proc1 = create_process('find', {path='/usr', name=pattern, print0=true})
> proc2 = create_process('xargs', {nullsep=true, delim='u'})
> proc1.stdin = proc2.stdout
> proc2:start(); proc1:start()

  Another approach is to create a "Unix program userdata type" [4].  Perhaps
something like:

	run(
	     cmd("find",{ "/usr" , "-name" , pattern , "-print0" })
	     .. cmd("xargs",{ "-0" }) ..
	     .. cmd("du") > "output.txt"
	)

  But I wouldn't want to type that at an interactive prompt.

> (create_process would create but not start; you'd have another
> function to create and start.) 

  If you are using Unix [1] as the kernel, it is impossible to create a
process and not start it.  Or rather, I don't think you know how process
creation under Unix works.  To create a new process, you call fork():

	pid_t child;

	child = fork();
	if (child == -1)
	  /* there was an error creating the process */
	else if (child == 0)
	  /* we are the newly created child process */
	else
	  /* we just created a new process */

Yes, the new process is a duplicate of the process that created it [2].  To
*run* a different program, you have to call exec() [3], which overlays the
calling program with the new program.  The call exec() never returns unless
there's an error.  A typical pattern to run a program in another process is
to:

	child = fork();
	if (child == -1)
	  handle_error();
	else if (child == 0)
	{
	  execve("/path/to/program",argv,envp);
	  handle_error();
	  _exit(1);
	}
	else
	{
	  /* either wait for the child process to end, or go on */
	  /* and do something else ... */
	}

It might seem silly to require two system calls to do one job, but there are
some good reasons for it [2a].

  -spc

[1]	This include Linux, the various BSDs and Mac-OS X.

[2]	It's always been this way under Unix.  From some personal
	correspondence with Dennis Ritchie about the fork() system call:

		At the time,  the trap instruction corresponding to `sys
		fork' (if I remember correctly) probably skipped an
		instruction in either the parent or the child to indicate
		the difference, and the slot perforce was usually filled
		with a branch to separate the parent and child.

		...

[2a]		The design of the fork primitive was somewhat adventitious,
		but it turned out that splitting the creation of the new
		process into two threads executing the same program (though
		with copied data) before the typical execution of a new
		program in the child is quite convenient.  For example, it
		gives the shell (say) a chance to redirect its I/O in the
		child process before handing control to the newly executed
		program.

[3]	It's actually one of six functions, execve() being the actual system
	call.

[4]	I did something similar to this in a language I wrote in college.  I
	made Unix commands a first-class data type.