Related
Is it possible to change current directory from a script?
I want to create a utility for directory navigation in Bash. I have created a test script that looks like the following:
#!/bin/bash
cd /home/artemb
When I execute the script from the Bash shell the current directory doesn't change. Is it possible at all to change the current shell directory from a script?
When you start your script, a new process is created that only inherits your environment. When it ends, it ends. Your current environment stays as it is.
Instead, you can start your script like this:
. myscript.sh
The . will evaluate the script in the current environment, so it might be altered
You need to convert your script to a shell function:
#!/bin/bash
#
# this script should not be run directly,
# instead you need to source it from your .bashrc,
# by adding this line:
# . ~/bin/myprog.sh
#
function myprog() {
A=$1
B=$2
echo "aaa ${A} bbb ${B} ccc"
cd /proc
}
The reason is that each process has its own current directory, and when you execute a program from the shell it is run in a new process. The standard "cd", "pushd" and "popd" are builtin to the shell interpreter so that they affect the shell process.
By making your program a shell function, you are adding your own in-process command and then any directory change gets reflected in the shell process.
In light of the unreadability and overcomplication of answers, i believe this is what the requestor should do
add that script to the PATH
run the script as . scriptname
The . (dot) will make sure the script is not run in a child shell.
Putting the above together, you can make an alias
alias your_cmd=". your_cmd"
if you don't want to write the leading "." each time
you want to source your script to the shell environment,
or if you simply don't want to remember that must be done
for the script to work correctly.
If you are using bash you can try alias:
into the .bashrc file add this line:
alias p='cd /home/serdar/my_new_folder/path/'
when you write "p" on the command line, it will change the directory.
If you run a bash script then it will operates on its current environment or on those of its children, never on the parent.
If goal is to run your command :
goto.sh /home/test
Then work interactively in /home/test one way is to run a bash interactive subshell within your script :
#!/bin/bash
cd $1
exec bash
This way you will be in /home/test until you exit ( exit or Ctrl+C ) of this shell.
With pushd the current directory is pushed on the directory stack and it is changed to the given directory, popd get the directory on top of the stack and changes then to it.
pushd ../new/dir > /dev/null
# do something in ../new/dir
popd > /dev/null
Simply go to
yourusername/.bashrc (or yourusername/.bash_profile on MAC) by an editor
and add this code next to the last line:
alias yourcommand="cd /the_path_you_wish"
Then quit editor.
Then type:
source ~/.bashrc or source ~/.bash_profile on MAC.
now you can use: yourcommand in terminal
I've made a script to change directory. take a look: https://github.com/ygpark/dj
Basically we use cd.. to come back from every directory. I thought to make it more easy by giving the number of directories with which you need to come back at a time. You can implement this using a separate script file using the alias command . For example:
code.sh
#!/bin/sh
_backfunc(){
if [ "$1" -eq 1 ]; then
cd ..
elif [ "$1" -eq 2 ]; then
cd ../..
elif [ "$1" -eq 3 ]; then
cd ../../..
elif [ "$1" -eq 4 ]; then
cd ../../../..
elif ["$1" -eq 10]; then
cd /home/arun/Documents/work
fi
}
alias back='_backfunc'
After using source code.sh in the current shell you can use :
$back 2
to come two steps back from the current directory. Explained in detail over here. It is also explained over there how to put the code in ~/.bashrc so that every new shell opened will automatically have this new alias command. You can add new command to go to specific directories by modifying the code by adding more if conditions and different arguments. You can also pull the code from git over here.
Add below cd line in your shellscript this:
exec $SHELL
This is my current way of doing it for bash (tested on Debian). Maybe there's a better way:
Don't do it with exec bash, for example like this:
#!/bin/bash
cd $1
exec bash
because while it appears to work, after
you run it and your script finishes, yes you'll be in the correct
directory, but you'll be in it in a subshell, which you can confirm by
pressing Ctrl+D afterwards, and you'll see it exits the subshell,
putting you back in your original directory.
This is usually not a state you want a script user to be left in after
the script they run returns, because it's non-obvious that they're in
a subshell and now they basically have two shells open when they
thought they only had one. They might continue using this subshell and not realize it, and it could have unintended consequences.
If you really want the script to exit and leave open a subshell in the
new directory, it's better if you change the PS1 variable so the
script user has a visual indicator that they still have a subshell
open.
Here's an example I came up with. It is two files, an outer.sh which you call directly,
and an inner.sh which is sourced inside the outer.sh script. The outer
script sets two variables, then sources the inner script, and
afterwards it echoes the two variables (the second one has just been
modified by the inner script). Afterwards it makes a temp copy of the
current user's ~/.bashrc file, adds an override for the PS1 variable
in it, as well as a cleanup routine, and finally it runs exec bash
--rcfile pointing at the .bashrc.tmp file to initialize bash with a modified environment, including the modified prompt and the cleanup
routine.
After outer.sh exits, you'll be left inside a subshell in the desired directory (in this case testdir/ which was entered into by the inner.sh script) with a visual
indicator making it clear to you, and if you exit out of the subshell,
the .bashrc.tmp file will be deleted by the cleanup routine, and you'll be back in the directory you started in.
Maybe there's a smarter way to do it, but that's the best way I could
figure out in about 40 minutes of experimenting:
file 1: outer.sh
#!/bin/bash
var1="hello"
var2="world"
source inner.sh
echo $var1
echo $var2
cp ~/.bashrc .bashrc.tmp
echo 'export PS1="(subshell) $PS1"' >> .bashrc.tmp
cat <<EOS >> .bashrc.tmp
cleanup() {
echo "cleaning up..."
rm .bashrc.tmp
}
trap 'cleanup' 0
EOS
exec bash --rcfile .bashrc.tmp
file 2: inner.sh
cd testdir
var2="bird"
then run:
$ mkdir testdir
$ chmod 755 outer.sh
$ ./outer.sh
it should output:
hello
bird
and then drop you into your subshell using exec bash, but with a
modified prompt which makes that obvious, something like:
(subshell) user#computername:~/testdir$
and if you Ctrl-D out of the subshell, it should clean up by deleting
a temporary .bashrc.tmp file in the testdir/ directory
I wonder if there's a better way than having to copy the .bashrc file
like that though to change the PS1 var properly in the subshell...
This is a simplified compilation of above answer.
Create a shell file shellfile.sh
In the script change your directory inside a function
#!/bin/bash
cd folder1/folder2/
Now run the script with . before it.
. uses the current thread/session to execute the script.
. shellfile.sh
This approach is easier for me.
Suppose on a personal iMac where you are an admin, under the default directory when a command window is opened, /Users/jdoe, this will be the directory to go to: /Users/jdoe/Desktop/Mongo/db.3.2.1/bin.
These are the steps that can have the job done:
vi mongobin, in which I entered:
cd /Users/jdoe/Desktop/Mongo/db.3.2.1/bin as the first line.
chmod 755 mongobin
source mongobin
pwd
Voila!
I've also created a utility called goat that you can use for easier navigation.
You can view the source code on GitHub.
As of v2.3.1 the usage overview looks like this:
# Create a link (h4xdir) to a directory:
goat h4xdir ~/Documents/dev
# Follow a link to change a directory:
cd h4xdir
# Follow a link (and don't stop there!):
cd h4xdir/awesome-project
# Go up the filesystem tree with '...' (same as `cd ../../`):
cd ...
# List all your links:
goat list
# Delete a link (or more):
goat delete h4xdir lojban
# Delete all the links which point to directories with the given prefix:
goat deleteprefix $HOME/Documents
# Delete all saved links:
goat nuke
# Delete broken links:
goat fix
I like to do the same thing for different projects without firing up a new shell.
In your case:
cd /home/artemb
Save the_script as:
echo cd /home/artemb
Then fire it up with:
\`./the_script\`
Then you get to the directory using the same shell.
Declare your path:
PATH='/home/artemb'
cd ${PATH}
My understanding is that when I execute a script inside a BASH shell using syntax like $ ./myscript.sh a sub-shell is started and the code runs in that shell, with STDOUT and STDERR output being printed on my shell, STDIN is taken from my shell. This is how script are interactive.
The top line of the file "myscript" is #!/bin/bash, this states the interpreter to be used to execute the syntax within the script file.
If I use the syntax source myscript.sh the code in my script is "pulled" in to my current environment and executed there, rather than in a sub-shell. I can't make the following code run in a script that I call with $ ./myscript.sh and effect my current shell:
#!/bin/bash
PS1='`
if [ $? -eq 0 ];
then echo -n "\[\033[00;35m\]\u\[\033[01;32m\]#\[\033[00;35m\]\h\[\033[00;32m\](\[\033[01;35m\]\W\[\033[01;32m\])\[\033[00;32m\]\$";
else echo -n "\[\033[00;35m\]\u\[\033[01;31m\]#\[\033[00;35m\]\h\[\033[01;31m\](\[\033[35m\]\W\[\033[31m\])\[\033[00;31m\]\$";
fi`\[\033[0m\]'
If I drop the #!/bin/bash and use source this script changes my command prompt. Can it be arranged in a script in such a fashion that I can call it with $ ./myscript.sh and it will make the change to my current shell, not the sub shell?
Once you have called a script without source, it is running in a subshell and there is no way to change the environment of the parent shell.
You can source a script with a shebang line. It is simply ignored when sourced.
when I am in a Cygwin terminal, I can easily use the "source" command.
For example, let's say I have a file called "my_aliases.sh", that contains the following
#!/bin/bash -f
alias clear='cmd /c cls'
#unalias clear
Then on the Cygwin terminal, I can type
$source my_aliases.sh
And it just works fine, and whenever I type "clear", I can see that it works.
But I don't know why doing the same thing inside another shell script, and calling that shell script doesn't work.
For example, let's say that I have a file called "run_alias.sh", with the following content:
#!/bin/bash -f
#
a=`source my_aliases.sh`
b=`ls -ltr`
echo $a
echo $b
And when I try to run my file
$ ./run_alias.sh
It just doesn't do anything. For example, I can see that the command (b) takes place, but nothing happens for command (a).
But after I run "run_alias.sh", and type "clear", I get the following error:
$ clear
bash: clear: command not found
I even tried to change run_alias.sh as follows:
#!/bin/bash -f
echo `source my_aliases.sh`
But now when run run_alias.sh, and type clear, I get the exact same error message !!!
Any idea how to call the "source" command from some other shell script in Cygwin?
A child process cannot alter its parent's environment.
When you execute the run_alias.sh script, you launch a new bash process, which sources your alias file. Then the script ends, that bash process terminates and it takes its modified environment with it.
If you want your aliases to be automatically available, source it from your $HOME/.bashrc file.
Backticks create a subshell. The changes made to your environment in that subshell do not affect the calling environment.
Id you want your script (run_alias.sh) to have access to the environment in my_aliases.sh, call source directly.
source my_aliases.sh
b=`ls -lrt`
echo $b
and if you want the changes that run_alias.sh makes to its environment to propagate to it's parent, run source on the command line.
$ source run_alias.sh
I want to write a shell script to execute commands like "export JAVA_HOME=....."
How could I write a script?
I try:
#!/bin/sh
echo "test"
export PATH=$JAVA_HOME/bin:$PATH
export AWS_AUTO_SCALING_HOME=/usr/local/CLI
export PATH=$PATH:$AWS_AUTO_SCALING_HOME/bin
export AWS_CREDENTIAL_FILE=/usr/local/CLI/credential-file-path.template
But the commands are not executed.
But the commands are not executed.
They are executed, but in a sub-shell. The parent shell does not inherit these values.
Instead of executing your script, source it:
source /path/to/myscript.sh
Or
. /path/to/myscript.sh
Further reading: What is the difference between executing a bash script and sourcing a bash script?
How are you executing your script? If you use:
$ script.sh
the environment is set for the duration of the script, but the parent shell is completely unaffected by this (Unix is not DOS!).
To get the results of the commands into your shell, use:
$ . script.sh
or in Bash you can use:
$ source script.sh
(This is a synonym for the . (dot) command, which has been in shells since the Bourne shell. The source command was in C shell first, then added to Bash.)
These read the script into the current process. Any environment variable settings affect the current process. Your profile is effectively read using . $HOME/.profile, for example.
Note that the file for the dotted command is searched for in the directories on $PATH, but the file only needs to be readable, not executable too.
Have you tried setting permission to execute the file??
chmod +x filename
Is it possible to change current directory from a script?
I want to create a utility for directory navigation in Bash. I have created a test script that looks like the following:
#!/bin/bash
cd /home/artemb
When I execute the script from the Bash shell the current directory doesn't change. Is it possible at all to change the current shell directory from a script?
When you start your script, a new process is created that only inherits your environment. When it ends, it ends. Your current environment stays as it is.
Instead, you can start your script like this:
. myscript.sh
The . will evaluate the script in the current environment, so it might be altered
You need to convert your script to a shell function:
#!/bin/bash
#
# this script should not be run directly,
# instead you need to source it from your .bashrc,
# by adding this line:
# . ~/bin/myprog.sh
#
function myprog() {
A=$1
B=$2
echo "aaa ${A} bbb ${B} ccc"
cd /proc
}
The reason is that each process has its own current directory, and when you execute a program from the shell it is run in a new process. The standard "cd", "pushd" and "popd" are builtin to the shell interpreter so that they affect the shell process.
By making your program a shell function, you are adding your own in-process command and then any directory change gets reflected in the shell process.
In light of the unreadability and overcomplication of answers, i believe this is what the requestor should do
add that script to the PATH
run the script as . scriptname
The . (dot) will make sure the script is not run in a child shell.
Putting the above together, you can make an alias
alias your_cmd=". your_cmd"
if you don't want to write the leading "." each time
you want to source your script to the shell environment,
or if you simply don't want to remember that must be done
for the script to work correctly.
If you are using bash you can try alias:
into the .bashrc file add this line:
alias p='cd /home/serdar/my_new_folder/path/'
when you write "p" on the command line, it will change the directory.
If you run a bash script then it will operates on its current environment or on those of its children, never on the parent.
If goal is to run your command :
goto.sh /home/test
Then work interactively in /home/test one way is to run a bash interactive subshell within your script :
#!/bin/bash
cd $1
exec bash
This way you will be in /home/test until you exit ( exit or Ctrl+C ) of this shell.
With pushd the current directory is pushed on the directory stack and it is changed to the given directory, popd get the directory on top of the stack and changes then to it.
pushd ../new/dir > /dev/null
# do something in ../new/dir
popd > /dev/null
Simply go to
yourusername/.bashrc (or yourusername/.bash_profile on MAC) by an editor
and add this code next to the last line:
alias yourcommand="cd /the_path_you_wish"
Then quit editor.
Then type:
source ~/.bashrc or source ~/.bash_profile on MAC.
now you can use: yourcommand in terminal
I've made a script to change directory. take a look: https://github.com/ygpark/dj
Basically we use cd.. to come back from every directory. I thought to make it more easy by giving the number of directories with which you need to come back at a time. You can implement this using a separate script file using the alias command . For example:
code.sh
#!/bin/sh
_backfunc(){
if [ "$1" -eq 1 ]; then
cd ..
elif [ "$1" -eq 2 ]; then
cd ../..
elif [ "$1" -eq 3 ]; then
cd ../../..
elif [ "$1" -eq 4 ]; then
cd ../../../..
elif ["$1" -eq 10]; then
cd /home/arun/Documents/work
fi
}
alias back='_backfunc'
After using source code.sh in the current shell you can use :
$back 2
to come two steps back from the current directory. Explained in detail over here. It is also explained over there how to put the code in ~/.bashrc so that every new shell opened will automatically have this new alias command. You can add new command to go to specific directories by modifying the code by adding more if conditions and different arguments. You can also pull the code from git over here.
Add below cd line in your shellscript this:
exec $SHELL
This is my current way of doing it for bash (tested on Debian). Maybe there's a better way:
Don't do it with exec bash, for example like this:
#!/bin/bash
cd $1
exec bash
because while it appears to work, after
you run it and your script finishes, yes you'll be in the correct
directory, but you'll be in it in a subshell, which you can confirm by
pressing Ctrl+D afterwards, and you'll see it exits the subshell,
putting you back in your original directory.
This is usually not a state you want a script user to be left in after
the script they run returns, because it's non-obvious that they're in
a subshell and now they basically have two shells open when they
thought they only had one. They might continue using this subshell and not realize it, and it could have unintended consequences.
If you really want the script to exit and leave open a subshell in the
new directory, it's better if you change the PS1 variable so the
script user has a visual indicator that they still have a subshell
open.
Here's an example I came up with. It is two files, an outer.sh which you call directly,
and an inner.sh which is sourced inside the outer.sh script. The outer
script sets two variables, then sources the inner script, and
afterwards it echoes the two variables (the second one has just been
modified by the inner script). Afterwards it makes a temp copy of the
current user's ~/.bashrc file, adds an override for the PS1 variable
in it, as well as a cleanup routine, and finally it runs exec bash
--rcfile pointing at the .bashrc.tmp file to initialize bash with a modified environment, including the modified prompt and the cleanup
routine.
After outer.sh exits, you'll be left inside a subshell in the desired directory (in this case testdir/ which was entered into by the inner.sh script) with a visual
indicator making it clear to you, and if you exit out of the subshell,
the .bashrc.tmp file will be deleted by the cleanup routine, and you'll be back in the directory you started in.
Maybe there's a smarter way to do it, but that's the best way I could
figure out in about 40 minutes of experimenting:
file 1: outer.sh
#!/bin/bash
var1="hello"
var2="world"
source inner.sh
echo $var1
echo $var2
cp ~/.bashrc .bashrc.tmp
echo 'export PS1="(subshell) $PS1"' >> .bashrc.tmp
cat <<EOS >> .bashrc.tmp
cleanup() {
echo "cleaning up..."
rm .bashrc.tmp
}
trap 'cleanup' 0
EOS
exec bash --rcfile .bashrc.tmp
file 2: inner.sh
cd testdir
var2="bird"
then run:
$ mkdir testdir
$ chmod 755 outer.sh
$ ./outer.sh
it should output:
hello
bird
and then drop you into your subshell using exec bash, but with a
modified prompt which makes that obvious, something like:
(subshell) user#computername:~/testdir$
and if you Ctrl-D out of the subshell, it should clean up by deleting
a temporary .bashrc.tmp file in the testdir/ directory
I wonder if there's a better way than having to copy the .bashrc file
like that though to change the PS1 var properly in the subshell...
This is a simplified compilation of above answer.
Create a shell file shellfile.sh
In the script change your directory inside a function
#!/bin/bash
cd folder1/folder2/
Now run the script with . before it.
. uses the current thread/session to execute the script.
. shellfile.sh
This approach is easier for me.
Suppose on a personal iMac where you are an admin, under the default directory when a command window is opened, /Users/jdoe, this will be the directory to go to: /Users/jdoe/Desktop/Mongo/db.3.2.1/bin.
These are the steps that can have the job done:
vi mongobin, in which I entered:
cd /Users/jdoe/Desktop/Mongo/db.3.2.1/bin as the first line.
chmod 755 mongobin
source mongobin
pwd
Voila!
I've also created a utility called goat that you can use for easier navigation.
You can view the source code on GitHub.
As of v2.3.1 the usage overview looks like this:
# Create a link (h4xdir) to a directory:
goat h4xdir ~/Documents/dev
# Follow a link to change a directory:
cd h4xdir
# Follow a link (and don't stop there!):
cd h4xdir/awesome-project
# Go up the filesystem tree with '...' (same as `cd ../../`):
cd ...
# List all your links:
goat list
# Delete a link (or more):
goat delete h4xdir lojban
# Delete all the links which point to directories with the given prefix:
goat deleteprefix $HOME/Documents
# Delete all saved links:
goat nuke
# Delete broken links:
goat fix
I like to do the same thing for different projects without firing up a new shell.
In your case:
cd /home/artemb
Save the_script as:
echo cd /home/artemb
Then fire it up with:
\`./the_script\`
Then you get to the directory using the same shell.
Declare your path:
PATH='/home/artemb'
cd ${PATH}