help-bash
[Top][All Lists]
Advanced

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

Re: [Help-bash] Different methods of running scripts; subshells and exec


From: Michael Convey
Subject: Re: [Help-bash] Different methods of running scripts; subshells and execute privileges
Date: Thu, 20 Aug 2015 18:05:39 -0700

On Thu, Aug 20, 2015 at 5:31 AM, Greg Wooledge <address@hidden> wrote:

> > Isn't this essentially the same as creating a subshell?
>
> No.  They are very different.
>
> exec-ing a program causes all of your current non-environment variables
> to go away.  All of your functions, all of your code, go away.  The only
> things that are retained are the process ID, open file descriptors,
> environment variables, resource limits, privileges, and signal handlers
> (traps).
>
> If you execute a script in the REGULAR way (without exec), what you get
> is a child process.  This is not a "subshell" -- see below.  A child
> process is the result of a fork() followed by an exec() in the child.
> The child process has a brand new PID, and inherits the parent's
> environment variables and open file descriptors, but runs independently.
> The parent may continue running at the same time (if the child is
> "asynchronous" or "background", usually in bash as a result of putting
> the & character at the end of the command), or in the more common case,
> the parent may sit around waiting for the child to finish before the
> parent can resume (in this case, the child process is called "synchronous"
> or "foreground").
>
> A subshell is also a child process but it has a very specific meaning
> in bash programming.  A subshell is the result of a fork() NOT followed
> by an exec().  A subshell inherits copies of everything the parent
> has -- environment variables, NON-environment variables, functions,
> loop states, everything.  It's a perfect clone.
>
> Subshells may be created explicitly by putting ( ) around a command,
> or more commonly, they are created implicitly by pipelines, or by a
> command substitution.
>
> Usually subshells are a pain in the ass that you have to know about and
> work around.  For example, consider this code:
>
> echo "$myvariable" | read a b c
>
> You just wanted to split the variable into three words, right?  Well,
> this doesn't work because the read takes place in a subshell (because
> of the pipeline).  Variables a, b and c do get the correct values,
> but those values are discarded when the pipeline ends.  You have to
> understand this and work around it.
>
> read a b c <<< "$myvariable"
>
> That one works, because read runs in the current process instead of in
> a subshell.
>
> I still don't understand what you are trying to DO.  We can keep answering
> theoretical questions all day, but this won't help you until we know what
> you're trying to accomplish, so we can skip all the theory and just tell
> you "Oh, you do it like this."
>
> Here, let me write up a little mini-FAQ.
>
> === What is the shebang for? ===
>
> The shebang (#!/usr/bin/env bash or #!/bin/sh or whatever) at the top of
> a file tells the KERNEL that when this file is executed as a program,
> the kernel should launch the specified interpreter and pass the file as
> an argument.
>
> Thus, if you have a file named /usr/local/bin/ascript which has proper
> permissions and begins with #!/bin/sh and you type "ascript" at the
> command line, the kernel will execute "/bin/sh /usr/local/bin/ascript".
>
> When /bin/sh starts reading /usr/local/bin/ascript, it will see the
> shebang as a comment (a line starting with #) and ignore it.
>
> === What's the difference between "foo" and "exec foo" and "source foo"?
> ===
>
> When you run "foo", bash looks for an alias, a function, or a builtin
> by that name.  If it doesn't find one, it searches the directories in PATH
> for a file named foo.  If it finds one, it will try to execute it as
> a program (using a fork() followed by an exec()).  The kernel runs foo
> however it sees fit based on foo's magic number (the first few bytes).
> If foo happens to be a compiled program in ELF format, the kernel launches
> the ELF loader.  If foo happens to be a script beginning with a shebang,
> the kernel launches the shebang interpreter.
>
> When you run "exec foo", you are telling bash to skip the fork() and just
> do the exec().  The current bash shell and script vanish, to be replaced
> by the new program foo.  foo may be another script, or a compiled program;
> it doesn't matter.  The results are the same.
>
> When you run "source foo" you are telling bash to read the lines of the
> file foo (which it finds by performing a PATH search) as if they had
> been part of the script that it's currently reading.  There is no fork()
> and there is no exec().  Bash just starts reading commands from a new
> file, continues until the end of that file, then resumes reading the
> original script.
>
> (Actually, bash treats "source" as a sort of function call.  You can
> do a "return" command to stop reading a sourced script and resume reading
> the original script.)
>
> === What's a subshell? ===
>
> A subshell is a fork() without an exec().  The new child process is an
> exact copy of the parent, with copies of all of the parent's variables.
> Any changes made in the subshell are lost when the child process ends.
>
> Subshells are created by putting ( ) around a command, or implicitly by
> pipelines and command substitutions.
>
> myfunc() { foo=bar; echo ok; }
> foo=foo
> x=$(myfunc)
> #  At this point, foo still equals foo.  The change to foo during myfunc
> #  happened in a subshell (implicitly created by the command substitution),
> #  so it is lost.
>

​Wow, fantastic. Your comments have cleared up much. Thank you for your
thorough explanation. Some of what you say contradicts my interpretation of
some of the concepts I've read about in books (I suspect that either the
book or my interpretation of the book is wrong). If you ever write a book,
I'll definitely purchase it and read it. Thanks again. ​


reply via email to

[Prev in Thread] Current Thread [Next in Thread]