--title zsh: Power Tools. Because having all your fingers is overrated. --author Paul L. Snyder --date January 12, 2009 --newpage --heading A non-partisan guide to shell selection --newpage --heading sh * The original Bourne shell * Written by Stephen Bourne for Version 7 Unix in 1977 * Usually, your '/bin/sh' is another shell running in compatibility mode --- Why should I use it? --- * You are writing a "portable" shell script --- * Your life will still be hell --- * If you're writing a large system, use a real programming lanugage (Perl, Python, etc.) --newpage --heading ksh * David Korn's Korn shell * MUCH better for programming than sh or *shudder* csh * Some interactive improvements * Two major versions, ksh88 and ksh93 (finally open sourced in 2000) * The FSF released pdksh (mostly ksh88-compatible) --- Why should I use it? --- * Misguided nostalgia --newpage --heading csh * The "C" shell * Ha! * Introduced many now-standard interactive features (job control, aliases, !-substitution * Nastily brain-damaged scripting behavior * Csh Programming Considered Harmful: http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/ * Implementations were historically very buggy * The modern Hamilton C Shell fixes many of the problems. But why? --- Why should I use it? --- * You are an idiot. --newpage --heading tcsh * t is for 'TENEX' * The TENEX OS (later TOPS-20) had command completion facilities * tcsh introduced programmable command completion to Unix shells * kept the csh syntax --- Why should I use it? --- * You like having cool people laugh at you. --- * Actually, there is no good reason to use tcsh. --newpage --heading bash * The Bourne-again shell * ksh was proprietary and csh sucked, so bash was created * Considered a ksh descendant * Default shell for most Linux distributions * /bin/sh is usually bash under the covers (except on Ubuntuish systems) --- Why should I use it? --- * You like the safety of being one of the herd --- * Power scares you --- * You are pathetically grateful for finally receiving features zsh has had for years --newpage --heading The Z shell * Mostly descended from ksh --- * Absorbs interesting (and possibly conflicting) features from other shells --- * Possesses the lucid clarity of perl... --- * ...and the streamlined elegance of emacs --- Why should I use it? --- * See remainder of presentation, below --newpage shell usage 1 --heading Shell usage... * Scripting - Make common tasks easier --- - Use POSIX sh for portability, not zsh = Portable shell code is non-trivial --- - Don't write large systems using shell scripting! = Use, e.g., Perl, Python, Ruby --newpage shell usage 2 --heading ...and Shell Usage * Interactive use - Make common tasks easier --- - Flexible command-line editing and history --- - Globbing --- - Completion --newpage agenda --heading Agenda * Startup files * zsh's modular design * Variables * Expansion and substitution * Interactive use * Completion --newpage initial words --heading But first, a few words... * zsh in all its gory glory is unspeakably complex --- * Happily, you don't need to know much to start using the shell --- * zsh rewards knowledge with power --newpage startup --heading Startup files Login files are run in this order ($ZDOTDIR defaults to $HOME): /etc/zshenv Run by all shells, cannot be overridden $ZDOTDIR/.zshenv Run by all shells (if RCS option is set) --- /etc/zprofile Run by login shells (if GLOBAL_RCS is set) $ZDOTDIR/.zprofile Run by login shells (if RCS is set) --- /etc/zshrc Run by interactive shells (if GLOBAL_RCS) $ZDOTDIR/.zshrc Run by interactive shells (if RCS is set) --- /etc/zlogin Run by login shells (if GLOBAL_RCS is set) $ZDOTDIR/.zlogin Run by login shells (if RCS is set) --newpage shutdown --heading Shutdown files When exiting: $ZDOTDIR/.zlogout Run by login shells (if RCS is set) /etc/zlogout Run by login shells (if GLOBAL_RCS is set) --newpage creating dotfiles --heading How do you create your startup files? * zsh is pretty bland until it has been configured. --- * Most of the cool options are turned off by default. --- * Two courses: --- - Steal someone else' .zshrc --- - Use the menu-based zsh-newuser-install (many distributions configure this to be run by default) [demo] --newpage zsh-newuser-install --heading Running zsh-newuser-install - If this isn't the case you can run it yourself: --beginshelloutput $ autoload -Uz zsh-newuser-install; zsh-newuser-install -f --endshelloutput --newpage modules --heading zsh Modules * Portions of the shell are compiled as optional .so modules. They can be loaded using 'zmodload'. Modules include: - zsh/zftp: command-line FTP program - zsh/complete: Programmable completion system - zsh/net/socket: 'zsocket' command to maniplate UNIX domain sockets - zsh/net/tcp: 'ztcp' command to create and accept TCP connections - zsh/zpty: Run a command under its own pseudo-terminal --newpage modules2 --heading more zsh Modules * A few more modules of interest: - zsh/termcap and zsh/terminfo: output termcap and terminfo sequences by capability name - zsh/mapfile: tie a file to an associative array - zsh/newuser: menu-drive dot-file creation for new users - zsh/pcre and zsh/regex: Perl-compatible and POSIX regexes --newpage arrays --heading Arrays --beginshelloutput $ beer=('Hop Devil' 'Golden Monkey' 'Old Horizontal') $ print $beer[2] # or ${beer[2]} Golden Monkey $ print $foo[-1] # negative subscripts allowed Old Horizontal --endshelloutput --- * 'setopt ksharrays' for ksh-style behavior --- * bash 3 supports arrays of this type --newpage hashes --heading Associative Arrays! --beginshelloutput $ typeset -A collective $ collective=(larks exaltation ravens unkindness crows murder) $ print $collective[larks] exaltation $ print ${(k)collective} larks ravens crows --- * You may be familiar with these as perl hashes --- * Associative array support will be coming in bash 4 --newpage numeric variables --heading Typed numeric variables * Integers: typeset -i foo * Alternate base integers: typeset -i 16 bar * Floating point, fixed notation: typeset -F baz * Floating point, scientific notation:typeset -E womble --beginshelloutput $ zmodload zsh/mathfunc $ (( pi = 4.0 * atan(1.0) )) $ echo $pi 3.1415926536 --endshelloutput --newpage typset tricks --heading Fun with typeset * Create a tied variable/array pair: typeset -T FOO foo * Create a variable that always expands to lowercase: typeset -l BAR * Or uppercase: typeset -u BAZ * Make a variable read-only: typeset -r WOMBLE * Keep array entries unique: typeset -U path --newpage prompts --heading Prompts * PS1 displayed at regular command prompt * PS2 for second-level prompt - Also displays details of nested shell constructs [demo] * PS3 displayed inside 'select' construct' * PS4 is the trace prompt --- * RPS1 and RPS2 are right prompts! 'RPS1=%t' --newpage prompt expansion --heading Prompt expansion (a partial list) %M FQ hostname %# '#' if shell is privileged, '%' otherwise %m hostname up to '.' %_ nesting status of shell constructs (for PS2) %n username %d Present working directory - $PWD %y User's login tty %3d Last three components of $PWD %h Current history num %i Line number of a trace (for PS4) %n Current script or func %D Date in yy-mm-dd %t time in 12-hour format %{..%} Escape sequence %D{format} format date using strftime(3) %n(x.true-text.false-text) E.g. %# = $(!.#.$) ! privileges # effective uid is n ? exit status of last command is n d day of the month is n / current absolute path has n elements --newpage expansion --heading Expansion and Substitution When you enter a command at the prompt, it is mangled as follows: 1. History Expansion 2. Alias Expansion 3. Process Substitution, Parameter Expansion, Command Substitution, Arithmetic Substitution, and Brace Expansion 4. Filename Expansion 5. Filename Generation ("globbing") --newpage history --heading History * History is inspired by csh's history system --- - setopt CSH_JUNKIE_HISTORY to lobotomize zsh --- !! is the last command executed --- !!$ is the last word of the last command --- !n refers to history command numbered 'n' --- 'history' for a list, or add '%h' to your prompt --- !str last command starting with 'str' --- !# is the command you are typing right now! --- !?str[?] is the last command containing 'str' --- !{...} prevents confusion with surrounding text --newpage word selectors --heading Selecting a word in a history line 0 the first word n the nth argument ^ the first argument $ the last argument % the word match by a 'str' search n-m words n through m * all the arguments --newpage modifiers --heading History modifiers For extra fun, use these with regular parameters! h remove one trailing path component t remove all but the last path component r remove filename extension e remove everything but the extension l convert all words to lowercase u convert all words to uppercase --- [g]s/old/new[/] Replace 'old' string with 'new'. if 'new' contains '&', '&' is replaced with 'old' --newpage modified modifiers --heading ...and Modifing the Modifiers! f repeat following modifier exhaustively F:expr: repeat following modifier expr times w apply following modifier to each word W:sep: like w, but applies to parts of string that are separated by 'sep' --newpage parameter expansion --heading Parameter expansion * All the usual suspects. E.g.: --beginshelloutput $ echo ${foo?BAR} BAR $ foo=FOO $ echo ${foo?BAR} FOO $ baz=/this/is/a/path $ echo ${baz%is*} /this/ $ echo ${baz/*is} /a/path --endshelloutput --newpage flags --heading Change things up with parameter expansion flags There are LOTS of these...this is just a small selection Place in parentheses before the parameter name, e.g., ${(%)PS1} % Expand prompt sequences C Capitalize each resulting word L Convert all letters to lowercase o sort words in ascending order O sort words in descending order u expand only first unique occurence of each word j:str: join the words of arrays using 'str' q quote the expanded words --newpage globbing --heading Filename generation, a.k.a. globbing - You all know about *, ?, [x] and [^y]. --- - How about 'ls bar<2-6>'? Only matches existing files --- - ^*FOO* globs all files without 'FOO' in their names --- - *(foo|bar)* globs files with either 'foo' or 'bar' --- - ba^z* globs 'bar' but not 'baz' --- - (foo)# matches zero or more 'foo's...(foo)## matches any number --newpage ksh globbing --heading It gets worse... - Use ksh-style glob operators to tweak your parentheses ---ls - *(foo) matches zero or more 'foo's --- - ?(foo) matches zero or one 'foo's --- - +(foo) matches one of more 'foo's --- - !(foo) match anything BUT 'foo' --- - Are you frightened yet? --newpage globbing flags --heading We're not done...it's time for globbing flags! --- - (#l) lowercase characters match upper or lower case; uppercase matches uppercase (#I) reenables case sensitivity --- - (#b) activate backreferences for parenthesized groups; store the matches in the $match array and the indices in $mbegin and $mend (#B) ends backreferencing. --- - (#aN) Use approximate matching! Allow up to N errors in the match. --- --newpage glob qualifiers --heading Ye gods, there's more! Glob qualifiers appear in parentheses at the end of a glob specifier... --- *(.) matches regular files only --- *(/) matches all directories --- *(@) matches all symbolic links --- *(x) matches all owner-executable files --- *(s) matches all setuid files --- *(f{go+w}) matches group or other-writeable files! --- This next one even scares me... --- *(estring) executes string as shell code! The currently matched file is available in $REPLY; override the return with $reply or $REPLY. Yipes! --newpage recursive globbing --heading Recursive Globbing One last trick... $ ls **/foo* --- Matches 'foo*' current directory or any subdirectory! --newpage simple structures --heading ...And now for something completely simple for x in *; do mv $x ${x:r}.bak; done --- Too much work! In zsh, just use for x in *; mv $x ${x:r}.bak --- Actually, this is now deprecated, so it's a bad habit that I keep using it. --- Similar short forms exist for 'if', 'while', and so on. --- Even better better: zmv '(file0?)' '$1.bak' --newpage zmv --heading zmv * zmv is the command-line rename tool you've always wanted --beginshelloutput $ zmv '(*)-(*).mpeg3' '$2_$1.mp3' --- $ zmv '(*)' '${(L)1}' --- $ alias mmv='noglob zmv -W' $ mmv *.pl.bak backups/*.pl --endshelloutput --newpage correction --heading Correction * Oopsing commands? 'setopt CORRECT' --- * Fat-fingering filenames? 'setopt CORRECT_ALL' --- * Prevent a command from being corrected: alias mv='nocorrect mv' --newpage multios --heading MULTIOS and redirection setopt MULTIOS for built-in tee functionality $ ls >>file1 >file2 | cat --- $ : > * This truncates every file matched by *! --- Well, not quite, as long as NO_CLOBBER is set. For maximum damage, use $ : >| * --newpage more multios --heading More MULTIOS How about a NULLCMD? $ combined --- Change the default command by setting NULLCMD to something other than 'cat' --- $ >combined --- zsh: file exists: combined NOCLOBBER saves your bacon. --newpage interactive --heading Useful interactive features * Multi-line editing --- * Variable editing! 'vared path' --- * 'zed' is zsh's built-in editor...use your zsh bindings for a quick edit (use C-j to save and exit or C-g to cancel) --- * One of my favorites: the buffer stack $ bindkey '\eq' push-line-or-edit * Stuff the buffer with 'print -z' --newpage completion --heading Finally, completion * I'm not going to tell you how to write new completion functions --- * I don't want to be lynched. --- * Besides, most anything you'd want to complete is probably in there already. --- * To get started using completion, just turn it on when you run zsh-newuser-install --newpage education through completion --heading Education through Completion * Forgot an option? Just hit TAB * Look at completions for 'tar', 'mplayer', 'emerge', or 'dpkg' --newpage information --heading Bedtime reading * man zsh and its 15 subpages (or just man zshall) * http://www.zsh.org - Read the zsh user manual...friendly and useful * Tips and tricks - http://www.rayninfo.co.uk/tips/zshtips.html - http://gd.tuwien.ac.at/opsys/linux/grml/grml-www/zsh/zsh-lovers.html * http://www.zshwiki.org --newpage end --heading Fin. --newpage random file --heading Selecting a random file from the current directory --beginshelloutput $ files=(*); echo $files[$RANDOM%$#files]] --endshelloutput --newpage suffix aliases --heading For the brain-damaged by Windows: file associations --beginshelloutput $ alias -s txt=less --endshelloutput --- This can be bad for security! --newpage global aliases --heading Global aliases work anywhere in the line --beginshelloutput $ alias -g ...='../..' $ alias -g L='| less' $ alias -g G='| egrep' --endshelloutput --newpage named directories --heading Name your favorite directories with CDABLE_VARS --newpage auto_cd --heading Eschew cd with AUTO_CD and AUTO_PUSHD --newpage