From Newsgroup: comp.unix.shell
Lew Pitcher <
lew.pitcher@digitalfreehold.ca>:
Long ago, I played with Minix (v1.5), and wrote a SysV-style
init(8) for it. At that time, I couldn't see a proper way to
parse the "process" field of /etc/inittab, and simply crafted a
naive parser from strtok() calls and special handling for
redirect characters. I knew that this wasn't the way to do it; I
really needed to use the commandline parsing logic of sh(1).
Fast forward to now, and I'm looking at recycling my old Minix
init(8) for use in a Linux "homebrew" container environment, and
I want to fix this commandline parsing clumsiness. My current
thinking is that there may be some way to invoke sh(1) to
perform the parsing and execution for me. My only oddball
requirement is that sh(1) "get out of the way" (so to speak),
and directly /exec()/ the resulting parsed commandline, rather
than fork() and exec().
I noticed the "-c" sh(1) commandline option, and have puzzled
about it. From tests, it /appears/ to do what I want: parse the
given string into argv[] arguments, then exec() that list, but
the documentation doesn't really say that this behaviour is to
be expected.
By the way:-a This is not a property of the "-c" sh option.-a This
behavior might occur whenever the shell is to execute a last
command before exiting, for example with a shell script given in
the shell's invocation parameter list or commands fed into the
shell's stdin.
From the OpenGroup's description of sh(1)[1], it /looks/ like
sh(1) is supposed to fork() and exec(), and the Linux sh(1)
manpage[2] is even less helpful about the behaviour of the -c
option. However, limited tests on my Linux boxen show that
bash(1) (at least version 4.3.48) appears to exec() only.
If Bash knows that after having (forked a new process and) execed
the binary with its argv[] arguments there is nothing left to do
other than receiving the exit status of that forked process and
then exiting using that exit status then it might well dispense
with forking a new process and just exec the binary with its
argv[] list.
Try and compare the following shell command lines:
(1)
bash -c '
ps -o ppid -o pgid -o pid -o stat -o args
es="$?"
printf '\''ps exited returning %s\n'\'' "$es"
exit "$es"
' sh
In this first example obviously the shell has to stay alive after
having execed the "ps" binary in order to be able to execute the
variable assignment and the "printf" command.
(2)
bash -c '
ps -o ppid -o pgid -o pid -o stat -o args
' sh
In this second example "bash" doesn't have to do more than fork
and exec "ps", then wait for "ps" to exit, receive the exit
status and exit returning that exit status.-a So it might very
well dispense with forking a new process but just replace itself
with the "ps" binary.
(3)
bash -c '
exec ps -o ppid -o pgid -o pid -o stat -o args
' sh
Finally in this third example (see below) the "exec" shell
builtin command tells "bash" to dispense with forking a new
process but just replace itself with the "ps" binary.
Repeat these command lines, but now use "sh" rather than "bash".-a
I expect the same behavior as before in (1) and (3) respectively,
but (2) may be different than with "bash".
So, can anyone advise me; does the sh(1) "-c" option cause shell to
a) parse the given string into a commandline argument list, and then
b) exec(2) that argument list, replacing sh(1) with the specified
binary within the process?
Not the "-c" option but the "exec" shell builtin command will do
what you want to be done:-a The "exec" shell builtin command tells
the shell not to fork a new process but rather exec the binary
replacing itself.
[1] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
By the way:-a There is new version of the POSIX standard:
<
http://pubs.opengroup.org/onlinepubs/9799919799/mindex.html>.
--- Synchronet 3.21a-Linux NewsLink 1.2