├── README.md ├── docs ├── config │ ├── general.md │ ├── history.md │ ├── hooks.md │ └── prompt.md ├── helpers │ ├── aliases.md │ ├── bindkey.md │ ├── completions.md │ ├── functions.md │ ├── variables.md │ └── widgets.md ├── misc │ └── reference.md └── usage │ ├── file_management.md │ ├── line_movement.md │ └── navigation.md └── img ├── autopair.gif ├── autosuggestions.gif ├── banner.png ├── bash-completion.gif ├── edit-command.gif ├── execute-named-cmd.gif ├── fc-example.gif ├── fzf-history.gif ├── globalias.gif ├── highlighting.gif ├── history-incremental-pattern-search-forward.gif ├── last-command.gif ├── prepend-sudo.gif ├── push-input.gif └── tetris.gif /README.md: -------------------------------------------------------------------------------- 1 | ![](/img/banner.png) 2 | 3 | If you're already using zsh or want to start this tutorial will help you understand how zsh works and how you can customize it to your needs. 4 | 5 | We won't go into every feature of zsh (read the man pages for that). 6 | Did you know zsh has a built in ftp client and calendar? 7 | Instead we will focus on functions of zsh that improve your productivity and help you get more out of your shell. 8 | 9 | We also will not be using any frameworks to customize the shell. 10 | You're free to use [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh), [prezto](https://github.com/sorin-ionescu/prezto), or many other options. They help you get started fast but they don't help you understand zsh and some of its more advanced features. 11 | 12 | We will assume you are already familiar with basic command line usage. 13 | How to run a command, how to set a variable, what command history is, etc. 14 | 15 | This documentation will build on the basics to show you advanced usage, customizations, and practical examples to make you better. 16 | 17 | ## Table of Contents 18 | 19 | * Configuration 20 | * [general](docs/config/general.md) 21 | * [history](docs/config/history.md) 22 | * [prompt](docs/config/prompt.md) 23 | * [hooks](docs/config/hooks.md) 24 | * Helpers 25 | * [aliases](docs/helpers/aliases.md) 26 | * [variables](docs/helpers/variables.md) 27 | * [functions](docs/helpers/functions.md) 28 | * [widgets](docs/helpers/widgets.md) 29 | * [completions](docs/helpers/completions.md) 30 | * Usage 31 | * [line movement](docs/usage/line_movement.md) 32 | * [navigation](docs/usage/navigation.md) 33 | * [file management](docs/usage/file_management.md) 34 | * Misc 35 | * [reference](docs/misc/reference.md) 36 | -------------------------------------------------------------------------------- /docs/config/general.md: -------------------------------------------------------------------------------- 1 | # Config 2 | 3 | Of course your shell needs to be configured to work. 4 | Here's some tips and advanced zsh config settings you might care about. 5 | 6 | If you're looking for config options like [ history ]( history.md ) and [ prompts ]( prompt.md ) please check out those sections directly. 7 | 8 | ## Files 9 | 10 | zsh allows basic configuration via a `.zshrc` file. 11 | There are other startup files but in general you don't need to create or edit them. 12 | 13 | If you want to change the location of your `.zshrc` and `.zhistory` files you can use `$HOME/.zshenv` to set a new path with the `$ZDOTDIR` environment variable. 14 | 15 | ```bash 16 | # $HOME/.zshenv 17 | 18 | export ZDOTDIR=$HOME/.dotfiles/zdotdir 19 | ``` 20 | 21 | 25 | 26 | Something I've found to be successful is to have a `$ZDOTDIR/zsh.d` folder and drop plugins from other plugin managers (e.g. oh-my-zsh, prezto) there. 27 | You can then easily source the files in your `.zshrc` file with something like 28 | 29 | ```bash 30 | for ZSH_FILE in "${ZDOTDIR:-$HOME}"/zsh.d/*.zsh(N); do 31 | source "${ZSH_FILE}" 32 | done 33 | ``` 34 | 35 | The `(N)` at the end of the pattern in the for loop above is called a glob qualifier. 36 | Its purpose is to set the `NULL_GLOB` option, which tells the loop not to error if that location is missing or empty. More information about glob qualifiers can be found in the [docs](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers). 37 | 38 | ## fpath 39 | 40 | zsh's `$fpath` variable is kinda like `$PATH` but for search paths zsh uses. 41 | One of the main things zsh uses it for is shared [functions](../helpers/functions.md). 42 | 43 | > A shared function is different from functions you declare in your `.zshrc` file. 44 | 45 | One major difference with `$fpath` is that it's an array, instead of a string separated with `:`. 46 | Instead of saying `export fpath=$ZDOTDIR/fuctions:$fpath` you need to use array syntax like `export fpath=($ZDOTDIR/functions $fpath)` with a space between the entries. 47 | An even better option is to append to the array with `fpath+=('/some/directory')` so you don't delete existing paths. 48 | 49 | Shared functions are loaded into a shell with the `autoload` command. 50 | 51 | Shared function files in `$fpath` don't need to declare a function name or have a function definition. 52 | You should name the file for the name of the function you want to use. 53 | 54 | ```bash 55 | # Add our own functions folder to fpath 56 | export fpath=($ZDOTDIR/functions $fpath) 57 | 58 | # Create a file called blah in $ZDOTDIR/functions folder 59 | $ echo 'echo blah blah' > ${fpath[1]}/blah 60 | 61 | $ autoload -U blah 62 | 63 | $ blah 64 | blah blah 65 | ``` 66 | 67 | > It's a good idea to put `emulate -L zsh` at the top of your function file to avoid user configuration or parameter expansion. 68 | 69 | There is more information about how files are searched in the [docs](http://zsh.sourceforge.net/Doc/Release/Functions.html). 70 | 71 | `fpath` is really only useful for making functions portable. 72 | You'll probably use some of them (examples in the functions section), but most functions you define can go in your `.zshrc` file. 73 | 74 | --- 75 | 76 | [home](../../README.md) | Next: [history](history.md) 77 | 78 | -------------------------------------------------------------------------------- /docs/config/history.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | If you work in the shell long enough your history will become your own personal reference. 4 | It's like automatic documentation which is the best kind of documentation. 5 | 6 | ## Configuration 7 | 8 | Unfortunately, the default configuration for history is not always great. 9 | You should already be familiar with variables such as `$HISTFILE` and `$HISTSIZE`. 10 | 11 | Here are a few shell options that can improve your history recording and usage. 12 | Drop this config in your `.zshrc` file to improve history recording. 13 | 14 | ``` 15 | setopt EXTENDED_HISTORY # Write the history file in the ':start:elapsed;command' format. 16 | setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits. 17 | setopt SHARE_HISTORY # Share history between all sessions. 18 | setopt HIST_EXPIRE_DUPS_FIRST # Expire a duplicate event first when trimming history. 19 | setopt HIST_IGNORE_DUPS # Do not record an event that was just recorded again. 20 | setopt HIST_IGNORE_ALL_DUPS # Delete an old recorded event if a new event is a duplicate. 21 | setopt HIST_FIND_NO_DUPS # Do not display a previously found event. 22 | setopt HIST_IGNORE_SPACE # Do not record an event starting with a space. 23 | setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file. 24 | setopt HIST_VERIFY # Do not execute immediately upon history expansion. 25 | setopt APPEND_HISTORY # append to history file 26 | setopt HIST_NO_STORE # Don't store history commands 27 | ``` 28 | 29 | ## Using Words from the Last Command 30 | 31 | For most people working with history means using `sudo !!` or occationally `ctrl+r` or `history | grep`. 32 | To master working with your history we should look at a few other commonly used shortcuts instead. 33 | First of all, you should know `!!` is essentially the same as 34 | ```bash 35 | # Note: this doesn't actually work but the fc command is effectively the same 36 | alias !!='fc -ln -1' 37 | ``` 38 | _More on `fc` later_ 39 | 40 | But there are lots of other aliases that start with `!`. 41 | Here are some. 42 | 43 | * `!*` or `!:*` = All **arguments** (minus command) 44 | * `!$` or `!:$` = Last argument/word 45 | * `!^` or `!:1` = First argument (doesn't include command name) 46 | * `!foo` = The last command that started with `foo` 47 | 48 | If you want to use words from commands earlier in your history you can also use the `!-n` format. 49 | 50 | * `!-3:$` = Use last word from 3 commands ago 51 | * `!-2:*` = Use all arguments from 2 commands ago 52 | * `!-2; !-1` = Combine last two commands into a single line 53 | 54 | You can also make this better with `bindkey` and the insert-last-word zle widget. 55 | More on what this means can be found in the [widgets section](../helpers/widgets.md). 56 | 57 | ```bash 58 | # press Alt+. to insert the last word from the previous command 59 | autoload -U smart-insert-last-word 60 | zle -N smart-insert-last-word 61 | bindkey "\e." smart-insert-last-word 62 | ``` 63 | 64 | ## Modifying the Last Command 65 | 66 | It's great to be able to use parts from the last command, but what if you want to rerun the last command with edits? 67 | You have a few options. 68 | 69 | ```bash 70 | echo foo foo 71 | # using the above style !! we can do a single substitution with 72 | !!:s/foo/bzz # bzz foo 73 | # global substitutions need g 74 | echo foo foo 75 | !!:sg/foo/bzz # bzz bzz 76 | # be careful though because global replace happen for the entire line 77 | echo foo foo 78 | !!:sg/o/z # echz: command not found... 79 | ``` 80 | 81 | > There are a ton of other history [modifiers](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Modifiers) if you read through the posix history man page. 82 | > For me they're all hard to remember and I don't use them enough to list them here. 83 | 84 | Sometimes `!!:s/` is hard to type so there's also the shorthand replace with `^` 85 | 86 | ```bash 87 | systemctl status sshd 88 | # replace status with restart 89 | ^status^restart # systemctl restart sshd 90 | # use ^:G for global replace 91 | echo foo foo 92 | ^foo^bzz^:G # bzz bzz 93 | ``` 94 | 95 | If you can't remember any of these formats (I don't blame you) then try `fc`. 96 | `fc` by default will edit your last command in a visual editor. 97 | This is great for long or multi-line commands. 98 | It can also be used to list commands from history and do substitution. 99 | 100 | > Bash `fc` is a little different than zsh's `fc` so be careful which man page you read. 101 | > `apropos fc` to find the specific version and then `man 1p fc`. 102 | 103 | ```bash 104 | echo 'foo 105 | bar' 106 | 107 | # edit last command in a visual editor and execute it 108 | fc 109 | ``` 110 | 111 | ![](../../img/fc-example.gif) 112 | 113 | For global string replacement you can use `string=replace` syntax. 114 | If you want to run the command without using an editor override `$EDITOR` with `-e -`. 115 | 116 | ```bash 117 | echo foo bar baz foo 118 | 119 | fc -e - foo=boo # echo boo bar baz boo 120 | ``` 121 | 122 | This works great as an alias `alias r='fc -e -'` so running `r foo=bar` will replace the string for you! 123 | 124 | ## Searching and Executing History 125 | 126 | We've already discussed using `ctrl+r` and `fc -l` to search through your history. 127 | By default the `history` command in zsh only shows the past 16 entries. 128 | If you want to list all of your history you can use `history 1`. 129 | 130 | Listing your entire history on its own is not very useful. 131 | Most often you want to search for something, to do that we can make a function to handle our searching. 132 | 133 | ```bash 134 | function h() { 135 | # check if we passed any parameters 136 | if [ -z "$*" ]; then 137 | # if no parameters were passed print entire history 138 | history 1 139 | else 140 | # if words were passed use it as a search 141 | history 1 | egrep --color=auto "$@" 142 | fi 143 | } 144 | 145 | # search entire history for "foo" with 146 | h foo 147 | ``` 148 | 149 | This will print a list of commands with numbers. 150 | You can immediately re-execute any of the commands with `!n` where `n` is the command number. 151 | 152 | ```bash 153 | h foo 154 | 100 echo foo 155 | 101 echo foo foo 156 | 102 echo foobar 157 | ``` 158 | 159 | Our command substitution from before works too! 160 | 161 | ```bash 162 | !101:s/foo/bar # echo bar foo 163 | ``` 164 | 165 | Sometimes I want to combine multiple commands from my history into a single line. 166 | We can do that with a function and zle 167 | 168 | ```bash 169 | # add the ability to print >> << for the portion of the cli we'll be using 170 | autoload -Uz narrow-to-region 171 | 172 | # define our function 173 | function _history-incremental-preserving-pattern-search-backward 174 | { 175 | local state 176 | MARK=CURSOR 177 | narrow-to-region -p "$LBUFFER${BUFFER:+>>}" -P "${BUFFER:+<<}$RBUFFER" -S state 178 | zle end-of-history 179 | zle history-incremental-pattern-search-backward 180 | narrow-to-region -R state 181 | } 182 | 183 | # load the function into zle 184 | zle -N _history-incremental-preserving-pattern-search-backward 185 | 186 | # bind it to ctrl+r 187 | bindkey "^R" _history-incremental-preserving-pattern-search-backward 188 | bindkey -M isearch "^R" history-incremental-pattern-search-backward 189 | bindkey "^S" history-incremental-pattern-search-forward 190 | ``` 191 | 192 | If you load the above configuration then `ctrl+r` will still work for normal history searching, but if instead of pushing `` you push `Alt+;` then a `;` will be added to the line and you can push `ctrl+r` again to insert another command. 193 | 194 | ![](../../img/history-incremental-pattern-search-forward.gif) 195 | 196 | Another way to use your history is with interactive searching. 197 | This is most useful when you can remember exactly what command you wanted but remember some of the words. 198 | 199 | To do this [`fzf`](https://github.com/junegunn/fzf) is a great tool. 200 | Install the binary in your `$PATH` and use a function like the one below to search command history. 201 | 202 | ```bash 203 | function fh() { 204 | eval $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed 's/ *[0-9]* *//') 205 | } 206 | ``` 207 | 208 | ![](../../img/fzf-history.gif) 209 | 210 | --- 211 | 212 | Previous: [general](general.md) | [home](../../README.md) | Next: [prompt](prompt.md) 213 | -------------------------------------------------------------------------------- /docs/config/hooks.md: -------------------------------------------------------------------------------- 1 | # Hooks 2 | 3 | [ Hooks ](http://zsh.sourceforge.net/Doc/Release/Functions.html#Hook-Functions) are arrays of functions that happen automatically when events occur in your shell. 4 | The hooks that are frequently used are `chpwd`, `precmd`, `preexec`, and `zshaddhistory`. 5 | 6 | `precmd` is executed before your prompt is displayed and is often used to set values in your [`$PROMPT`](prompt.md). 7 | `preexec` is executed between when you press `enter` on a command prompt but before the command is executed. 8 | 9 | Each hook is an array of [widgets](../helpers/widgets.md) which are executed by zsh. 10 | To display what widgets are in each of your hooks you can use the [zhooks function](https://github.com/agkozak/zhooks). 11 | 12 | ## Set $PROMPT in precmd 13 | 14 | One example of using a hook is by changing your `$PROMPT` using `precmd` hook. 15 | `precmd` runs before each prompt which makes it a perfect candidate for setting your `$PROMPT`. 16 | 17 | If you want to create a custom function that sets your `$RPROMPT` variable to your last command exit code you can use this. 18 | 19 | ```bash 20 | # allows parameter expansion, arithmatic, and shell substitution in prompts 21 | setopt prompt_subst 22 | 23 | function check_last_exit_code() { 24 | local LAST_EXIT_CODE=$? 25 | if [[ $LAST_EXIT_CODE -ne 0 ]]; then 26 | local EXIT_CODE_PROMPT=' ' 27 | EXIT_CODE_PROMPT+="%{$fg[red]%}-%{$reset_color%}" 28 | EXIT_CODE_PROMPT+="%{$fg_bold[red]%}$LAST_EXIT_CODE%{$reset_color%}" 29 | EXIT_CODE_PROMPT+="%{$fg[red]%}-%{$reset_color%}" 30 | RPROMPT="$EXIT_CODE_PROMPT" 31 | else 32 | RPROMPT=' ' 33 | fi 34 | } 35 | 36 | typeset -a precmd_functions 37 | # append the function to our array of precmd functions 38 | precmd_functions+=(check_last_exit_code) 39 | ``` 40 | This will run the `precmd_functions` each time you execute a command and get a new prompt. 41 | Notice how we set `RPROMPT` in the function 42 | 43 | Likewise you can also use the function directly to set `RPROMPT` via a subshell but you'll need to make sure your function `echo`s the text instead of setting the variable directly. 44 | 45 | ```bash 46 | setopt prompt_subst 47 | 48 | function check_last_exit_code() { 49 | local LAST_EXIT_CODE=$? 50 | if [[ $LAST_EXIT_CODE -ne 0 ]]; then 51 | local EXIT_CODE_PROMPT=' ' 52 | EXIT_CODE_PROMPT+="%{$fg[red]%}-%{$reset_color%}" 53 | EXIT_CODE_PROMPT+="%{$fg_bold[red]%}$LAST_EXIT_CODE%{$reset_color%}" 54 | EXIT_CODE_PROMPT+="%{$fg[red]%}-%{$reset_color%}" 55 | echo "$EXIT_CODE_PROMPT" 56 | fi 57 | } 58 | 59 | # set RPROMT directly. Will override other functions where it gets set 60 | RPROMPT='$(check_last_exit_code)' 61 | ``` 62 | 63 | ## Add Function to Hook 64 | 65 | If you want to extend your hooks you can easily do it with the [`add-zsh-hook`](https://github.com/zsh-users/zsh/blob/master/Functions/Misc/add-zsh-hook) which should be available in most zsh packages. 66 | 67 | ```bash 68 | # create a do-ls function 69 | # Make sure to use emulate -L zsh or 70 | # your shell settings and a directory 71 | # named 'rm' could be deadly 72 | do-ls() {emulate -L zsh; ls;} 73 | 74 | # add do-ls to chpwd hook 75 | add-zsh-hook chpwd do-ls 76 | 77 | cd ~/ 78 | Desktop Music 79 | Documents Pictures 80 | Downloads Public 81 | ``` 82 | 83 | You may need to play with which hook to use and your function definition to get it just right. 84 | Many prompt themes will automatically install hooks for you. 85 | 86 | ## Adding Custom Hooks 87 | 88 | Sometimes it would be helpful if a widget was a hook. 89 | This would allow the widget to run multiple functions when invoked. 90 | 91 | You could redefine widgets like zle-line-init and zle-keymap-select with user widgets but that makes it hard to combine smaller functions. 92 | 93 | Instead, you can create a custom hook and redefine the widget to invoke your hook. 94 | Then you can add/remove functions to the hook as needed. 95 | 96 | For that you can use [`hooks-define-hook`](https://github.com/willghatch/zsh-hooks) and `hooks-add-hook` from the same plugin. 97 | 98 | --- 99 | 100 | Previous: [prompt](prompt.md) | [home](../../README.md) | Next: [aliases](../helpers/aliases.md) 101 | -------------------------------------------------------------------------------- /docs/config/prompt.md: -------------------------------------------------------------------------------- 1 | # Prompt 2 | 3 | Your terminal prompt is your best guide to passive productivity gains. 4 | It's also a very opinionated part of any shell. 5 | Some people like multiple lines. 6 | Some people love minimal prompts without distractions. 7 | 8 | Whatever you prefer, you should probably start with an existing prompt and customize it rather than writing one from scratch. 9 | 10 | One setting you probably want is `setopt PROMPT_SUBST` which will let you expand parameters and substitute commands in your prompt. 11 | 12 | ## $PROMPT 13 | 14 | zsh can use the `$PS1` variable like bash for your prompt but you should use `$PROMPT` instead. 15 | In most cases they should have the same value. 16 | 17 | You can export it just like any variable and your current prompt should change. 18 | Be careful you don't have a function in your `precmd` hook that overrides this variable. 19 | 20 | The zsh prompt expansion uses cryptic variables like `%n` and `%#` which you should never try to memorize. 21 | It's a waste of brain resource and you're better off looking them up if you want to use them to extend a prompt theme. 22 | 23 | Here's a link to the available variables and what they do—[Prompt Expansion](http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html). 24 | The prompt can also handle special colors with things like `%{orange}`, `%{limegreen}`, and `${PR_RST}`. 25 | Seriously, why are you still reading this section? 26 | You shouldn't be creating a prompt from scratch. 27 | 28 | Look below to use a pre-made prompt theme. 29 | 30 | ## $RPROMPT 31 | 32 | One of the cool features about zsh is the ability to have a "right prompt". 33 | Export `$RPROMPT` or `$RPS1` to start using it. 34 | No one ever said you could only have information on the left side of your terminal. 35 | 36 | It works the same way as `$PROMPT` but it's right aligned. 37 | 38 | Export it using one of those archaic variables and give it a try. 39 | 40 | ```bash 41 | # Show time on the right side of the screen 42 | $ export RPROMPT='%t' 43 | 44 | # show git repo info in your prompt 45 | export RPROMPT='$vcs_info_msg_0_' 46 | ``` 47 | 48 | If you don't like these there are a million other options. 49 | Search your favorite search engine to find something to your liking. 50 | 51 | > Some prompt themes will export `$PROMPT` and `$RPROMPT` 52 | 53 | ## `prompt` command 54 | 55 | The `prompt` command is provided as a [standard zsh widget](https://github.com/zsh-users/zsh/blob/master/Functions/Prompts/promptinit) 56 | You can load it into your shell with 57 | 58 | ```bash 59 | autoload -Uz promptinit; promptinit 60 | ``` 61 | 62 | Now you can list installed prompts with `prompt -p`. 63 | This will give you the theme name and an example of what it looks like rendered. 64 | 65 | Select the theme you want with `prompt $THEME`. 66 | 67 | If you want to save the prompt theme you should add that command to your `.zshrc`. 68 | Make sure you also `autoload` the widget in the config before using `prompt`. 69 | 70 | ### Install custom theme 71 | 72 | If you want to install a custom zsh theme that the `prompt` command can use you need to add a `prompt_*_setup` file to your `$fpath`. 73 | The file can be any standard zsh script and can use existing variables and functions available in the environment. 74 | 75 | On Linux the standard prompts are available in `/usr/share/zsh`. 76 | You should be able to find a prompt setup example in there, but you probably want to keep your own prompts somewhere in your home directory. 77 | 78 | ```bash 79 | fpath=($ZDOTDIR/prompts $fpath) 80 | 81 | autoload -Uz promptinit; promptinit 82 | prompt MyAwesomePrompt # Looks for $ZDOTDIR/promts/prompt_MyAwesomePrompt_setup 83 | ``` 84 | 85 | Prompt setup files can export both `$PROMPT` and `$RPROMPT` if you want them to. 86 | 87 | ## Creating your own theme 88 | 89 | If you refused to listen to all my other advice you can create your own theme. 90 | I still recommend you start from one of the many theme examples available by default, in a zsh manager like [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh/tree/master/themes), or standalone prompt projects like [ spaceship ]( https://github.com/denysdovhan/spaceship-prompt ) or [alien](https://github.com/eendroroy/alien). 91 | 92 | Once you create a prompt you like put it in a `prompt_MyPrompt_setup` file and put it in a folder in your `$fpath`. 93 | Follow the instructions above to load the prompt in your `.zshrc` file. 94 | 95 | --- 96 | 97 | Previous: [history](history.md) | [home](../../README.md) | Next: [hooks](hooks.md) 98 | -------------------------------------------------------------------------------- /docs/helpers/aliases.md: -------------------------------------------------------------------------------- 1 | # Aliases 2 | 3 | A good place to start is to list all of your shell aliases with the `alias` command. 4 | When passed no parameters it will list the current aliases as defined in your configuration or explicitly defined in the shell. 5 | 6 | ## Defining Aliases 7 | 8 | Aliases in zsh share the common `alias` builtin command from other shells. 9 | Aliases are handy for saving time when typing commands. 10 | If they are not "global aliases" then they only work at the beginning of a command prompt (more below). 11 | Typical aliases are used for commands such as: 12 | 13 | ```bash 14 | alias g=git 15 | ``` 16 | 17 | Aliases can be single commands or expand to multiple words. 18 | 19 | ```bash 20 | alias gc='git commit' 21 | ``` 22 | 23 | If you define your alias with a space after the definition it will allow the following word to be interpreted as an alias. 24 | 25 | ```bash 26 | # notice the space inside the quotes 27 | alias ss='sudo ' 28 | ``` 29 | 30 | This will allow you to use an alias after `ss`. 31 | 32 | ```bash 33 | # Without the space you would receive an error that the `g` command is not found 34 | ss g 35 | ^^ runs `sudo git` 36 | ``` 37 | 38 | If an alias conflicts with the name of a command you can escape it with quotes `''`. 39 | 40 | ```bash 41 | alias grep='grep --color=auto' 42 | 43 | echo foo | grep fo 44 | # ^^^^ uses grep --color=auto alias 45 | 46 | echo foo | 'grep' fo 47 | # ^^^^ will not colorize match 48 | ``` 49 | 50 | Aliases don't have to only be letters and numbers. 51 | 52 | ```bash 53 | alias :q=exit 54 | alias ..='cd ..' 55 | alias ....='cd ../..' 56 | alias --='cd -' 57 | ``` 58 | 59 | > You can also alias escape special characters such as `\` so be careful 60 | 61 | ## Global Aliases 62 | 63 | zsh has an additional option for global aliases with the `-g` option which allows aliases at any part of the command line. 64 | Without the `-g` option aliases will only be matched at the beginning of a line or after an alias ending with a space. 65 | 66 | Global aliases can be helpful for aliasing any text you frequently write at the command line. 67 | ```bash 68 | # pipe output to grep 69 | alias -g G='| grep' 70 | # pipe output to less 71 | alias -g L='| less' 72 | # pipe output to `wc` with option `-l` 73 | alias -g W='| wc -l' 74 | # convert multiline output to single line and copy it to the system clipboard 75 | alias -g C='| tr -d ''\n'' | xclip -selection clipboard' 76 | ``` 77 | 78 | Because global aliases can be used anywhere in the command line they can also be chained together. 79 | ```bash 80 | echo -e 'zsh\nis\n\great\nhello\ngoodbye' G -A1 hello C 81 | # grep for hello and include 1 line after ^^^^^^^^^ ^ 82 | | 83 | # trim new lines and copy 'hello goodbye' to the ---| 84 | # system clipboard 85 | ``` 86 | 87 | Aliases can also be used inside definitions of other aliases! 88 | ```bash 89 | alias -g G='| grep' 90 | alias -g W='| wc -l' 91 | alias -g GfooW='G foo W' 92 | ``` 93 | 94 | ## Automatically Expand Aliases 95 | 96 | Aliases are great for typing but sometimes they can be confusing to remember what aliases you have defined or when searching your command [history](../config/history.md). 97 | Trying to remember if you ran a command with `git` or `g` can make it hard to find the command you need. 98 | 99 | It's also helpful to show full commands if you are pair programming or giving a presentation. 100 | 101 | For that you can use a function that will automatically exand your aliases on your current command line after you press space. 102 | 103 | ```bash 104 | # don't worry about zle. We'll go over it later in the zle section. 105 | globalias() { 106 | zle _expand_alias 107 | zle expand-word 108 | zle self-insert 109 | } 110 | zle -N globalias 111 | 112 | # space expands all aliases, including global 113 | bindkey -M emacs " " globalias 114 | bindkey -M viins " " globalias 115 | 116 | # control-space to make a normal space 117 | bindkey -M emacs "^ " magic-space 118 | bindkey -M viins "^ " magic-space 119 | 120 | # normal space during searches 121 | bindkey -M isearch " " magic-space 122 | ``` 123 | 124 | ![](../../img/globalias.gif) 125 | 126 | ## Advanced Aliases 127 | 128 | Because aliases expand to any text they can get quite complex. 129 | The expanded text can include subshells, variables, and additional quoted text. 130 | 131 | > Advanced aliases can also be implemented as shell functions or widgets. 132 | 133 | Here are a few examples 134 | ```bash 135 | # Search through your command history and print the top 10 commands 136 | alias history-stat='history 0 | awk ''{print $2}'' | sort | uniq -c | sort -n -r | head' 137 | 138 | # Use `which` to find aliases and functions including binaries 139 | which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot' 140 | 141 | ``` 142 | 143 | # Conclusion 144 | 145 | Aliases are great for replacing text at an interactive shell. 146 | For more options on powering up your interactive shell check out [functions](functions.md) and [widgets](widgets.md). 147 | 148 | --- 149 | 150 | Previous: [hooks](hooks.md) | [home](../../README.md) | Next: [variables](variables.md) 151 | -------------------------------------------------------------------------------- /docs/helpers/bindkey.md: -------------------------------------------------------------------------------- 1 | # Bindkey 2 | 3 | The `bindkey` key mappings can be very confusing to decipher. 4 | It can use multiple different notations but it's smart to use the same key notation throughout your configuration. 5 | 6 | You can print all of your current key bindings in the current keymap with `bindkey`. 7 | To print the full `bindkey` command to add to your `.zshrc` file use `bindkey -L`. 8 | 9 | In general you'll bind a widget so a key sequence or a key with modifier. 10 | This can be declared in [caret notation](https://en.wikipedia.org/wiki/Caret_notation) using `^`, using [escape sequences](https://en.wikipedia.org/wiki/Escape_sequence) using `\`, in octal (`\NNN`), hex (`\xNN`), or unicode (`\uNNNN`). 11 | None of these are particularly great for people to read. 12 | 13 | This is also tricky because it depends on your keyboard, operating system, and shell. 14 | Here are some basics 15 | 16 | * `\e`, `\E`, = Escape 17 | * `^[` = Alt key (on some keyboards this is the same as escape) 18 | * `^?` = Delete 19 | * `^X`, `^` = Control 20 | 21 | The keys that come after the modifier can add more confusion. 22 | 23 | ## Delete key binding 24 | 25 | To delete a key binding you can use `bindkey -d $KEYS`. 26 | Make sure you don't delete characters you need for typing. 27 | 28 | I have got myself in trouble more than once with `bindkey -d ' '`. 29 | As a thought experiement, how would you remedy the pickle you get yourself in if you do that? 30 | -------------------------------------------------------------------------------- /docs/helpers/completions.md: -------------------------------------------------------------------------------- 1 | # Completions 2 | 3 | zsh has very comprehensive completions which is one of the main reasons to switch to zsh for your interactive shell. 4 | Under the covers it works similarly to other shell completion engines (completions are read from files and functions on disc) but there are a few features that make it better. 5 | 6 | > Some of the completion features have been ported to other shells and some have been stolen from other shells. 7 | > Many of the sections below are optional to enhance your shell experience. 8 | 9 | One nice difference is the way completions are displayed and how tab works when at a completion menu. 10 | Here is a comparison with how `bash` and `zsh` differ. 11 | 12 | ![](../../img/bash-completion.gif) 13 | 14 | There are lots of commands that have built in tab completion for zsh. 15 | If a command you use doesn't support tab completion by default have a look at [zsh-users/zsh-completions](https://github.com/zsh-users/zsh-completions). 16 | You can install it with the various zsh plugin managers or download the repo and add the src directory to your `$fpath`. 17 | 18 | ## Autosuggestions 19 | 20 | Autosuggestions are like a preemptive tab complete for your .zhistory file. 21 | The autosuggestion plugin will look at your current command, what commands you typically type, and suggest possibilities for you to use and save key strokes. 22 | 23 | Installation instructions can be found in the [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) repo. 24 | 25 | ![](../../img/autosuggestions.gif) 26 | 27 | ## Highlighting 28 | 29 | Syntax highlighting works similarly to an IDE but at your terminal. 30 | It can highlight everything from brackets to cursor position. 31 | 32 | Under the covers it works like all the other things in zsh with zle widgets. 33 | Have a look at the [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) repo for installation instructions 34 | 35 | ![](../../img/highlighting.gif) 36 | 37 | ## zstyle 38 | 39 | List your current styles by invoking `zstyle` by itself. 40 | 41 | The rest of this section will have to come at a later date. 42 | 43 | --- 44 | 45 | Previous: [widgets](widgets.md) | [home](../../README.md) | Next: [line movement](../usage/line_movement.md) 46 | -------------------------------------------------------------------------------- /docs/helpers/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | User defined functions allow you to take snippets of code and reuse them. 4 | This is similar to any programming language and has the following benefits over aliases. 5 | 6 | Functions can: 7 | 8 | * accept parameters (e.g. `$1`, `$2`...) 9 | * have control logic (e.g. `case` and `if`) 10 | * spawn subshells 11 | * have error codes and traps 12 | 13 | Functions are a building block for a lot of zsh. 14 | Two major aspects of zsh are [widgets](widgets.md) and [hooks](../config/hooks.md) which are both uses of functions. 15 | 16 | We'll only show some basics and examples here because it's assumed you know how create shell functions. 17 | 18 | ## Basics 19 | 20 | Hopefully you know the dozen different ways to declare a function. 21 | 22 | ```bash 23 | # declare ffoo function 24 | function ffoo() { echo foo; } 25 | 26 | # declare fbar function 27 | fbar() { echo bar; } 28 | 29 | # Anonymous function 30 | () { echo baz; } 31 | ``` 32 | 33 | Nothing goes between the `()` and nothing is returned in traditional function definitions. 34 | You can set a global variable from within the function or `echo` a value in the function and use a subshell to store its value. 35 | 36 | ```bash 37 | function to-lower() { 38 | echo ${1:l} 39 | } 40 | 41 | var=$(to-lower FOO) 42 | 43 | echo $var 44 | foo 45 | ``` 46 | 47 | ## Print a Function 48 | 49 | Sometimes you can't remember what's in a function or where it's sourced from. 50 | To print a shell function to stdout you can use `type -f` or `whence -f`. 51 | Both are zsh builtin commands. 52 | 53 | ```bash 54 | echo 'do-ls() { emulate -L zsh; \ls; }' > do-ls 55 | 56 | source ./do-ls 57 | 58 | whence -f do-ls 59 | do-ls () { 60 | emulate -L zsh 61 | \ls 62 | } 63 | ``` 64 | 65 | It's also handy to know where a function comes from. 66 | Sometimes you can't remember what file it was sourced in if you need to edit it. 67 | 68 | To print the file it came from use `whence -v` 69 | 70 | ``` 71 | whence -v do-ls 72 | do-ls is a shell function from ./do-ls 73 | 74 | # if you declare the function directly you won't 75 | # see the path with -v 76 | do-ls() { emulate -L zsh; \ls; } 77 | 78 | whence -v do-ls 79 | do-ls is a shell function 80 | ``` 81 | 82 | ## Examples 83 | 84 | Here are some examples of functions I have found fun or handy. 85 | 86 | > Many of these functions require external programs like `fzf`. 87 | > Make sure you have them installed in your `$PATH` if you want to use them. 88 | 89 | ### Show website certificate from hostname 90 | 91 | ```bash 92 | function curl-cert() { 93 | openssl s_client -showcerts -connect "${1}":443 -servername ${1} 94 | } 95 | ``` 96 | 97 | ### Interactively export AWS_PROFILE 98 | 99 | ```bash 100 | function awsp() { 101 | export AWS_PROFILE=$(grep profile ${HOME}/.aws/config \ 102 | | awk '{print $2}' | sed 's,],,g' \ 103 | | fzf --layout reverse --height=10% --border) 104 | } 105 | ``` 106 | 107 | ### Interactive man search 108 | 109 | ```bash 110 | function mans(){ 111 | man -k . \ 112 | | fzf -n1,2 --preview "echo {} \ 113 | | cut -d' ' -f1 \ 114 | | sed 's# (#.#' \ 115 | | sed 's#)##' \ 116 | | xargs -I% man %" --bind "enter:execute: \ 117 | (echo {} \ 118 | | cut -d' ' -f1 \ 119 | | sed 's# (#.#' \ 120 | | sed 's#)##' \ 121 | | xargs -I% man % \ 122 | | less -R)" 123 | } 124 | ``` 125 | 126 | ### Interactive git diff 127 | 128 | ```bash 129 | function fshow() { 130 | git log --graph --color=always \ 131 | --format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" \ 132 | | fzf --ansi --preview "echo {} \ 133 | | grep -o '[a-f0-9]\{7\}' \ 134 | | head -1 \ 135 | | xargs -I % sh -c 'git show --color=always %'" \ 136 | --bind "enter:execute: 137 | (grep -o '[a-f0-9]\{7\}' \ 138 | | head -1 \ 139 | | xargs -I % sh -c 'git show --color=always % \ 140 | | less -R') << 'FZF-EOF' 141 | {} 142 | FZF-EOF" 143 | } 144 | ``` 145 | 146 | ### The most important functions 147 | 148 | ```bash 149 | disappointed() { echo -n " ಠ_ಠ " |tee /dev/tty| xclip -selection clipboard; } 150 | 151 | flip() { echo -n "(╯°□°)╯ ┻━┻" |tee /dev/tty| xclip -selection clipboard; } 152 | 153 | shrug() { echo -n "¯\_(ツ)_/¯" |tee /dev/tty| xclip -selection clipboard; } 154 | 155 | matrix() { echo -e "\e[1;40m" ; clear ; while :; do echo $LINES $COLUMNS $(( $RANDOM % $COLUMNS)) $(( $RANDOM % 72 )) ;sleep 0.05; done|awk '{ letters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()"; c=$4; letter=substr(letters,c,1);a[$3]=0;for (x in a) {o=a[x];a[x]=a[x]+1; printf "\033[%s;%sH\033[2;32m%s",o,x,letter; printf "\033[%s;%sH\033[1;37m%s\033[0;0H",a[x],x,letter;if (a[x] >= $1) { a[x]=0; } }}' } 156 | ``` 157 | 158 | --- 159 | 160 | Previous: [variables](variables.md) | [home](../../README.md) | Next: [widgets](widgets.md) 161 | -------------------------------------------------------------------------------- /docs/helpers/variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | If you've used a shell for any amount of time you know what variables are. 4 | zsh uses `$` to denote a variable just like other shells. 5 | 6 | Variables are a subset of shell [parameters](http://zsh.sourceforge.net/Doc/Release/Parameters.html) which have lots of different values and types. 7 | You probably mostly care about variables when writing functions or scripts to handle positional parameters or arguments. 8 | 9 | Some notable special parameters are: 10 | 11 | * `!` = The pid of the last backgrounded command (e.g. `echo $!`) 12 | * `$` = The pid of this shell (e.g. `cat /proc/$$/cmdline`) 13 | * `?` = The exit status returned by the last command (e.g. `echo $?`) 14 | * `_` = The last argument to the previous command (e.g. `echo $_`) 15 | * `$RANDOM` = Pseudo-random integer from 0 to 32767 16 | * `$PWD` = Same output as `pwd` 17 | * `$n` = Positional parameter passed to script (e.g. `echo $1`) 18 | 19 | > There are a lot of odd behaviors to variables in any shell. 20 | > You shouldn't try to memorize all of them and we won't cover them here. 21 | 22 | ## Parameter Substitution 23 | 24 | Variable substitution in zsh works the same as most other shells and has been covered many other places. 25 | There are lots of options for how and what you need to substitute inside a variable so I won't go over most of it here. 26 | 27 | Variable substitute uses `${variable action value}` syntax where a punctuation mark denotes an action and a string, variable, or command for value. 28 | Things like this are completely valid syntax 29 | 30 | ```bash 31 | # echo YYYY-MM-DD if $foo and $datestring are not set or an empty string 32 | ${foo:-$(date +${datestring-"%Y-%m-%d"})} 33 | 34 | # if foo not declared use the value of $bar and upper case it 35 | ${${foo-$bar}:u}`. 36 | ``` 37 | 38 | This can be very powerful and also very confusing to read, use it sparingly. 39 | Check out `man zshexpn` for more information. 40 | 41 | The substitutions I generally find useful are: 42 | 43 | * `${var:-foo}` = substitute var with foo if it is unset or set to an empty string 44 | * `${var:s/foo/bar}` = replace foo for bar (same as `${var/foo/bar}` in bash) 45 | * `${var:h}` = leave "head" of variable path (same as `dirname ${var}` or `${str%/*}` in bash) 46 | * `${var:t}` = leave "tail" of variable path (same as `basename` or `${str##*/}` in bash) 47 | * `${var:l}` = convert variable to lowercase (this is `${var,,}` in bash) 48 | * `${var:u}` = convert variable to UPPERCASE (this is `${var^^}` in bash) 49 | 50 | One neat thing about these is they can be combined. 51 | Say you want to rename all file extensions in the current directory to lower case. 52 | You can do 53 | 54 | ```bash 55 | for FILE in $(ls -1); do 56 | # :r takes filename and removes extension 57 | # :e takes extension without filename 58 | # :l lowercases text 59 | mv ${FILE} ${FILE:r}.${FILE:e:l} 60 | done 61 | ``` 62 | 63 | As I said before, use these sparingly. 64 | In general I still write all of my scripts in bash so I only use these substitutions in functions declared in `.zshrc` or at an interactive shell. 65 | 66 | --- 67 | 68 | Previous: [aliases](aliases.md) | [home](../../README.md) | Next: [functions](functions.md) 69 | -------------------------------------------------------------------------------- /docs/helpers/widgets.md: -------------------------------------------------------------------------------- 1 | # Widgets 2 | 3 | Widgets are how zsh performs actions in your terminal. 4 | Everything from moving the cursor to command completion to executing commands. 5 | 6 | There are built in widgets for a majority of shell functionality and you can have user defined widgets to add your own functionality. 7 | User widgets are used by binding them to keyboard shortcuts using the `bindkey` command. 8 | More about defining widgets can be found in the zle section. 9 | 10 | Widgets have descriptive names such as `vi-backward-kill-word` and `beginning-of-history`. 11 | User defined widgets have no standard naming convention but you should stick with something meaningful for troubleshooting if things go wrong. 12 | 13 | You can list all available widgets with 14 | ```bash 15 | zle -al 16 | ``` 17 | 18 | If you want to list your current keybindings and widgets use the `bindkey` command by itself. 19 | To see a list of bindings and a command you can run to set the key binding in your `.zshrc` file use `bindkey -L` 20 | 21 | The widgets with `.` at the begining of the name are read-only system widgets that cannot be changed. 22 | 23 | An example of binding a built in widget to a keyboard shortcut is 24 | 25 | > When you bind a key to a widget any previous key binding will be overridden. 26 | 27 | ```bash 28 | bindkey '^a' begining-of-line 29 | ``` 30 | 31 | This binds `ctrl+a` to jump to the begining of the current line at a prompt. 32 | For a map of bindkey key representations have a look at [this table](bindkey.md) 33 | 34 | > `bindkey` works for typing strings at the terminal too! 35 | > Try `bindkey -s '^[s' '^Qsudo '` to enter sudo when your cursor is at a new command prompt. 36 | 37 | A majority of widgets are included by default in zsh but additional widgets are available to load from modules. 38 | User widgets will also need to use `autoload -U $WIDGET_NAME` and `zle -N $WIDGET_NAME` to be available for `bindkey`. 39 | 40 | Widgets are generally broken up into different categories. 41 | This includes 42 | * Movement 43 | * History Control 44 | * Modifying Text 45 | * Arguments 46 | * Completion 47 | * Miscellaneous 48 | * Text Objects 49 | 50 | We already have sections in this workshop for [ line movement ]( ../usage/line_movement.md ) and [ history ]( ../config/history.md ) so in this section we'll look at some widgets in other categories. 51 | 52 | ## User Widgets 53 | 54 | Some user widgets will be included by your system in your `$fpath` and others you can declare yourself as functions in `.zshrc`. 55 | You've already seen examples of using widgets in your `$fpath` in the history section. 56 | 57 | The basic commands are: 58 | 59 | ```bash 60 | autoload -U $WIDGET 61 | zle -N $WIDGET 62 | bindkey $KEY_SEQUENCE $WIDGET 63 | ``` 64 | 65 | `autoload -U` (`-U` is for user) will find a file in your `$fpath` named `$WIDGET` and makes it available to `zle`. 66 | `zle -N` (`-N` is for new) will make the widget available to the command line. 67 | 68 | `bindkey` also uses keymaps for different keyboard shortcuts but we won't worry about that here. 69 | 70 | ## Test widgets 71 | 72 | You can also test a widget interactively at a command line. 73 | Of course there's a widget `execute-named-cmd` for that. 74 | 75 | To invoke a widget first find which one you want to try with `zle -la` and then enter some text on a command prompt. 76 | 77 | Now either execute `alt+x` in emacs mode or `:` in vimcmd mode. 78 | Type the name of the widget you want to test and press enter. 79 | 80 | ![](../../img/execute-named-cmd.gif) 81 | 82 | ## Examples 83 | 84 | You might be thinking, what's the big deal with widgets? 85 | They're just functions with keyboard mappings. 86 | 87 | The best way to show their power is with some examples. 88 | 89 | > Most of these examples use vi keymaps. 90 | > Modify them as you wish for other keymaps. 91 | 92 | ### Prepend `sudo` to a command and put your cursor back to the previous location with `esc,s` 93 | 94 | ![](../../img/prepend-sudo.gif) 95 | 96 | ```bash 97 | function prepend-sudo { 98 | if [[ $BUFFER != "sudo "* ]]; then 99 | BUFFER="sudo $BUFFER"; CURSOR+=5 100 | fi 101 | } 102 | zle -N prepend-sudo 103 | 104 | bindkey -M vicmd s prepend-sudo 105 | ``` 106 | 107 | ### Get output from last command with `ctrl+q,ctrl+l` 108 | 109 | ![](../../img/last-command.gif) 110 | 111 | ```bash 112 | zmodload -i zsh/parameter 113 | 114 | insert-last-command-output() { 115 | LBUFFER+="$(eval $history[$((HISTCMD-1))])" 116 | } 117 | zle -N insert-last-command-output 118 | 119 | bindkey "^Q^L" insert-last-command-output 120 | ``` 121 | 122 | ### Edit the current command in your `$EDITOR` with `esc,v` 123 | 124 | ![](../../img/edit-command.gif) 125 | 126 | ```bash 127 | autoload -U edit-command-line 128 | zle -N edit-command-line 129 | bindkey -M vicmd v edit-command-line 130 | ``` 131 | 132 | ### Tetris 133 | 134 | Of course there are some less useful widgets available too. 135 | 136 | ```bash 137 | autoload -Uz tetris 138 | zle -N tetris 139 | bindkey '^T' tetris 140 | ``` 141 | 142 | ![](../../img/tetris.gif) 143 | 144 | If you want to get really fancy you can check out [`tetriscurses`](https://github.com/zsh-users/zsh/blob/master/Functions/Misc/tetriscurses) too. 145 | 146 | --- 147 | 148 | Previous: [functions](functions.md) | [home](../../README.md) | Next: [completions](completions.md) 149 | -------------------------------------------------------------------------------- /docs/misc/reference.md: -------------------------------------------------------------------------------- 1 | # Reference Material 2 | 3 | * [Zsh Users](https://github.com/zsh-users) 4 | * [Terminals are sexy](https://terminalsare.sexy/) 5 | * [Zsh docs](http://zsh.sourceforge.net/Doc/Release/index.html#Top) 6 | * [Awesome zsh plugins](https://github.com/unixorn/awesome-zsh-plugins) 7 | 8 | ## Tools I Used 9 | 10 | * [Screenkey](https://gitlab.com/wavexx/screenkey) - Keyboard overlay 11 | * [Peek](https://github.com/phw/peek) - Gif recorder 12 | * [Kitty](https://sw.kovidgoyal.net/kitty/) - terminal 13 | -------------------------------------------------------------------------------- /docs/usage/file_management.md: -------------------------------------------------------------------------------- 1 | # File Management 2 | 3 | If you use a POSIX operating system where "everything is a file™" you'll likely be managing files frequently. 4 | 5 | zsh of course adds some nice file management functions. 6 | 7 | ## Listing and Globbing 8 | 9 | ```bash 10 | $ ls ~/*/*/*.txt 11 | 12 | $ ls ~/**/tmp/* 13 | ``` 14 | 15 | ## zmv 16 | 17 | `zmv` is a user contributed function included with most zsh distributions. 18 | It allows you to move, copy, or link files based on pattern matching. 19 | You can read more in the functions section. 20 | 21 | First load zmv in your .zshrc file or local shell with. 22 | 23 | ```bash 24 | $ autoload -U zmv 25 | ``` 26 | 27 | Example usage: 28 | 29 | ```bash 30 | # rename all .JPEG extensions to .jpg 31 | $ zmv '(*).JPEG' '$1.jpg' 32 | 33 | # replace all spaces in file and folder names with underscore 34 | # notice parameter replacement is available from the variables section 35 | $ zmv '(* *)' '${1// /_}' 36 | ``` 37 | 38 | The `zmv` widget can also handy copying and symlinking. 39 | You can use it with `alias zcp='zmv -C'` and `alias zln='zmv -L'` or you can symlink files names `zcp` and `zln` in your `$fpath` to `zmv`. 40 | 41 | --- 42 | 43 | Previous: [navigation](navigation.md) | [home](../../README.md) 44 | -------------------------------------------------------------------------------- /docs/usage/line_movement.md: -------------------------------------------------------------------------------- 1 | # Line Movement 2 | 3 | Effectively moving around on the command line might be the single biggest time saver. 4 | Most of the time you're typing but you'll also be doing a fair deal of line editing and movement. 5 | 6 | If you're using arrows to move back and forth you can save a lot of time learning some basic line movement keyboard shortcuts. 7 | 8 | Your line movement will depend on your loaded keymaps and the widgets you have bound to different keys. 9 | More information about custom widgets can be found in the zle section. 10 | 11 | You can manually set your keymap mode with 12 | 13 | ```bash 14 | # emacs mode 15 | bindkey -e 16 | 17 | # vi mode 18 | bindkey -v 19 | ``` 20 | 21 | For the examples below I'll be using vi mode for movement. 22 | 23 | > If your `$EDITOR` or `$VISUAL` environment variables start with `vi` when the shell starts your keymap mode is automatically set to vi 24 | 25 | Keybindings have special characters to represent them. 26 | More info can be found in [bindkey](bindkey.md). 27 | 28 | For reference: 29 | 30 | * `^` = Ctrl 31 | * `^[` = Alt 32 | * `\e` or `\E` = Escape 33 | 34 | If you want to figure out how a key sequence should be represented you can try `xxd` or `echo " $keypress"`. 35 | The key letter you press is not case sensitive. 36 | So `^a` is the same as `^A`. 37 | 38 | ## Basic movement 39 | 40 | I may like vi mode for my keymap but I'm not a monster. 41 | Let's set the emacs begining and end of line keyboard shortcuts and reverse search. 42 | 43 | > `bindkey` works by default on your main keymap. 44 | > In vi mode that is `viins` so these keybindings work when typing but not in `vimcmd` mode. 45 | > To explicitly set a keymap mode use `bindkey -M $mode`. 46 | 47 | ```bash 48 | bindkey '^r' history-incremental-search-backward 49 | bindkey '^a' beginning-of-line 50 | bindkey '^e' end-of-line 51 | # Also fix annoying vi backspace 52 | bindkey '^?' backward-delete-char 53 | ``` 54 | 55 | Now I have four ways to get to the begining and end of a command line. 56 | I can press `Escape`,`^` or `Ctrl`+`a` for the begining of the line, and `Escape`,`$` or `Ctrl`+`e` for the end of the line. 57 | 58 | > With vi mode you have to press escape to enter vicmd mode to enter standard vi commands. 59 | > The `^` and `$` are standard vi commands for beginning and end of a line. 60 | 61 | We also probably want some other useful keyboard shortcuts so we don't have to go back and forth between (vicmd) and vi-insert (viins) modes. 62 | I personally like these. 63 | 64 | ```bash 65 | bindkey '^[b' vi-backward-blank-word 66 | bindkey '^[w' vi-forward-blank-word 67 | ``` 68 | 69 | To find widgets you can use, remember to use `zle -la` to list all widgets and `alt+x` or `esc,:` to try the widget with `execute-named-cmd`. 70 | 71 | ## Modifying Text 72 | 73 | Remember, you can use any emacs or vi commands depending on what keymap you're using. 74 | You don't have to bind keys to use them. 75 | 76 | If you want some additional functionallity check out the examples below. 77 | 78 | In general you'll be binding widgets to keyboard shortcuts. 79 | There are many `*-kill-*` widgets available that will let you kill words, lines, or letters. 80 | 81 | If you don't want to feel like a murderer you can also try the `*-yank-*` and `*-push-*` widgets too. 82 | 83 | ### Advance Word Movements and Modifications 84 | When jumping between words (for example `[^f` and `[^b` for forward and backwards in emacs mode). The shell uses a few different 85 | things to define what a "word" is. By default you can `echo $WORDCHARS` to see all the special characters the shell will include 86 | as a single word. For example the default is: 87 | ``` 88 | $ echo $WORDCHARS 89 | *?_-.[]~=/&;!#$%^(){}<> 90 | ``` 91 | This means that any alphanumeric character plus any of the above will be combined to be a single word. Notice how `/` is 92 | included in the above. This means `foo/bar-bazz` will be one word to be acted upon. 93 | 94 | If you are coming from bash and want your jump/delete word functions to stop at `/` and `-` you can add this in 95 | your .zshrc: 96 | ``` 97 | autoload -U select-word-style 98 | select-word-style bash 99 | ``` 100 | Without it `[^D` will delete a full directory path. For example: 101 | ``` 102 | ## Without 103 | $ cd /project/example/delete 104 | ## Press [^D from front beginning of line 105 | $ /project/example/delete 106 | ## Press [^D again 107 | $ 108 | 109 | ## With select-word-style bash 110 | $ cd /project/example/delete 111 | ## Press [^D from front beginning of line 112 | $ /project/example/delete 113 | ## Press [^D again 114 | $ /example/delete 115 | ``` 116 | You can learn about the available word styles and their behavior [here](https://linux.die.net/man/1/zshcontrib) (search for `select-word-syle`). 117 | But here is a quick explanation: 118 | ``` 119 | $ select-word-style 120 | Usage: select-word-style word-style 121 | where word-style is one of the characters in parentheses: 122 | (b)ash: Word characters are alphanumerics only 123 | (n)ormal: Word characters are alphanumerics plus $WORDCHARS 124 | (s)hell: Words are command arguments using shell syntax 125 | (w)hitespace: Words are whitespace-delimited 126 | (d)efault: Use default, no special handling (usually same as `n') 127 | (q)uit: Quit without setting a new style 128 | ``` 129 | One important thing to note is that any `select-word-style` that is not `normal` will may not respect `$WORDCHARS`. 130 | When in `normal` select-word-style all alphanumeric characters plus anything in `$WORDCHARS` is used by zsh to determine 131 | when to stop its action. If you want even more control feel free to set the var directly. This can then be used to make your 132 | backwards jump different than your forwards jump. 133 | For example if I want my backward delete to delete a whole directory path I can set this: 134 | ``` 135 | ## with word-style set to `normal` but $WORDCHARS='' 136 | default-backward-delete-word () { 137 | local WORDCHARS="*?_[]~=/&;!#$%^(){}<>" 138 | zle backward-delete-word 139 | } 140 | zle -N default-backward-delete-word 141 | bindkey '^W' default-backward-delete-word 142 | ``` 143 | 144 | ### Yank current command and paste 145 | 146 | For example, let's say you have a long command line typed up but you forgot you needed to run a command first or look up some information. 147 | Normally you would push `ctrl+c` to cancel the command, run your command, and then use your mouse to select, copy, and paste the long command to the next prompt. 148 | Or maybe you open a new terminal, run your command, and come back. 149 | 150 | What if I told you there was a better way? 151 | Check out the `push-input` widget. 152 | 153 | Let's bind it to `ctrl+q` and see what it does. 154 | 155 | ![](../../img/push-input.gif) 156 | 157 | You can also use `yank-pop` widget to keep a ring of yanked commands you can cycle through. 158 | 159 | ```bash 160 | # re-sets yank and kill shortcuts to bash/emacs mode 161 | bindkey -M viins '^P' push-line-or-edit 162 | bindkey -M viins '^Y' yank 163 | # in order to use the below you must first yank without hitting enter 164 | # subsequent alt-y combos will cycle through 165 | # kill ring 166 | bindkey -M viins '\ey' yank-pop 167 | bindkey -M viins '^U' backward-kill-line 168 | ``` 169 | 170 | ### Matching deliminators 171 | 172 | There's a handy plugin called [autopair](https://github.com/hlissner/zsh-autopair) that can automatically match your brackets and quotes in your terminal. 173 | It works similarly to an IDE and loads an autopair-insert, autopair-close, and autopair-delete widget. 174 | 175 | ![](../../img/autopair.gif) 176 | 177 | There's also an included [`surround`](https://github.com/zsh-users/zsh/blob/master/Functions/Zle/surround) widget but I had difficulty using it. YMMV. 178 | 179 | --- 180 | 181 | Previous: [completions](../helpers/completions.md) | [home](../../README.md) | Next: [navigation](navigation.md) 182 | -------------------------------------------------------------------------------- /docs/usage/navigation.md: -------------------------------------------------------------------------------- 1 | # Navigation 2 | 3 | Navigating your filesystem is likely something you do on a regular basis. 4 | zsh knows this and has added some helpers to help you move quickly from one folder to another. 5 | 6 | ## `cd` basics 7 | 8 | Just to make sure we're all on the same page, here are some quick `cd` tips that should work in any shell. 9 | 10 | ```bash 11 | # change directory to your $HOME with just the cd command 12 | cd 13 | 14 | # change directory to the previous directory with 15 | cd - 16 | ``` 17 | 18 | Now let's set the `cdpath` variable. 19 | This variable will do automatic searching for a folder within folders specified in variable. 20 | 21 | If you're familiar with bash's `CDPATH` it works the same way but in zsh instead of being a colon separated string (e.g. `.:~:~/src`) it's an array. 22 | 23 | ```bash 24 | # make some temporary folders 25 | $ mkdir -p ~/test ~/src/foo/bar 26 | 27 | $ export cdpath=(. ~ ~/src/) 28 | 29 | # cd to $HOME 30 | $ cd 31 | 32 | # cd to ~/src/foo 33 | $ cd foo 34 | 35 | # cd to ./bar 36 | $ cd bar 37 | 38 | # cd to ~/test 39 | $ cd test 40 | 41 | # cd back to ~/src/foo/bar 42 | $ cd - 43 | ``` 44 | 45 | That's pretty powerful! 46 | Add more search directories you frequent and jump right to them. 47 | 48 | zsh also allows more powerful directory movement. 49 | `AUTO_CD` will automatically change into a directory even without `cd` if that directory is not the name of a command. 50 | 51 | ```bash 52 | $ ls 53 | foo/ bar/ some.txt 54 | 55 | $ setopt auto_cd 56 | 57 | # cd into foo 58 | $ foo 59 | 60 | $ pwd 61 | ~/foo 62 | 63 | # change directory to other parts of the filesystem with full paths 64 | $ ls /var/log/ 65 | apache docker 66 | 67 | $ /var/log/apache 68 | ``` 69 | 70 | That's cool but doesn't save a lot of time. 71 | You can also cd into unique paths with using partial words 72 | 73 | ``` 74 | # assume this directory exists /var/log/apache/errors/ 75 | # cd into it with 76 | $ /v/l/a/e 77 | 78 | $ pwd 79 | /var/log/apache/errors/ 80 | 81 | # if /var/log/aero/errors exists you can use the minimal unique names 82 | $ /v/l/ae/e 83 | 84 | $ pwd 85 | /var/log/aero/errors 86 | ``` 87 | 88 | You can also swap any part of your current path. 89 | If you have two directories with similar structures it makes moving between them much faster. 90 | 91 | ```bash 92 | $ mkdir -p foo/bar/baz/{today,tomorrow,jan-01,jan-02}/{folder1,folder2} 93 | 94 | $ tree -F foo 95 | foo 96 | └── bar/ 97 | └── baz/ 98 | ├── today/ 99 | │   ├── folder1/ 100 | │   └── folder2/ 101 | ├── tomorrow/ 102 | │   ├── folder1/ 103 | │   └── folder2/ 104 | ├── jan-01/ 105 | │   ├── folder1/ 106 | │   └── folder2/ 107 | └── jan-02/ 108 | ├── folder1/ 109 | └── folder2/ 110 | 111 | $ f/b/b/tom/folder2 112 | 113 | $ pwd 114 | foo/bar/baz/tomorrow/folder2 115 | 116 | # cd into foo/bar/baz/today/folder2 117 | $ cd tomorrow today 118 | 119 | $ pwd 120 | foo/bar/baz/today/folder2 121 | 122 | # this also works for partial names 123 | $ cd today jan-01 124 | 125 | $ pwd 126 | foo/bar/baz/jan-01/folder2 127 | 128 | $ cd 01 02 129 | 130 | $ pwd 131 | foo/bar/baz/jan-02/folder2 132 | ``` 133 | 134 | This is about as good as it's going to get without using an external tool. 135 | 136 | ## External Tools 137 | 138 | Now that we've covered some of what zsh can do built in it's up to you to take it one step further with external tools. 139 | These tools will keep track of files and directories you frequently use and allow you to interactively search for them or automatically jump to them. 140 | 141 | Some additional homework for the reader is to install and setup one of the following 142 | * [fasd](https://github.com/clvv/fasd): All in one jump and edit tool with fuzzy matching 143 | * [autojump](https://github.com/wting/autojump): Jump to directories with frequency tracking 144 | * [z](https://github.com/rupa/z): Jump to most used directories based on 'frecency' 145 | * [v](https://github.com/rupa/v): Like `z` but for editing files with `vim` 146 | 147 | --- 148 | 149 | Previous: [line movement](line_movement.md) | [home](../../README.md) | Next: [file management](file_management.md) 150 | -------------------------------------------------------------------------------- /img/autopair.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/autopair.gif -------------------------------------------------------------------------------- /img/autosuggestions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/autosuggestions.gif -------------------------------------------------------------------------------- /img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/banner.png -------------------------------------------------------------------------------- /img/bash-completion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/bash-completion.gif -------------------------------------------------------------------------------- /img/edit-command.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/edit-command.gif -------------------------------------------------------------------------------- /img/execute-named-cmd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/execute-named-cmd.gif -------------------------------------------------------------------------------- /img/fc-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/fc-example.gif -------------------------------------------------------------------------------- /img/fzf-history.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/fzf-history.gif -------------------------------------------------------------------------------- /img/globalias.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/globalias.gif -------------------------------------------------------------------------------- /img/highlighting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/highlighting.gif -------------------------------------------------------------------------------- /img/history-incremental-pattern-search-forward.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/history-incremental-pattern-search-forward.gif -------------------------------------------------------------------------------- /img/last-command.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/last-command.gif -------------------------------------------------------------------------------- /img/prepend-sudo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/prepend-sudo.gif -------------------------------------------------------------------------------- /img/push-input.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/push-input.gif -------------------------------------------------------------------------------- /img/tetris.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rothgar/mastering-zsh/921766e642bcf02d0f1be8fc57d0159a867299b0/img/tetris.gif --------------------------------------------------------------------------------