Bash commands don't work after changing Path variable - linux

I'm unable to use ls, bash.. any popular commands that are critical after changing the path.
I'm unsure what it was before (because I can't do vi command either).
I ran the first command, and realized the first one had a typo - not PATH, but I've typed PATh.
So I immediately ran the next one:
export PATH="/usr/local/bin:$PATh"
export PATH="/usr/local/bin:$PATH"
Then vi, ls, bash commands started to not work.
I did echo $PATH to see the PATH.
usr/local/bin:/usr/local/bin:/usr/local/bin:/usr/local/bin:
This is what I got. Any help is appreciated.

You should be able to source /etc/profile to reset your PATH variable, though it may step on a few other variables you've configured along the way. You could also just grep for the appropriate line from that to set PATH and redo that in your current environment
Also, you can always specify the full path to an executable you need in the interim. For example, if you wanted to use grep with the PATH in its current state you could use /bin/grep (or perhaps /usr/bin/grep depending your system)

1 > Try to load default .profile script
$ /bin/bash ./.profile
2 > Just Logout from current session
and Re-login it.

It appears you have "broken" your bash shell ~/.bash_profile script, because of the PATh typo. (The question explicitly states bash, so I'm answering in that context.)
Since the PATH is "broken", you will need to access your editor by using its fully qualified path.
/usr/bin/vi ~/.bash_profile
That should allow you to fix the PATh to be PATH.
If you find that you need to edit your PATH environment variable a lot, this little bash function can be helpful.
vipath() {
local tmpfile=$(mktemp /tmp/vipath-edit.XXXXXX)
echo "$PATH" | tr ':' '\n' > "$tmpfile"
vi "$tmpfile" && PATH=$(paste -s -d ':' "$tmpfile")
rm "$tmpfile"
}
Note: there are better ways to ensure the $tmpfile gets deleted that I did not use in this snippet. And on a multiuser system, there are better ways to make sure the temporary file is not located in a shared location.
If you want to add a directory location to your PATH, without adding duplicate locations, this little bash function can be helpful to prepend a directory location.
pathadd() {
if [ -d "$1" ] && [[ ! ":$PATH:" =~ ":$1:" ]]
then
PATH="$1:$PATH"
fi
}

I had the same in RHEL8, I did an export PATH for a certain directory and then no command worked anymore. I performed a faulty export PATH command probably.
I got messages like this:
>$ yum
bash: yum: command not found...
Install package 'yum' to provide command 'yum'? [N/y] n
Luckily I had some other similar systems where I could get the path from, so I did:
export PATH=/home/USER/.local/bin:/home/USER/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbine
Change USER to your own.
To make it permanent: add to $HOME/.bashrc with:
export PATH=$PATH:<YOUR PATH HERE>

When you do export PATH="/usr/local/bin:$PATH" you are saying, set PATH to /usr/local/bin: plus whatever was in the PATH variable before. This essentially concatenates the string "/usr/local/bin:" with the old path. That is why your PATH repeats so much, you must have run that command a few times.
Try running this: export PATH="/usr/local/bin".

Related

Running `cd` in `.sh` file in anaconda environment on Linux [duplicate]

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, added execute permission with chmod, and copied it to /usr/bin. When I call it by:
proj, it does nothing. What am I doing wrong?
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, you're back in the interactive shell and nothing ever changed there.
One way to get around this is to use an alias instead:
alias proj="cd /home/tree/projects/java"
You're doing nothing wrong! 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.
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.
The cd is done within the script's shell. When the script ends, that 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 and space before the script name.)
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.
You can use . to execute a script in the current shell environment:
. script_name
or alternatively, its more readable but shell specific alias source:
source script_name
This avoids the subshell, and allows any variables or builtins (including cd) to affect the current shell instead.
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
Use exec bash at the end
A 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 a (new) bash prompt in a certain directory after execution of a bash script from within another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash
To return to the previous, parental bash instance, use Ctrl+D.
Update
At least with newer versions of bash, the exec on the last line is no longer required. Furthermore, the script could be made to work with whatever preferred shell by using the $SHELL environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL
I got my code to work by using. <your file name>
./<your file name> dose not work because it doesn't change your directory in the terminal it just changes the directory specific to that script.
Here is my program
#!/bin/bash
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace
Here is my terminal
nova:~ Kael$
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$
simply run:
cd /home/xxx/yyy && command_you_want
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.
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)
On my particular case i needed too many times to change for the same directory.
So on my .bashrc (I use ubuntu) i've added the
1 -
$ nano ~./bashrc
function switchp
{
cd /home/tree/projects/$1
}
2-
$ source ~/.bashrc
3 -
$ switchp java
Directly it will do: cd /home/tree/projects/java
Hope that helps!
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.
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.
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.
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
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5827311.html
In your ~/.bash_profile file. add the next function
move_me() {
cd ~/path/to/dest
}
Restart terminal and you can type
move_me
and you will be moved to the destination folder.
You can use the operator && :
cd myDirectory && ls
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo "Arguments to the functions were $1, $2, ..."
}
This technique is used by autojump: http://github.com/joelthelion/autojump/wiki to provide you with learning shell directory bookmarks.
You can create a function like below in your .bash_profile and it will work smoothly.
The following function takes an optional parameter which is a project.
For example, you can just run
cdproj
or
cdproj project_name
Here is the function definition.
cdproj(){
dir=/Users/yourname/projects
if [ "$1" ]; then
cd "${dir}/${1}"
else
cd "${dir}"
fi
}
Dont forget to source your .bash_profile
This should do what you want. Change to the directory of interest (from within the script), and then spawn a new bash shell.
#!/bin/bash
# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash
If you run this, it will take you to the directory of interest and when you exit it it will bring you back to the original place.
root#intel-corei7-64:~# ./mov_dir.sh
root#intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root#intel-corei7-64:~#
This will even take you to back to your original directory when you exit (CTRL+d)
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.
As explained on the other answers, you have changed the directory, but only within the sub-shell that runs the script. this does not impact the parent shell.
One solution is to use bash functions instead of a bash script (sh); by placing your bash script code into a function. That makes the function available as a command and then, this will be executed without a child process and thus any cd command will impact the caller shell.
Bash functions :
One feature of the bash profile is to store custom functions that can be run in the terminal or in bash scripts the same way you run application/commands this also could be used as a shortcut for long commands.
To make your function efficient system widely you will need to copy your function at the end of several files
/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile
You can sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile to edit/create those files quickly
Howto :
Copy your bash script code inside a new function at the end of your bash's profile file and restart your terminal, you can then run cdd or whatever the function you wrote.
Script Example
Making shortcut to cd .. with cdd
cdd() {
cd ..
}
ls shortcut
ll() {
ls -l -h
}
ls shortcut
lll() {
ls -l -h -a
}
If you are using fish as your shell, the best solution is to create a function. As an example, given the original question, you could copy the 4 lines below and paste them into your fish command line:
function proj
cd /home/tree/projects/java
end
funcsave proj
This will create the function and save it for use later. If your project changes, just repeat the process using the new path.
If you prefer, you can manually add the function file by doing the following:
nano ~/.config/fish/functions/proj.fish
and enter the text:
function proj
cd /home/tree/projects/java
end
and finally press ctrl+x to exit and y followed by return to save your changes.
(NOTE: the first method of using funcsave creates the proj.fish file for you).
You need no script, only set the correct option and create an environment variable.
shopt -s cdable_vars
in your ~/.bashrc allows to cd to the content of environment variables.
Create such an environment variable:
export myjava="/home/tree/projects/java"
and you can use:
cd myjava
Other alternatives.
Note the discussion How do I set the working directory of the parent process?
It contains some hackish answers, e.g.
https://stackoverflow.com/a/2375174/755804 (changing the parent process directory via gdb, don't do this) and https://stackoverflow.com/a/51985735/755804 (the command tailcd that injects cd dirname to the input stream of the parent process; well, ideally it should be a part of bash rather than a hack)
It is an old question, but I am really surprised I don't see this trick here
Instead of using cd you can use
export PWD=the/path/you/want
No need to create subshells or use aliases.
Note that it is your responsibility to make sure the/path/you/want exists.
I have to work in tcsh, and I know this is not an elegant solution, but for example, if I had to change folders to a path where one word is different, the whole thing can be done in the alias
a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'
If the path is always fixed, the just
a alias_name2 'cd path/you/always/need'
should work
In the line above, the new folder path is set
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.

how do you change into another directory in a shell script [duplicate]

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, added execute permission with chmod, and copied it to /usr/bin. When I call it by:
proj, it does nothing. What am I doing wrong?
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, you're back in the interactive shell and nothing ever changed there.
One way to get around this is to use an alias instead:
alias proj="cd /home/tree/projects/java"
You're doing nothing wrong! 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.
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.
The cd is done within the script's shell. When the script ends, that 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 and space before the script name.)
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.
You can use . to execute a script in the current shell environment:
. script_name
or alternatively, its more readable but shell specific alias source:
source script_name
This avoids the subshell, and allows any variables or builtins (including cd) to affect the current shell instead.
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
Use exec bash at the end
A 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 a (new) bash prompt in a certain directory after execution of a bash script from within another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash
To return to the previous, parental bash instance, use Ctrl+D.
Update
At least with newer versions of bash, the exec on the last line is no longer required. Furthermore, the script could be made to work with whatever preferred shell by using the $SHELL environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL
I got my code to work by using. <your file name>
./<your file name> dose not work because it doesn't change your directory in the terminal it just changes the directory specific to that script.
Here is my program
#!/bin/bash
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace
Here is my terminal
nova:~ Kael$
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$
simply run:
cd /home/xxx/yyy && command_you_want
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.
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)
On my particular case i needed too many times to change for the same directory.
So on my .bashrc (I use ubuntu) i've added the
1 -
$ nano ~./bashrc
function switchp
{
cd /home/tree/projects/$1
}
2-
$ source ~/.bashrc
3 -
$ switchp java
Directly it will do: cd /home/tree/projects/java
Hope that helps!
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.
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.
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.
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
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5827311.html
In your ~/.bash_profile file. add the next function
move_me() {
cd ~/path/to/dest
}
Restart terminal and you can type
move_me
and you will be moved to the destination folder.
You can use the operator && :
cd myDirectory && ls
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo "Arguments to the functions were $1, $2, ..."
}
This technique is used by autojump: http://github.com/joelthelion/autojump/wiki to provide you with learning shell directory bookmarks.
You can create a function like below in your .bash_profile and it will work smoothly.
The following function takes an optional parameter which is a project.
For example, you can just run
cdproj
or
cdproj project_name
Here is the function definition.
cdproj(){
dir=/Users/yourname/projects
if [ "$1" ]; then
cd "${dir}/${1}"
else
cd "${dir}"
fi
}
Dont forget to source your .bash_profile
This should do what you want. Change to the directory of interest (from within the script), and then spawn a new bash shell.
#!/bin/bash
# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash
If you run this, it will take you to the directory of interest and when you exit it it will bring you back to the original place.
root#intel-corei7-64:~# ./mov_dir.sh
root#intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root#intel-corei7-64:~#
This will even take you to back to your original directory when you exit (CTRL+d)
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.
As explained on the other answers, you have changed the directory, but only within the sub-shell that runs the script. this does not impact the parent shell.
One solution is to use bash functions instead of a bash script (sh); by placing your bash script code into a function. That makes the function available as a command and then, this will be executed without a child process and thus any cd command will impact the caller shell.
Bash functions :
One feature of the bash profile is to store custom functions that can be run in the terminal or in bash scripts the same way you run application/commands this also could be used as a shortcut for long commands.
To make your function efficient system widely you will need to copy your function at the end of several files
/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile
You can sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile to edit/create those files quickly
Howto :
Copy your bash script code inside a new function at the end of your bash's profile file and restart your terminal, you can then run cdd or whatever the function you wrote.
Script Example
Making shortcut to cd .. with cdd
cdd() {
cd ..
}
ls shortcut
ll() {
ls -l -h
}
ls shortcut
lll() {
ls -l -h -a
}
If you are using fish as your shell, the best solution is to create a function. As an example, given the original question, you could copy the 4 lines below and paste them into your fish command line:
function proj
cd /home/tree/projects/java
end
funcsave proj
This will create the function and save it for use later. If your project changes, just repeat the process using the new path.
If you prefer, you can manually add the function file by doing the following:
nano ~/.config/fish/functions/proj.fish
and enter the text:
function proj
cd /home/tree/projects/java
end
and finally press ctrl+x to exit and y followed by return to save your changes.
(NOTE: the first method of using funcsave creates the proj.fish file for you).
You need no script, only set the correct option and create an environment variable.
shopt -s cdable_vars
in your ~/.bashrc allows to cd to the content of environment variables.
Create such an environment variable:
export myjava="/home/tree/projects/java"
and you can use:
cd myjava
Other alternatives.
Note the discussion How do I set the working directory of the parent process?
It contains some hackish answers, e.g.
https://stackoverflow.com/a/2375174/755804 (changing the parent process directory via gdb, don't do this) and https://stackoverflow.com/a/51985735/755804 (the command tailcd that injects cd dirname to the input stream of the parent process; well, ideally it should be a part of bash rather than a hack)
It is an old question, but I am really surprised I don't see this trick here
Instead of using cd you can use
export PWD=the/path/you/want
No need to create subshells or use aliases.
Note that it is your responsibility to make sure the/path/you/want exists.
I have to work in tcsh, and I know this is not an elegant solution, but for example, if I had to change folders to a path where one word is different, the whole thing can be done in the alias
a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'
If the path is always fixed, the just
a alias_name2 'cd path/you/always/need'
should work
In the line above, the new folder path is set
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.

Bash decorating "cd" - change directory inside a script [duplicate]

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, added execute permission with chmod, and copied it to /usr/bin. When I call it by:
proj, it does nothing. What am I doing wrong?
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, you're back in the interactive shell and nothing ever changed there.
One way to get around this is to use an alias instead:
alias proj="cd /home/tree/projects/java"
You're doing nothing wrong! 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.
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.
The cd is done within the script's shell. When the script ends, that 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 and space before the script name.)
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.
You can use . to execute a script in the current shell environment:
. script_name
or alternatively, its more readable but shell specific alias source:
source script_name
This avoids the subshell, and allows any variables or builtins (including cd) to affect the current shell instead.
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
Use exec bash at the end
A 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 a (new) bash prompt in a certain directory after execution of a bash script from within another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash
To return to the previous, parental bash instance, use Ctrl+D.
Update
At least with newer versions of bash, the exec on the last line is no longer required. Furthermore, the script could be made to work with whatever preferred shell by using the $SHELL environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL
I got my code to work by using. <your file name>
./<your file name> dose not work because it doesn't change your directory in the terminal it just changes the directory specific to that script.
Here is my program
#!/bin/bash
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace
Here is my terminal
nova:~ Kael$
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$
simply run:
cd /home/xxx/yyy && command_you_want
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.
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)
On my particular case i needed too many times to change for the same directory.
So on my .bashrc (I use ubuntu) i've added the
1 -
$ nano ~./bashrc
function switchp
{
cd /home/tree/projects/$1
}
2-
$ source ~/.bashrc
3 -
$ switchp java
Directly it will do: cd /home/tree/projects/java
Hope that helps!
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.
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.
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.
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
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5827311.html
In your ~/.bash_profile file. add the next function
move_me() {
cd ~/path/to/dest
}
Restart terminal and you can type
move_me
and you will be moved to the destination folder.
You can use the operator && :
cd myDirectory && ls
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo "Arguments to the functions were $1, $2, ..."
}
This technique is used by autojump: http://github.com/joelthelion/autojump/wiki to provide you with learning shell directory bookmarks.
You can create a function like below in your .bash_profile and it will work smoothly.
The following function takes an optional parameter which is a project.
For example, you can just run
cdproj
or
cdproj project_name
Here is the function definition.
cdproj(){
dir=/Users/yourname/projects
if [ "$1" ]; then
cd "${dir}/${1}"
else
cd "${dir}"
fi
}
Dont forget to source your .bash_profile
This should do what you want. Change to the directory of interest (from within the script), and then spawn a new bash shell.
#!/bin/bash
# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash
If you run this, it will take you to the directory of interest and when you exit it it will bring you back to the original place.
root#intel-corei7-64:~# ./mov_dir.sh
root#intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root#intel-corei7-64:~#
This will even take you to back to your original directory when you exit (CTRL+d)
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.
As explained on the other answers, you have changed the directory, but only within the sub-shell that runs the script. this does not impact the parent shell.
One solution is to use bash functions instead of a bash script (sh); by placing your bash script code into a function. That makes the function available as a command and then, this will be executed without a child process and thus any cd command will impact the caller shell.
Bash functions :
One feature of the bash profile is to store custom functions that can be run in the terminal or in bash scripts the same way you run application/commands this also could be used as a shortcut for long commands.
To make your function efficient system widely you will need to copy your function at the end of several files
/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile
You can sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile to edit/create those files quickly
Howto :
Copy your bash script code inside a new function at the end of your bash's profile file and restart your terminal, you can then run cdd or whatever the function you wrote.
Script Example
Making shortcut to cd .. with cdd
cdd() {
cd ..
}
ls shortcut
ll() {
ls -l -h
}
ls shortcut
lll() {
ls -l -h -a
}
If you are using fish as your shell, the best solution is to create a function. As an example, given the original question, you could copy the 4 lines below and paste them into your fish command line:
function proj
cd /home/tree/projects/java
end
funcsave proj
This will create the function and save it for use later. If your project changes, just repeat the process using the new path.
If you prefer, you can manually add the function file by doing the following:
nano ~/.config/fish/functions/proj.fish
and enter the text:
function proj
cd /home/tree/projects/java
end
and finally press ctrl+x to exit and y followed by return to save your changes.
(NOTE: the first method of using funcsave creates the proj.fish file for you).
You need no script, only set the correct option and create an environment variable.
shopt -s cdable_vars
in your ~/.bashrc allows to cd to the content of environment variables.
Create such an environment variable:
export myjava="/home/tree/projects/java"
and you can use:
cd myjava
Other alternatives.
Note the discussion How do I set the working directory of the parent process?
It contains some hackish answers, e.g.
https://stackoverflow.com/a/2375174/755804 (changing the parent process directory via gdb, don't do this) and https://stackoverflow.com/a/51985735/755804 (the command tailcd that injects cd dirname to the input stream of the parent process; well, ideally it should be a part of bash rather than a hack)
It is an old question, but I am really surprised I don't see this trick here
Instead of using cd you can use
export PWD=the/path/you/want
No need to create subshells or use aliases.
Note that it is your responsibility to make sure the/path/you/want exists.
I have to work in tcsh, and I know this is not an elegant solution, but for example, if I had to change folders to a path where one word is different, the whole thing can be done in the alias
a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'
If the path is always fixed, the just
a alias_name2 'cd path/you/always/need'
should work
In the line above, the new folder path is set
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.

Bash: Invalidated commands

I've incurred a worrisome issue with my bash shell. I was editing my bash_profile and accidentally exported an incomplete command (export PATH=/usr/local/bin). After I had reloaded my terminal, nearly all of my bash commands fail to work properly. When I try to run any one of them, the errors state: command not found.
How do I fix this? Is there an alternate way to open or find my bash_profile?
I would appreciate any immediate input I can get on this issue. Thank you in advance.
You can execute commands if you can give the directory name. Almost all the basic Unix commands are under the /bin or /usr/bin directory. For example, /bin/mv.
Fortunately, builtin commands are still recognizable.
Move your .bash_profile and .bashrc file out of the way for now, and see what the system default is.
You can manually edit your PATH on the command line to:
$ PATH="/bin:/usr/bin"
$ cd
$ mv .bash_profile .bash_profile.bak
$ mv .bashrc .bashrc.bak
$ mv .profile .profile.bak
$ mv .bash_login .bash_login.bak
NOTE: Some of these mv command may fail simply because that particular file may not exist.
which will give you access to most of the basic Unix commands. Or you can specify the commands with their full directory names:
$ PATH="/bin:/usr/bin"
$ cd
$ /bin/mv .bash_profile .bash_profile.bak
$ /bin/mv .bashrc .bashrc.bak
$ /bin/mv .profile .profile.bak
$ /bin/mv .bash_login .bash_login.bak
Now, log in again and see what your default $PATH is set to. This is set by the /etc/profile. You might find that's just fine, and remove setting PATH in your startup script.
The standard for PATH is something like this:
/usr/share/bin or /usr/local/bin - These contain non-standard Unix/Linux commands. For example, if you install Maven on your system, the mvn command will usually be located in one of these directories (maybe as a symbolic link). This directory is a place where commands not found in the /bin and /usr/bin directory are stored. This directory is first, so you can replace the version which came with your system with more recent versions. For example, I might have VIM 6.4 installed, but I want to use version 7.3 instead.
/bin:/usr/bin - The standard directories where 99% of the Unix commands live.
$HOME/bin - These are executables you wrote -- either scripts or binaries. This is at the end of the PATH list because it makes sure that you don't accidentally execute the wrong version of the command. Imagine if some joker wrote a shell script called cp that executed /bin/rm instead and placed it in your $HOME/bin directory.
Other directories you'll see may include /sbin/ and /usr/sbin which are administrator commands (ping and ifconfig are sometimes in one of these directories.) /opt/bin/X11 (or wherever the X11 binaries are stored). Sometimes other commands will futz around with your PATH, for example Perlbrew.
#fedorqui's comment provides a quick fix.
The OP could also have used the following to quickly get to a shell with default values for $PATH:
To create a bash shell with a pristine default environment:
without running profile/initialization scripts
without inheriting any environment variables from the current shell
run:
/usr/bin/env -i bash --norc
Note:
Due to use of env's -i option, many environment variables that are normally set will NOT be set in the resulting shell , such as USER, HOME and LANG.
Similarly, the $PATH value you'll get is presumably one hard-coded into bash itself, but it should provide access to at least the standard utilities.
--norc suppresses loading of ~/.bashrc, which normally happens by default for interactive non-login bash shells (bash also supports the --noprofile option to suppress loading of /etc/profile and ~/.bash_profile, but it doesn't apply here, since the shell created is a non-login shell).
If env is in the current shell's $PATH, env -i bash --norc will do.
env is in /usr/bin/ on at least Linux and on FreeBSD/OSX, probably also on other platforms.

When I execute bash, the $PATH keeps repeating itself

I have added entries such as the following in my /etc/bashrc (on Fedora).
#=========== Maven Related variables
export JAVA_HOME='/usr/java/default'
export PATH=${JAVA_HOME}:$PATH
#=========== Maven Related variables
export M2_HOME=/usr/local/apache-maven/apache-maven-3.0.4
export PATH=${M2_HOME}/bin:$PATH
#=========== Ant Related variables
export ANT_HOME=/usr/local/apache-ant
export Path=${ANT_HOME}/bin:$PATH
Now, each time that I execute bash command to refresh the environment variables, all these additions are repeated, and the PATH just keep adding itself recursively; if I keep doing bash for a few dozen times, then the $PATH becomes a hundred lines of repeating content. What am I doing wrong?
Note that I have added these entries to /etc/bashrc since I want to have these values in PATH no matter what user I login as.
Thanks,
Shannon
Don't set your PATH incrementally in .bashrc; set it once in .profile and leave it alone thereafter. Or, since you mention /etc/bashrc, don't set the PATH incrementally in /etc/bashrc; set it once in /etc/profile and leave it alone.
One side-benefit; things will work a little faster.
See also the code in How do I manipulate PATH elements in shell scripts for code to clean up a repetitive PATH.
If by this statement:
... execute bash command to refresh the environment variables ...
you mean that you are entering the command
bash
at the command prompt, you are not "refreshing the environment variables". You are launching a new subshell of the current shell. The new shell inherits the path of the original shell, to which you are once again making additions. Each time you do this the PATH will get longer.
You can use something like:
PATH=$(echo "$PATH" | awk -v RS=: -v ORS=: '!(a[$0]++)' | sed 's/:$//')
to clean up your path after changing it. Also, since the the first match is used when scanning the path, having duplicates doesn't really matter.
I had also faced the same problem (CentOS). This is how I fixed it.
Added the following lines to my user's .profile
export PATH=/usr/local/apache-maven-3.3.3/bin:$PATH
export JAVA_HOME=/usr
export SHELL=/bin/bash
# to run bash (because ksh was my default shell)
/bin/bash
No changes to my user's .bashrc file
No changes to /etc/profile
No changes to /etc/bashrc

Resources