Ubuntu HowTo: Why doesn’t “cd” work in a shell script?

Original Source Link

I just want to write a script which changes my directory.

I put the below commands in the file /home/alex/pathABC

#!/bin/sh
cd /home/alex/Documents/A/B/C
echo HelloWorld

I did

chmod +x pathABC

In the Terminal, while in /home/alex, I ran ./pathABC, but the output is just HelloWorld and the current directory is not changed.

So what is wrong?

As others have explained, the directory is changed in the child process of your script, not in the terminal process from which the script is called. After the child process dies, you are back in the terminal which is left where it was.

Several alternatives:

1. Symbolic link

Put a symlink in your home to the long path you want to easily access

$ ln -s /home/alex/Documents/A/B/C ~/pathABC

then access the directory with:

$ cd ~/pathABC

2. Alias

Put an alias in your ~/.bashrc:

alias pathABC="cd /home/alex/Documents/A/B/C"

(from here)

3. Function

Create a function that changes the directory, the function runs in the process of your terminal and can then change its directory.

(from here)

4. Avoid running as child

Source your script instead of running it. Sourcing (done by . or source) causes the script to be executed in the same shell instead of running in its own subshell.

$ . ./pathABC

(from here and here)

5. cd-able vars

Set the cdable_vars option in your ~/.bashrc and create an environment variable to the directory:

shopt -s cdable_vars
export pathABC="/home/alex/Documents/A/B/C"

Then you can use cd pathABC

(from here)

When you run script in a Terminal, a child process runs. In this child program i.e. your script will change to whatever directory specified. But in the parent process, i.e. where you run the script is still in the old path.
OR simply we can say:

The scope of cd command is only for child process not parent.

You are making a thinking error. While the current shell stays in the same directory, the script has moved to the new directory.

You could see that by creating another script in the new directory, and running it from your script, after it has changed directory:

#!/bin/sh
cd /home/alex/Documents/A/B/C && ./another_script.sh # (if it is executable)

The second script would run from the new directory.

HelloWorld 

is just the output of the script.

Because hello world is just a trace statement, let’s try this:

Create bash script file cd.sh containing:

#!/bin/bash
echo "/home/mike/Documents/A/B/C"
  • The .sh extension is an older convention of giving bash script filenames an extension. It’s purely cosmetic and usually unnecessary. However in this case it’s important to differentiate from the core cd command.

Mark the bash script file executable using:

chmod a+x cd.sh

Now run the file:

$ cd $(./cd.sh)
bash: cd: /home/alex/Documents/A/B/C: No such file or directory
  • cd we all know.
  • $(...) executes command inside parenthesis and returns output.
  • If cd.sh was in your path you don’t need to specify where it is. We prefix with ./ to specify the command is in the current directory.
  • The echo output from the cd.sh script is sent back to the parent via the $(...). The parent (our shell prompt) uses this output and passes it to the Linux cd command.

As others have mentioned a child process can’t change the parent’s directory. This is one way the child can tell the parent where to go after the process ends.

Trying to use cd inside the shell script does not work because the shell script runs in the subshell and once the script is over it returns to the parent shell, which is why the current directory does not change.

To achieve changing of the directory use sourcing.You can either use . scriptname.sh or source scriptname.sh command to use sourcing.

Note : Also when you use sourcing do not use the exit command because it then closes your connection.

CDPATH might help in some cases.

# add this to .bashrc .zshrc or whatever
export CDPATH="/home/alex/Documents/A/B:$CDPATH"

# and then you can just do...
cd C

Note u have to add ‘A/B’ to CDPATH, not ‘A/B/C’
You can add multiple paths to CDPATH just like PATH.

Actually, I just found, after many searches, that if you need to change the directory, and still keep the same shell, so you will get all the answers in your currect script, you can use:

(cd your_dir; do_some_command_there)

For example, what I needed to use, was:

((cd your_dir; git remote -v | wc -l)

Works like a charm!

Tagged : / / /

Making Game: libtool error cd: ../..: Not a directory

Original Source Link

Getting this very bizarre error from libtool when trying to install a package I built. It happens when running in a subdirectory (src/api) of the source tree:

make[5]: Leaving directory '/users/galac/embray/src/slurm/src/api'
 /bin/mkdir -p '/usr/local/lib'
 /bin/bash ../../libtool   --mode=install /usr/bin/install -c   libslurm.la '/usr/local/lib'
../../libtool: line 929: cd: ../..: Not a directory

The relevant section of libtool looks like this:

  914 # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
  915 # is ksh but when the shell is invoked as "sh" and the current value of
  916 # the _XPG environment variable is not equal to 1 (one), the special
  917 # positional parameter $0, within a function call, is the name of the
  918 # function.
  919 progpath=$0
  920 
  921 # The name of this program.
  922 progname=`$ECHO "$progpath" |$SED "$sed_basename"`
  923 
  924 # Make sure we have an absolute progpath for reexecution:
  925 case $progpath in
  926   [\/]*|[A-Za-z]:\*) ;;
  927   *[\/]*)
  928      progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
  929      progdir=`cd "$progdir" && pwd`
  930      progpath=$progdir/$progname
  931      ;;
  932   *)
  933      _G_IFS=$IFS
  934      IFS=${PATH_SEPARATOR-:}
  935      for progdir in $PATH; do
  936        IFS=$_G_IFS
  937        test -x "$progdir/$progname" && break
  938      done
  939      IFS=$_G_IFS
  940      test -n "$progdir" || progdir=`pwd`
  941      progpath=$progdir/$progname
  942      ;;
  943 esac

If I insert set -x into the script around this section, I see the following trace:

+ progpath=../../libtool
++ printf '%sn' ../../libtool
++ /bin/sed 's|^.*/||'
+ progname=libtool
+ case $progpath in
++ printf '%sn' ../../libtool
++ /bin/sed 's|/[^/]*$||'
+ progdir=../..
++ cd ../..
../../libtool: line 930: cd: ../..: Not a directory
+ progdir=
+ progpath=/libtool
...

this leads to further errors since it does not set progpath=../../libtool, the correct path for libtool itself (in the top-level source dir). It also seems to be correctly setting progdir=../... So why is ../.. not a directory?

Clearly it looks fine if I check manually:

~/src/slurm/src/api$ ls -ld ../..
drwxr-xr-x 11 xxxxxx xxxxx 4096 May 14 14:52 ../..

It is not a symlink or anything like that.

Never seen anything like this in my 20 years of development.

The problem was that I’m on an NFS mount on an HPC system (on which I have administrative privileges). But the error occurred when running sudo make install.

The NFS server appears to be using the root_squash option, which makes NFS mounts unreadable by the root user; see https://linux.die.net/man/5/exports

The solution is to simply move my build to a non-NFS filesystem and install from there. Thanks to everyone who had a look.

Tagged : / /