Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

zshrc alias gets executed when launching a new terminal

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 ?

like image 787
nha Avatar asked Mar 09 '16 15:03

nha


People also ask

How do I use alias in Zshrc?

To set up a simple alias, edit the ~/. zshrc file using your text editor and add an alias at the bottom. It is good to keep all your aliases in a single section of the file to avoid confusion and ease of edit. alias ginit="git init ."

How to check if alias is set in zshrc?

Otherwise, try cat ~/.zshrc | grep alias to see all aliases set in your .zshrc to make sure there are no other aliases being set. Thanks for contributing an answer to Unix & Linux Stack Exchange!

Where are zsh aliases stored in Linux?

ZSH aliases are configured in the .zshrc file located in the user’s home directory. They are loaded on shell startup, but you can force-reload them by sourcing the .zshrc file.

What are the different types of aliases available in zsh?

ZSH has four main types of aliases. Simple aliases are a short form of a long command. To set up a simple alias, edit the ~/.zshrc file using your text editor and add an alias at the bottom. It is good to keep all your aliases in a single section of the file to avoid confusion and ease of edit.

What is zshrc in shell script?

The next file is.zshrc that contains the shell configurations and commands. It is sourced in interactive shells and contains aliases, key bindings, variables, and functions. The final file is.zlogout, which gets read when the shell session closes. You can use it to set up commands executed when the shell exits.


1 Answers

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:

  1. Loading the alias takes time
  2. 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 😉.

like image 157
Adaephon Avatar answered Nov 12 '22 05:11

Adaephon