Differences between revisions 1 and 3 (spanning 2 versions)
Revision 1 as of 2007-05-02 23:53:47
Size: 1540
Editor: redondos
Comment:
Revision 3 as of 2008-05-09 11:28:15
Size: 2536
Editor: Lhunath
Comment: explain the forking a bit better. many newbies read the faq.
Deletions are marked like this. Additions are marked like this.
Line 11: Line 11:
If one executes this simple script, what happens? Bash forks, and the parent waits. The child executes the script, including the {{{chdir(2)}}} system call, and then exits. The parent, which was waiting for the child, harvests the child's exit status (presumably 0 for success), and then bash carries on with the next command.

Since the {{{chdir}}} was done b
y a child process, it has no effect on the parent.
If one executes this simple script, what happens? Bash forks, resulting in a parent (which is the bash in which you ran the command that executed the script) and a child (which is the bash fork that runs the actual script code). The child is started and the parent waits for it to finish. The child executes the script, including the {{{chdir(2)}}} system call, changes its current directory to `/tmp`, and then exits. The parent, which was waiting for the child, harvests the child's exit status (presumably 0 for success), and then bash carries on with the next command.  Nowhere in this process has the parent's current working directory changed, only the child's (which died as the script reached its end).
Line 17: Line 15:
So, how does one go about it? You can still have the {{{cd}}} command in an external file, but you can't ''run it'' as a script. Instead, you must {{{source}}} it (or "dot it in", using the {{{.}}} command, which is a synonym for {{{source}}}).
So, how does one go about changing the current working directory of the ''parent''? You can still have the {{{cd}}} command in an external file, but you can't ''run it'' as a script. That would cause the forking explained earlier. Instead, you must source it with {{{.}}} (or the Bash-only synonym, {{{source}}}). Sourcing basically means you execute the commands in a file using the ''current'' shell; not in a forked shell (child shell):
Line 20: Line 17:
   echo 'cd /tmp' > $HOME/mycd
   source $HOME/mycd
   pwd # Now, we're in /tmp
   echo 'cd /tmp' > "$HOME/mycd" # Create a file that contains the 'cd /tmp' command.
   . $HOME/mycd                   # Source that file, executing the 'cd /tmp' command in the current shell.
   pwd   # Now, we're in /tmp
Line 25: Line 22:
The same thing applies to setting variables. {{{source}}} the file that contains the commands; don't try to run it. The same thing applies to setting variables. {{{.}}} ('dot') the file that contains the commands; don't try to run it.
Line 27: Line 24:
Functions are run in the same same shell, so it is possible to put
If the command you execute is a function, not a script, it will be executed in the current shell. Therefore, it's possible to define a function to what we tried to do with an external file in the examples above, without needing to 'dot' or 'source' anything. Define the following function and then call it simply by executing `mycd`:
Line 33: Line 29:
in .bashrc or similar, and then use {{{mycd}}} to change the directory. Put it in `~/.bashrc` or similar if you want the function to automatically be available in every '''new session''' you open '''after''' putting it in that file.

Anchor(faq60)

I'm trying to write a script that will change directory (or set a variable), but after the script finishes, I'm back where I started (or my variable isn't set)!

Consider this:

   #!/bin/sh
   cd /tmp

If one executes this simple script, what happens? Bash forks, resulting in a parent (which is the bash in which you ran the command that executed the script) and a child (which is the bash fork that runs the actual script code). The child is started and the parent waits for it to finish. The child executes the script, including the chdir(2) system call, changes its current directory to /tmp, and then exits. The parent, which was waiting for the child, harvests the child's exit status (presumably 0 for success), and then bash carries on with the next command. Nowhere in this process has the parent's current working directory changed, only the child's (which died as the script reached its end).

Moreover, there is no conceivable way you can ever have a child process affect any part of the parent's environment, which includes its variables as well as its current working directory.

So, how does one go about changing the current working directory of the parent? You can still have the cd command in an external file, but you can't run it as a script. That would cause the forking explained earlier. Instead, you must source it with . (or the Bash-only synonym, source). Sourcing basically means you execute the commands in a file using the current shell; not in a forked shell (child shell):

   echo 'cd /tmp' > "$HOME/mycd"  # Create a file that contains the 'cd /tmp' command.
   . $HOME/mycd                   # Source that file, executing the 'cd /tmp' command in the current shell.
   pwd                            # Now, we're in /tmp

The same thing applies to setting variables. . ('dot') the file that contains the commands; don't try to run it.

If the command you execute is a function, not a script, it will be executed in the current shell. Therefore, it's possible to define a function to what we tried to do with an external file in the examples above, without needing to 'dot' or 'source' anything. Define the following function and then call it simply by executing mycd:

   mycd() { cd /tmp; }

Put it in ~/.bashrc or similar if you want the function to automatically be available in every new session you open after putting it in that file.

BashFAQ/060 (last edited 2014-04-27 04:35:48 by ormaaj)