views:

7467

answers:

10

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

#!/bin/bash
cd /home/askgelal/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?

+34  A: 

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/askgelal/projects/java"
Greg Hewgill
Technically, the previous directory isn't restored. For your interactive shell, the change never happened. It only happened for the subshell.
S.Lott
The alias goes in ~/.bash_profile and/or in ~/.bashrc
Federico Ramponi
Aliases aren't so flexible to manage or change. In case of many 'cd's, scripts could be better.
Thevs
Functions are more flexible than aliases, so that's where you'd look next when aliases aren't enough.
ephemient
I think this discussion gets pointless. "There are zillions ways to make things in Perl" (tm)
Thevs
+1 for the excellent explanation, coupled with a helpful alternative!
Adam Liss
Is it worth noting that on MS-DOS, the behaviour of scripts was that a called script could change the directory (and even drive) of the calling command shell? And that Unix does not have this defect?
Jonathan Leffler
Jonathan: while that's true, it's not really related to the question. Answers on SO would get twice as long if they had to each list the corresponding deficiencies in MS-DOS!
Greg Hewgill
+25  A: 

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.

Adam Liss
dot command: i learned something new today!
DarenW
`.` is also spelled `source`, choose whichever you find more memorable.
ephemient
@Ephemient: Good pointsource That explains why it workssource Also proves that laziness-not necessity-is often the mother of inventionsource
Adam Liss
@ephemient: note that source is used in C shell and Bash; it is not supported in POSIX or Korn shells, nor in classic Bourne shell.
Jonathan Leffler
+5  A: 

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.

Daniel Spiewak
+4  A: 

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.

yjerem
Having that symlink in every directory would be a nuisance. It would be possible to put the symlink in $HOME and then do 'cd ~/my-project'. Frankly, though, it is simpler to use CDPATH.
Jonathan Leffler
A: 

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)
Thevs
That gets rather messy after a few runs.. You will have to `exit` (or ctrl+d) several times to exit the shell, for example.. An alias is so much cleaner (even if the shell command outputs a directory, and it cd's to the output - alias something="cd `getnewdirectory.sh`")
dbr
Note the 'exec'. It makes replace of old shell.
Thevs
The exec only replaces the sub-shell that was running the cd command, not the shell that ran the script. Had you dotted the script, then you'd be correct.
Jonathan Leffler
Make it 'dotted' - no problem. I just proposed solution. It doesn't depend on how you launch this.
Thevs
Hehe, I think it's better just to 'dot' one-line script with only cd command :) I'll keep my answer anyway... That will be correct stupid answer to incorrect stupid question :)
Thevs
+10  A: 

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
Jonathan Leffler
+2  A: 

to navigate directories quicky, there's $CDPATH, cdargs, and ways to generate aliases automatically

http://jackndempsey.blogspot.com/2008/07/cdargs.html

http://muness.blogspot.com/2008/06/lazy-bash-cd-aliaes.html

http://articles.techrepublic.com.com/5100-10878_11-5827311.html

Gene T
+1  A: 

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.

J. A. Faucett
+2  A: 

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, then script name)

Tzachi.e
A: 
Matt Thomas