Monday, May 21, 2012

Why doesn"t "cd' work in a bash shell script?


I'm trying to write a small script to change the current directory to my project directory:




#!/bin/bash
cd /home/tree/projects/java



I saved this file as proj, changed the chmod, copied it to /usr/bin . When I call it by: proj , it does nothing. What am I doing wrong?


Source: Tips4all

12 comments:

  1. Shell scripts are run inside a subshell, and each subshell has its own concept of what the current directory is. The cd succeeds, but as soon as the subshell exits, the previous current directory is restored.

    One way to get around this is to use an alias instead:

    alias proj="cd /home/tree/projects/java"

    ReplyDelete
  2. Nothing. You've changed the directory, but only within the subshell that runs the script.

    You can run the script in your current process with the "dot" command:

    . proj


    But I'd prefer Greg's suggestion to use an alias in this simple case.

    ReplyDelete
  3. Jeremy Ruten's idea of using a symlink triggered a thought that hasn't crossed any other answer. Use:

    CDPATH=:$HOME/projects


    The leading colon is important; it means that if there is a directory 'dir' in the current directory, then 'cd dir' will change to that, rather than hopping off somewhere else. With the value set as shown, you can do:

    cd java


    and, if there is no sub-directory called java in the current directory, then it will take you directly to $HOME/projects/java - no aliases, no scripts, no dubious execs or dot commands.

    My $HOME is /Users/jleffler; my $CDPATH is:

    :/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work

    ReplyDelete
  4. The cd is done within the scripts' shell, the shell exits, and then you are left in the directory you were... source the script, don't run it. instead of:

    # ./myscript.sh


    do

    # . ./myscript.sh


    (notice the dot, space, and script name)

    ReplyDelete
  5. The cd in your script technically worked as it changed the directory of the shell that ran the script, but that was a separate process forked from your interactive shell.

    A Posix-compatible way to solve this problem is to define a shell procedure rather than a shell-invoked command script.

    jhome () {
    cd /home/tree/projects/java
    }


    You can just type this in or put it in one of the various shell startup files.

    ReplyDelete
  6. To make a bash script that will cd to a select directory :

    Create the script file


    #!/bin/sh
    # file : /scripts/cdjava
    #
    cd /home/askgelal/projects/java


    Then create an alias in your startup file.


    #!/bin/sh
    # file /scripts/mastercode.sh
    #
    alias cdjava='. /scripts/cdjava'





    I created a startup file where I dump all my aliases and custom functions.
    Then I source this file into my .bashrc to have it set on each boot.



    For example, create a master aliases/functions file: /scripts/mastercode.sh
    (Put the alias in this file.)


    Then at the end of your .bashrc file:


    source /scripts/mastercode.sh






    Now its easy to cd to your java directory, just type cdjava and you are there.

    ReplyDelete
  7. When you fire a shell script, it runs a new instance of that shell (/bin/bash). Thus, your script just fires up a shell, changes the directory and exits. Put another way, cd (and other such commands) within a shell script do not affect nor have access to the shell from which they were launched.

    ReplyDelete
  8. It only changes the directory for the script itself, while your current directory stays the same.

    You might want to use a symbolic link instead. It allows you to make a "shortcut" to a file or directory, so you'd only have to type something like cd my-project.

    ReplyDelete
  9. You can combine an alias and a script:

    alias proj='cd \`/usr/bin/proj !*\`"


    provided that the script echos the destination path. Note that those are backticks surrounding the script name.

    For example, your script could be

    #!/bin/bash
    echo /home/askgelal/projects/java/$1


    The advantage with this technique is that the script could take any number of command line parameters and emit different destinations calculated by possibly complex logic.

    ReplyDelete
  10. You can do following:

    #!/bin/bash
    cd /your/project/directory
    # start another shell and replacing the current
    exec /bin/bash


    EDIT: This could be 'dotted' as well, to prevent creation of subsequent shells.

    Example:

    . ./previous_script (with or without the first line)

    ReplyDelete
  11. LOOOOOng time after, but I did the following:

    create a file called case

    paste the following in the file:

    #!/bin/sh

    cd /home/"$1"


    save it and then:

    chmod +x case


    I also created an alias in my .bashrc:

    alias disk='cd /home/; . case'


    now when I type:

    case 12345


    essentially I am typing:

    cd /home/12345


    You can type any folder after 'case':

    case 12

    case 15

    case 17


    which is like typing:

    cd /home/12

    cd /home/15

    cd /home/17


    respectively

    In my case the path is much longer - these guys summed it up with the ~ info earlier.

    ReplyDelete
  12. You can combine Adam & Greg's alias and dot approaches to make something that can be more dynamic—

    alias project=". project"


    Now running the project alias will execute the project script in the current shell as opposed to the subshell.

    ReplyDelete