I am using zsh and in my config I am adding another alias :
alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"
However this alias seems to get executed every time I open a new terminal (or at least it slows down starting up a new terminal considerably).
How can I add this alias without it being launched every time I open a new terminal ?
TL;DR (or should it be TL;WR - "too long, won't read"?)
alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
You are at least partially correct in that a part of this command is run when the alias is declared. This has two effects:
Loading the alias takes time
The alias does not work
Explanation
You are declaring the alias in double quotes, which allows for parameter expansion. That means that the parts $(find . -name ".git") and ${dir%/*} will be expanded and substituted at the time the alias is declared taking the values they produce at the time.
For $(find . -name ".git") this means that . is most likely $HOME and the construct is replaced by a newline separated list of all .git directories (and other file-like objects) in your home directory.
${dir%/*} will probably be substituted with an empty string as dir is most likely not set. Remember: the alias itself is not executed at that time, so dir will not be set by the for loop.
This means that:
alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"
will effectively be saved as something like
alias recursively_git_pull_all_repo="for dir in docs/repo1/.git
docs/repo2/.git
otherdocs/repo/.git
whatever/.git; do
cd ; git pull ; cd -; done"
This will - fortunately - fail with an zsh: command not found: docs/repo2/.git error because of the newline after the first repository. If it did not, it would change into your home directory repeatedly (cd without parameter) and try to do git pull.
Solution
The quick solution would be to just use single quotes, when declaring the alias, that way the command and parameter will substituted when the alias is run, not when it is declared.
But there are still some other problems with that:
if any of the directory names contain white spaces, this will fail because for will interpret it as two values
if you cannot switch into one of the found directories, git pull will be run from the current directory instead. This can actually happen if the execute flag is not set on a directory in the path.
without additional parameters find will also list non-directories named .git
Instead I would suggest to use what zsh has to offer:
alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
you do not actually need find to get a list of the directories. The ** glob will be recursively expanded to all directories. So **/.git will be expanded to all paths with .git as last element.
you can use glob qualifiers to restrict expansions to elements that fulfill certain criteria. In this case **/.git(/) the qualifier / means: "only directories".
you can also history expansion modifiers as glob qualifiers. This is done with a : followed by the modifier - in this case h, which removes the last path component (like the program dirname, or ${dir%/*} from your example).
you do not actually need do change directory into a git repository in order to do use git; you can tell git where to work with git -C <path>
as you are now only running one command inside the loop, you can use the short syntax for for, meaning no do ...; done.
Fun fact: If you use d instead of dir, another short form of for and the option GLOB_STAR_SHORT is enabled (requires zsh >= 5.2), you can use:
for d (**.git(/:h))git -C $d pull
which is only 4 characters longer than your alias name. Yes, I know about completion. But there is also history searching 😉.
You could put it in a function instead of an alias:
$ cat zshFunction
function recursively_git_pull_all_repo () {
echo "Executing code"
for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done
}
$ recursively_git_pull_all_repo
zsh: command not found: recursively_git_pull_all_repo
$ source zshFunction
$
$ recursively_git_pull_all_repo
Executing code
$
In my .bashrc file, I have the following lines:
alias cd='_cd'
function cd()
{
cd "$1"
PS1='[$USER] "$PWD" $ '
}
However, after sourcing my .bashrc, every time I try to run the command, I get a process completed message, and I am locked out of the shell.
[prompt] $ source ~/.bashrc
[prompt] $ cd ~
[Process completed]
How can I easily implement this function without getting the process completed message?
Your cd function is recursive, and eventually the shell gets too deep and gives up.
Ensure you're calling the shell's cd inside the function:
cd() {
builtin cd "$1"
PS1='[$USER] "$PWD" $ '
}
You don't have to do this if you define your prompt with: PS1='[\u] "\w" \$ ' -- see the PROMPTING section of your bash man page.
Declaring alias cd='_cd' does not mean you are changing the builtin command cd to _cd. It means you are making an alias of _cd that is invoked when you enter cd. Command expansion follows the order of aliases, functions, builtin and then executables in $PATH. So if there is an alias, function and builtin with the same name, the alias will be executed.
Next it seems you are trying to set your PS1 with a function, while as Jonathan explained it is better to just declare it plain in your .bashrc like
PS1='[$USER] "$PWD" $ '
I would recommend however to use the special characters the prompt recognizes instead of system variables.
$USER is the current user, which in PS1 can represented by \u
$PWD is the working directory, you have the option here to show the full path with \w or just the current with \W.
There are a lot of other useful options, but you should check them out by yourself.
https://www.gnu.org/software/bash/manual/bashref.html#Controlling-the-Prompt
So your prompt may be something like PS1=[\u] \w $
I've got a strange issue while working with a bash script. Here it is:
PWD=${pwd}
# several commands
cd /etc/nginx/sites-enabled/
# more commands
cd $PWD
# I expect that I returning to my directory,
# but $PWD contains current dir - /etc/nginx/sites-enabled/
This behavior is kind of lazy. $PWD stores command, which calculates the current directory and returns it at the moment we call $PWD, but I want to store the string variable in it. How to do that?
PWD is an environmental variable and is changed when you change the directory.
Use a different name for the variable,
eg:
MYPWD=${PWD} #or MYPWD=$(pwd)
cd /etc/nginx/sites-enabled/
cd $MYPWD
Try:
PWD=`pwd`
Or:
PWD=$(pwd)
Both expressions will execute the pwd command and store the command output in the shell variable PWD. There is plenty of discussion on the web about when to use each style. The one point that I recall is that the "$(cmd)" approach allows for nesting of commands, e.g.
CURRENT_BASENAME=$(basename $(pwd))
Edit - It just occurred to me that PWD is a built in shell variable that always expands to the current working directory.
you may also find
cd -
usefull
I have a shell script with the following code:
dir=sample
`mkdir $dir`
`cp /home/bhavya/workspace/UnetStack/logs/log-0.txt $dir/log.txt`
`cd $dir`
In the last line with the cd command in the back quotes, I was not able to cd into the corresponding directory.
But once I removed the back quotes I was able to cd.
What I was wondering is why didn't the cd work with the back quote?
When you ran:
`mkdir $dir`
the shell first ran the command mkdir $dir in a subshell, capturing its (standard) output, and then ran the captured string as a command. Fortunately, the output was empty, so the second step executed nothing.
When you then ran:
`cp /home/bhavya/workspace/UnetStack/logs/log-0.txt $dir/log.txt`
the copy was executed in a subshell, and the output was captured and executed. Again, the output was empty, so the second phase of execution did nothing.
Then you ran:
`cd $dir`
Once more, the cd operation was run in a subshell, which exited after changing its own current working directory, but without affecting the parent shell (this is Unix, not a DOS .bat command file). As before, the output of the cd command was captured, and executed, but the output was empty so there was nothing to execute.
Essentially, you don't use back-quotes as extensively as you are doing.
It would be sufficient to write:
dir=sample
mkdir $dir
cp /home/bhavya/workspace/UnetStack/logs/log-0.txt $dir/log.txt
cd $dir
...other activity in the new directory...
Note that if this is in a script, then the normal ways of executing a script would still leave the parent shell in the original directory. There are ways to make it affect the original shell — find out about the . command (or, in bash, the source command; that's easier to search for).
You normally use back quotes (or, better, the $(...) notation) to capture data. For example:
gcc_lib_dir=$(dirname $(dirname $(which gcc)))/lib
The innermost command is which gcc; it might yield /usr/gcc/v4.7.1/bin/gcc; the inner dirname then yields /usr/gcc/v4.7.1/bin; the outer dirname yields /usr/gcc/v4.7.1; the appended /lib gives
gcc_lib_dir=/usr/gcc/v4.7.1/lib
That also shows why $(...) is superior to the back-quote notation:
gcc_lib_dir=`dirname \`dirname \\\`which gcc\\\`\``/lib
That's harder to get right, and harder to type!
Backquotes run the commands within in a subshell. The subshell changed directory, but there is no way to propagate this back up to the script's shell.
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}