Hi Terry, > Bob Dunlop wrote: > > exec rdate -v 192.168.0.2 > > This only occurred to me last night. When I put the command into my > script without preceding it with 'exec' it appears to work. According > to the references I've found for exec, (including its man page), the > command allows you to wrap the arguments of the target command so that > the underlying code receives them correctly.
I don't recognise that description. :-) Which man page? On my system, there's $ man -f exec | sort -V exec (1p) - execute commands and open, close, or copy file... exec (3p) - execute a file exec (3) - execute a file exec (n) - Invoke subprocesses $ But yours could be different. The first line of `man exec' will give the section name in parenthesis. > My rdate command includes one switch and one argument, so how come it > works without the exec prefix? You're so off track that I'll ignore the question. :-) If you've a three-command shell script that does #! /bin/sh who -b id -G date -d yesterday +%Y-%m-%d then when sh is told to run it, it reads in the file. That's a single Linux process doing that work. There is no way for a process to ask the kernel to create a new process to run who(1), say. Instead, there are two distinct system calls that sh uses to get the kernel to run who. (This was a fundamental distinction between Unix and many other OS at the time that gave Unix some of its unique attributes that made it popular.) First, sh uses fork(2) to have the kernel duplicate it. There are now two sh processes running: the original that made the request to the kernel; and the "child" of it that has been created. The first still has its original process ID, the second has a new one that wasn't already in use. The return value from fork() in the parent is the child's process ID so it can distinguish it from other children, e.g. to kill(2) it. But the same call to fork() also returns in the child and that return value is 0; an invalid PID. This allows the source code to do alternate things in the parent and child. pid = fork(); if (pid == -1) err(1, "fork failed"); if (pid) return; /* Only the child continues from here. */ That's the first half of the story. sh can now create many sh, each of which can create yet more. But for running the script, the child will use the execve(2) system call to execute the given file, passing on the specified arguments and environment variables, in this case /usr/bin/who. The kernel keeps the child sh's process ID, and quite a few other bits of it like open files, but replaces the running machine code with that from who, arranging for execution to wind its way towards main(). Meanwhile, parent sh will use the wait(2) system call to go to sleep until the kernel returns to it with news of its child, e.g. that it has _exit(2)'d. sh starts all over again with the next line for id(1) and the fork, execve, wait cycle repeats. In the sh's language, placing "exec" at the start of the command is telling sh to skip the fork stage. It still calls execve(), but the kernel isn't replacing the child shell; it's the original shell that gets trampled with a new executable. If the above script has the second command changed to "exec id -G" then when id(1) finishes and _exit()s, it's the parent of the sh that was running the script that returns from wait() with news that its child, that it started as a sh but ended up as id, has exited. Thus date(1) isn't run. A minor optimisation, still useful on small machines, is to therefore augment the last command a shell script runs, if it's an external one requiring a fork+exec, with exec, saving the fork and its overhead of creating a new process. That's why Bob said to replace rdate ... exit $? with exec rdate ... Cheers, Ralph. -- Next meeting: Bournemouth, Tuesday, 2017-10-03 20:00 Meets, Mailing list, IRC, LinkedIn, ... http://dorset.lug.org.uk/ New thread: mailto:dorset@mailman.lug.org.uk / CHECK IF YOU'RE REPLYING Reporting bugs well: http://goo.gl/4Xue / TO THE LIST OR THE AUTHOR