I want to run a script to simply change the current working directory:
#!/bin/bash
cd web/www/project
But, after I run it, the current pwd remains unchanged! How can I do that?
I want to run a script to simply change the current working directory:
#!/bin/bash
cd web/www/project
But, after I run it, the current pwd remains unchanged! How can I do that?
It is an expected behavior. The script is run in a subshell, and cannot change the parent shell working directory. Its effects are lost when it finishes.
To change the current shell's directory permanently you should use the source
command, also aliased simply as .
, which runs a script in the current shell environment instead of a sub shell.
The following commands are identical:
. script
or
source script
For small tasks such as this, instead of creating script, create an alias like this,
$ alias cdproj='cd /dir/web/www/proj'
You should add this to your .bashrc
file, if you want it set for every interactive shell.
Now you can run this as $ cdproj
.
eval \
./script` or
eval $(./script)` to execute those commands. This is a common approach for commands that need to update the invoking shell's environment.
– Keith Thompson
Dec 20 '11 at 10:41
eval
approach.
– jw013
Sep 14 '12 at 20:08
exec bash
at the endA bash script operates on its current environment or on that of its children, but never on its parent environment.
However, this question often gets asked because one wants to be left at the bash prompt in a certain directory after the execution of a bash script from another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd desired/directory
exec bash
This creates a new subshell. Type Ctrl+D or exit
to return to the first shell where the script was initially started.
$SHELL
at the endAt least with newer versions of bash
, the exec
on the last line is no longer required. Furthermore, the script can be made to work with whatever preferred shell by using the $SHELL
environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
$SHELL
exec
is typically considered the last resort of a scoundrel.. :)
– neuronet
Aug 16 '16 at 00:18
exit
(or Ctrl+D), you’ll have to do that 11 to 21 times. (2) Another drawback of using an executable script is that, if you set any shell options (e.g., dotglob
or globstar
) in your interactive shell session, you will lose them, because you’re starting a new shell.
– G-Man Says 'Reinstate Monica'
Apr 10 '19 at 16:39
exec
was required with older versions of bash
and possibly other shells. I updated the answer accordingly.
– Serge Stroobandt
Oct 25 '20 at 10:49
cdf
that given a file path it takes me to its directory: cd "$(dirname "$1")"; $SHELL
– Marinos An
Jan 27 '21 at 13:41
exec
, the current process is replaced, so nothing piles up. And I personally never change shell options interactively in normal use.
– xeruf
Aug 11 '21 at 10:17
exec bash
(or anything similar) into an interactive shell, it will replace the current shell process with a new copy of bash
. But surely you know that, whenever you run a utility program (like cp
or ls
) or a script, your interactive shell forks a new process to run that program. (Of course this does not apply to built-in commands like cd
and pwd
.) … (Cont’d)
– G-Man Says 'Reinstate Monica'
Aug 21 '21 at 00:13
exit
command, the script-running shell process terminates. But when the script does exec bash
, the script-running shell process is replaced with a new, interactive shell process, in addition to the one that launched the script. So, yes, bash processes pile up.
– G-Man Says 'Reinstate Monica'
Aug 21 '21 at 00:13
pstree
what type of processes this creates. You will notice that each time you run the script a new shell process is created, without replacing the invoking interactive shell. The exec
would replace the non-interactive shell running the script, but not the interactive shell invoking the script. As have been pointed out, this also resets several aspects of the shell, and unless special care is taken, shell's command line history is not carried forward into the new shell session either.
– Kusalananda
Aug 21 '21 at 06:52
Depends on what you're going to do, another solution can be creating a function instead of a script.
Example:
Create a function in a file, let's say /home/aidin/my-cd-script
:
function my-cd() {
cd /to/my/path
}
Then include it in your bashrc
or zshrc
file:
# Somewhere in rc file
source /home/aidin/my-cd-script
Now you can use it like a command:
$ my-cd
While there are answers that do the exact action that you want, a more standard method for such purpose is to create symbolic link:
ln -s ~/web/www/project proj #use full path to dir!
Then you could cd
to the directory using the name proj
:
cd proj
This method is more flexible because you could access files using the short name without cd
:
ls proj/ #note the endslash!
vim proj/file.x
Because I functionalized a lot my cd, i did this :
added this line in ~/.bashrc
alias cd='. my_cd'
and my_cd is a script in my $PATH
that does the actual cd
.
To prevent recusive calls an actual cd
in the script is written \cd
this means "uses legacy cd not the alias".
By functionalized i mean
cd
in a project subdir brings me to the project home dir, not my home.cd
to a project inexistent (project name have nomenclature): suggest to create the environement of the project.cd
to a project that hapen to be archived : ask to revive it or just move to archive.otherwise works like cd
.
For me the most convenient and flexible approach was a mixture of an alias and a script:
Here I create a script that changes to a directory and activates the appropriate python environment. The scripts location is exmplary in /path/to/workon_myproj.sh
.
#!/usr/bin/env bash
cd $HOME/workspace/myproj
source .venv/bin/activate
alias workon_myproj='source /path/to/workon_myproj.sh'
Add the alias definition into your appropriate shell start file e.g.
.profile
, .bashrc
or .zshrc
.
You can now simply execute workon_myproj
in a shell which will source the content of your script in the desired directory.
You could event improve your script to take an argument so that it works with multiple projects in a specific working directory, or combine it with a git pull
to get the latest changes immediately and so on... everything boiler plate stuff you do when continuing to work on a specific project.
This combines the answer by Serge with an unrelated answer by David. It changes the directory, and then instead of forcing a bash shell, it launches the user's default shell. It however requires both getent
and /etc/passwd
to detect the default shell.
#!/usr/bin/env bash
cd desired/directory
USER_SHELL=$(getent passwd <USER> | cut -d : -f 7)
$USER_SHELL
Of course this still has the same deficiency of creating a nested shell.
Why not use "exec" it seams to do exactly what I wish.
#!/bin/bash
cd someplace
exec bash
~/someplace
exit
(or Ctrl+D), you’ll have to do that 11 to 21 times.
– G-Man Says 'Reinstate Monica'
Apr 08 '19 at 04:23
dotglob
or globstar
), you will lose them, because you’re starting a new shell. … (Cont’d)
– G-Man Says 'Reinstate Monica'
Apr 10 '19 at 16:05
You can do that using a function or using && The examples bellow installs Zabbix and creates a file with a line inside it.
Ex:
#!/bin/bash
# Create Function:
installZabbix(){
cd /usr/src/zabbix-4.2.4;
./configure --enable-agent;
make install;
cd /usr/src/;
>file;
echo "Hi, this is a file." >>file;
}
# Call the function:
installZabbix
or:
#!/bin/bash
cd /usr/src/zabbix-4.2.4 && ./configure --enable-agent && make install && cd /usr/src && >file && echo "Hi, this is a file." >>file
return
to escape from a script sourced in this way, notexit
- they are like shell functions, andexit
will exit the shell that sourced the script. – Charles Stewart Dec 19 '11 at 08:19source ./script
the same? – amyassin Dec 19 '11 at 13:04.
andsource
are equal in bash../
before filename if it's in the same directory. It is ok to run only this:. script
ls -a
for example. The similarity to the . command is just a bad coincidence I think../
is required to execute programs that are not in PATH. If it's just an argument, like to the . command, then it's (mostly) redundant (programs can see the difference, try e.g.find subdir
vsfind ./subdir
- you get equivalent but differently spelled output in this example). – hawk Jul 18 '21 at 11:37