\documentclass{beamer} \usetheme{Luebeck} \setbeamercovered{dynamic} \usepackage{graphicx} \usepackage{fontspec} \beamertemplatenavigationsymbolsempty \usepackage{etoolbox} \makeatletter \preto{\@verbatim}{\topsep=0pt \partopsep=0pt } \makeatother \title{\texttt{zsh} Protips} \author{Jack Rosenthal} \date{4 April 2016} \logo{\includegraphics[width=30mm]{graphics/lug.pdf}} \begin{document} \begin{frame} \maketitle \end{frame} \logo{} \begin{frame} \frametitle{What is \texttt{zsh}?} \texttt{zsh} is a UNIX shell with \texttt{bash}-like syntax and plenty of features. \pause \begin{itemize}[<+->] \item \texttt{zsh} features its own line editor, \texttt{zle}, with bindable widgets and the ability to make custom bindings \item \texttt{zsh} features its own history expansion engine with Readline compatibility \item \texttt{zsh} tries to avoid \texttt{bash}isms by making the syntax do what it looks like it is doing (eg. appending and writing to two files in the same command) \item \texttt{zsh} comes with plenty of syntactic sugar and features like floating point arithmetic \end{itemize} \end{frame} \begin{frame} \frametitle{Using \texttt{zsh} as your default shell (Personal machine)} \centering \Large \texttt{\$ chsh -s `which zsh`} \end{frame} \begin{frame} \frametitle{Using \texttt{zsh} as your default shell (School machine)} Put this line at the top of your \texttt{.bash\_profile}: \texttt{tty\hfill>/dev/null\hfill\&\&\hfill command\hfill -v\hfill zsh\hfill >/dev/null\hfill \&\&\hfill exec\hfill zsh} \medskip A few notes: \begin{itemize} \item You only have to do this because the \texttt{loginShell} LDAP attribute is used as your shell, and you don't have permission to change it. \item If you change it on one machine that uses \texttt{fermat} for its \texttt{/u}, then it will be changed on all. \end{itemize} \end{frame} \begin{frame} \frametitle{Setting up \texttt{zsh}} \textbf{Option 1} Use an ``instantly awesome zsh'' framework like \emph{Oh My Zsh} or \emph{Prezto}. This has the advantage of a \emph{Vundle}-like plugin system and less time spent configuring. \medskip \textbf{Option 2} Do it yourself. Allows more customisation and typically lighter. \end{frame} \begin{frame}[fragile] \frametitle{Writing a \texttt{.zshrc}} If you've decided to go with \textbf{Option 2}, you will need to write a \texttt{.zshrc}, a script that runs when you start \texttt{zsh}. Here are some things you may consider adding: \medskip \begin{tabular}{l l} \texttt{bindkey -v} \emph{or} \texttt{bindkey -e} & \texttt{vim} or \texttt{emacs} \texttt{zle} bindings \\ \texttt{HISTFILE=\textasciitilde/.histfile} & Persistent history \\ \texttt{HISTSIZE=1000} & Up to 1000 items in history \\ \texttt{SAVEHIST=1000} & Up to 1000 items persistent \\ \texttt{setopt appendhistory} & Append history to the history file \\ \texttt{setopt histignoredups} & Ignore duplicates in history \\ \texttt{setopt histignorespace} & Ignore lines which begin with a space \\ \texttt{setopt autopushd} & Use the dirstack as you \texttt{cd} \end{tabular} \medskip Also don't forget to set your \texttt{EDITOR}, \texttt{PAGER}, etc. \end{frame} \begin{frame} \frametitle{More \texttt{setopt} options} \begin{itemize} \item \texttt{beep}/\texttt{nobeep} - Ring the terminal bell on \texttt{zle} error \item \texttt{notify} - Report the status of background jobs immediately \item \texttt{nomatch} - If a globbing pattern has no matches, print an error, instead of leaving it unchanged in the argument list. \item \texttt{autocd} - Change to a directory if you just type the name \item \texttt{correct} - Typo Correction \item \texttt{extendedglob} - Extended globbing, explained later \end{itemize} Read \texttt{man zshoptions}. There are many options. \end{frame} \begin{frame} \frametitle{Extended Globbing} With \texttt{setopt extendedglob}, you can use some cool extended globbing patterns: \begin{itemize} \item \texttt{**} matches any all of the child directories, recursively, including the current directory \item \texttt{***} is the same as above, but follows symlinks \end{itemize} If you enable \texttt{setopt globstarshort}, you can shorten \texttt{**/*} to \texttt{**} and \texttt{***/*} to \texttt{***}. This would cause \texttt{**.c} to match all files ending in \texttt{.c} recursively. \end{frame} \begin{frame}[fragile] \frametitle{More Cool Globbing} \textbf{Globbing What Its Not} \begin{verbatim} zsh % ls main.c Makefile README.md zsh % echo ^*.c Makefile README.md\end{verbatim} \pause \vfill \textbf{Numeric Ranges} \begin{verbatim} zsh % ls hello1234 hello1235 hello1400 zsh % echo hello<1230-1240> hello1234 hello1235\end{verbatim} \pause \vfill \textbf{Perl-Style Or} \begin{verbatim} zsh % ls hello.txt world.gif zap.sh zsh % echo *.(txt|gif) hello.txt world.gif\end{verbatim} \end{frame} \begin{frame}[fragile] \frametitle{Brace Expansion} \begin{verbatim}zsh % echo hello_{one,two,three,four,five} hello_one hello_two hello_three hello_four hello_five\end{verbatim} \pause \begin{verbatim}zsh % echo hello{1..5} hello1 hello2 hello3 hello4 hello5\end{verbatim} \pause \begin{verbatim}zsh % echo hello{07..12} hello07 hello08 hello09 hello10 hello11 hello12\end{verbatim} \pause \begin{verbatim}zsh % echo {a..z} a b c d e f g h i j k l m n o p q r s t u v w x y z\end{verbatim} \end{frame} \begin{frame} \frametitle{Parameter Expansion} \texttt{\$\{}...\texttt{\}} is a parameter expansion. A parameter expansion will always involve a variable. \begin{itemize}[<+->] \item \texttt{\$\{VAR\}} will expand to the value of \texttt{VAR}. \item \texttt{\$\{\#VAR\}} will expand to the length of \texttt{VAR}. \item If \texttt{VAR} is a filename, \texttt{\$\{VAR:h\}} will expand to the directory of the file, \texttt{:t} to the name of the file, and \texttt{:r} to the file without its extension. \item \texttt{\$\{VAR:s/$find$/$replace$/\}} will do \texttt{sed}-style substitution \end{itemize} \pause Read \texttt{man zshexpn} for more. I take no responsibility for brain damage. \end{frame} \begin{frame} \frametitle{Suffix Aliases} \centering Say you want \texttt{.tex} files to open in your \texttt{\$EDITOR}. \medskip {\Large\ttfamily alias -s tex=\$EDITOR\par} \medskip Then type just the filename as the command. \end{frame} \begin{frame} \frametitle{Global Aliases} \centering Global aliases expand anywhere in the command. \medskip {\Large\ttfamily \begin{tabular}{l} alias -g ...='../..' \\ alias -g ....='../../..' \\ alias -g .....='../../../..' \\ \end{tabular} } \end{frame} \begin{frame} \frametitle{Multiple Redirection} \texttt{zsh} can redirect to and from multiple inputs/outputs at the same time. So... \medskip \small \begin{tabular}{l l} Rather than typing & You can type \\ \hline \texttt{w >file1; w >file2; w >file3} & \texttt{w >file\{1..3\}} \\ \texttt{cat file\{1,2\} | less} & \texttt{less log | grep ERR} \\ \end{tabular} \end{frame} \begin{frame} \frametitle{Process Substitution} You can substitute a command in, like it's a file. \begin{itemize} \item \texttt{<($\ldots$)} is used to read output from a command \item \texttt{>($\ldots$)} is used to write to the stdin of a command \item \texttt{=($\ldots$)} is like \texttt{<($\ldots$)}, however creates a temporary file (so seek is allowed) \end{itemize} For example, to compare the output of two commands: \texttt{diff <(command1) <(command2)} \end{frame} \begin{frame} \frametitle{Kill command for later use} Say you start typing a command but then realise you have to do something else first. Bind a key to \texttt{push-line}. I use \texttt{q} in \texttt{vi} normal mode: \medskip \texttt{bindkey -M vicmd q push-line} \medskip Then, press \texttt{q} when you have another command to run first. Your old command will reappear when the first one finishes. \end{frame} \begin{frame}[fragile] \frametitle{Making custom key widgets} Write a function, then bind it with \texttt{zle -N}. Example: \medskip \begin{verbatim} function __zkey_prepend_sudo { if [[ $BUFFER != "sudo "* ]]; then BUFFER="sudo $BUFFER" CURSOR+=5 fi } zle -N prepend-sudo __zkey_prepend_sudo bindkey -M vicmd "s" prepend-sudo\end{verbatim} \medskip Now \texttt{s} will put \texttt{sudo} at the beginning of the command. \end{frame} \begin{frame} \frametitle{Completion} To add intelligent completion to \texttt{zsh}, add this line to your \texttt{\textasciitilde/.zshrc}: \medskip \centering \Large \texttt{autoload -U compinit \&\& compinit} \end{frame} \begin{frame} \frametitle{Completion Automagic Rehash} \centering \Large \texttt{zstyle ':completion:*' rehash true} \end{frame} \begin{frame} \frametitle{Completion Case Correction} \centering \texttt{zstyle ':completion:*' matcher-list 'm:\{a-z\}=\{A-Z\}'} \end{frame} \begin{frame} \frametitle{Approximate Completion} \centering Allow one error for every three letters typed. \medskip \texttt{zstyle ':completion:*:approximate:' max-errors 'reply=(\$(((\$\#PREFIX+\$\#SUFFIX)/3 )) numeric )'} \end{frame} \begin{frame}[fragile] \frametitle{Misc: Directory Hashing} Hash directories like their home direcories for quick and convenient access: \medskip \begin{verbatim}zsh % hash -d os=~/classes/cs/os zsh % cd ~os\end{verbatim} \medskip \pause \begin{block}{Protip} \small If you copy your \texttt{\textasciitilde/.zshrc} between systems, it may be convenient to set up hashes on a per system basis: \smallskip \begin{verbatim}case $(hostname) in toilers) hash -d web=/home/www ;; mastergo) hash -d web=/var/www ;; ... esac\end{verbatim} \end{block} \end{frame} \end{document}