11. Command Line Editing

zsh’s command line editor, ZLE, is quite powerful. It is designed to emulate either emacs or vi; the default is emacs. To set the bindings for vi mode, type bindkey -v. If your EDITOR or VISUAL environment variable is vi, zsh will use vi emulation by default. You can then switch to emacs mode with bindkey -e.

In addition to basic editing, the shell allows you to recall previous lines in the history. In emacs mode, this is done with Ctrl+P (control-P) or (on many terminals) with the cursor-up key:

Example 11.1: History recall with Ctrl+P
% ls ~
-           README      file        mail        pub         tmp
Mailboxes   bin         func        nicecolors  scr         zsh
News        etc         iris        notes       src
% echo foobar
foobar
% «Ctrl+P»
% echo foobar«Ctrl+P»
% ls ~_

Pressing Ctrl+P once brings up the previous line (echo foobar); pressing it again brings up the line before that (ls ~). The cursor is left at the end of the line, allowing you to edit the line if desired before executing it. In many cases, ZLE eliminates the need for the fc command, since it is powerful enough to handle even multiline commands:

Example 11.2: Recall multiline with Ctrl+P
% for i in a b c d e
> do
> echo $i
> done
a
b
c
d
e
% «Ctrl+P»
% for i in a b c d e
 do
 echo $i
 done_

Now you can just move up to the part you want to change…

Example 11.3: Edit recalled multiline command
% for i in _ b c d e
 do
 echo $i
 done

change it, and execute the new command.

Example 11.4: Execute edited multiline loop
% for i in f g h i j
 do
 echo $i
 done
f
g
h
i
j

Also, you can search the history for a certain command using Esc+P, this will look for the last command that started with the (part of the) word at the beginning of the current line. Hitting Esc+P another time gets you the command before that, etc.

Example 11.5: History search with Esc+P
% set «Esc+P»
% setopt autolist «Esc+P»
% setopt nocorrect_

Another way is to do an incremental search, emacs-style:

Suppose you have retrieved an old history event in one of these ways and would like to execute several consecutive old commands starting with this one. Ctrl+O will execute the current command and then put the next command from the history into the editor buffer. Typing Ctrl+O several times will therefore reexecute several consecutive commands from the history. Of course, you can edit some of those commands in between.

In addition to completion (see below), Tab performs expansion if possible.

Example 11.7: Expand globs with Tab
% ls *.c«Tab»
% ls foofile.c fortune.c rnd.c strfile.c unstr.c_

For example, suppose you have a bunch of weird files in an important directory:

Example 11.8: List awkward filenames
% ls
  * * *       ; & % $??foo  dspfok        foo.c
  !"foo"!       ` \ `         foo           rrr

You want to remove them, but you don’t want to damage foo.c. Here is one way to do this:

Example 11.9: Expand star with quoting
% rm *«Tab»
% rm \ \ \*\ \*\ \*\ \ \  \!\"foo\"\! \;\ \&\ %\ \$'
''
'foo \`\ \\\ \` dspfok foo foo.c rrr_

When you expand *, zsh inserts the names of all the files into the editing buffer, with proper shell quoting. Now, just move back and remove foo.c from the buffer:

Example 11.10: Delete all but foo.c
% rm \ \ \*\ \*\ \*\ \ \  \!\"foo\"\! \;\ \&\ %\ \$'
''
'foo \`\ \\\ \` dspfok foo _rr

and press return. Everything except foo.c will be deleted from the directory. If you do not want to actually expand the current word, but would like to see what the matches are, type Ctrl+Xg.

Example 11.11: List matches with Ctrl+X g
% rm f*«Ctrl+X»g
foo    foo.c
% rm f*_

Here’s another trick; let’s say you have typed this command in:

Example 11.12: Incomplete gcc command line
% gcc -o x.out foob.c -g -Wpointer-arith -Wtrigraphs_

and you forget which library you want. You need to escape out for a minute and check by typing ls /usr/lib, or some other such command; but you don’t want to retype the whole command again, and you can’t press return now because the current command is incomplete. In zsh, you can put the line on the buffer stack, using Esc+Q, and type some other commands. The next time a prompt is printed, the gcc line will be popped off the stack and put in the editing buffer automatically; you can then enter the proper library name and press return (or, Esc+Q again and look for some other libraries whose names you forgot).

A similar situation: what if you forget the option to gcc that finds bugs using AI techniques? You could either use Esc+Q again, and type man gcc, or you could press Esc+H, which essentially does the same thing; it puts the current line on the buffer stack, and executes the command run-help gcc, where run-help is an alias for man.

Another interesting command is Esc+A. This executes the current line, but retains it in the buffer, so that it appears again when the next prompt is printed. Also, the cursor stays in the same place. This is useful for executing a series of similar commands:

Example 11.13: Repeat similar cc commands
% cc grok.c -g -lc -lgl -lsun -lmalloc -Bstatic -o b.out
% cc fubar.c -g -lc -lgl -lsun -lmalloc -Bstatic -o b.out
% cc fooble.c -g -lc -lgl -lsun -lmalloc -Bstatic -o b.out

The Esc+' command is useful for managing the shell’s quoting conventions. Let’s say you want to print this string:

Example 11.14: String needing shell quotes
don't do that; type 'rm -rf \*', with a \ before the *.

All that is necessary is to type it into the editing buffer:

Example 11.15: Type unquoted awkward string
% don't do that; type 'rm -rf \*', with a \ before the *.

press Esc+' (escape-quote):

Example 11.16: After Esc+’ quote-line
% 'don'\''t do that; type '\''rm -rf \*'\'', with a \ before the *.'

then move to the beginning and add the echo command.

Example 11.17: Echo after quote-line
% echo 'don'\''t do that; type '\''rm -rf \*'\'', with a \ before the *.'
don't do that; type 'rm -rf \*', with a \ before the *.

Let’s say you want to create an alias to do this echo command. This can be done by recalling the line with Ctrl+P and pressing Esc+' again:

Example 11.18: Re-quote recalled echo
% 'echo '\''don'\''\'\'''\''t do that; type '\''\'\'''\''rm -rf
\*'\''\'\'''\'', with a \ before the *.'\'''

and then move to the beginning and add the command to create an alias.

Example 11.19: Quoted line as alias
% alias zoof='echo '\''don'\''\'\'''\''t do that; type '\''\'\'''\''rm
-rf \*'\''\'\'''\'', with a \ before the *.'\'''
% zoof
don't do that; type 'rm -rf \*', with a \ before the *.

If one of these fancy editor commands changes your command line in a way you did not intend, you can undo changes with Ctrl+_, if you can get it out of your keyboard, or Ctrl+X Ctrl+U, otherwise.

Another use of the editor is to edit the value of variables. For example, an easy way to change your path is to use the vared command:

Example 11.20: Edit PATH with vared
% vared PATH
> /u/pfalstad/scr:/u/pfalstad/bin/sun4:/u/maruchck/scr:/u/subbarao/bin:/u/maruc
hck/bin:/u/subbarao/scripts:/usr/princeton/bin:/usr/ucb:/usr/bin:/bin:/usr/host
s:/usr/princeton/bin/X11:/./usr/lang:/./usr/etc:/./etc

You can now edit the path. When you press return, the contents of the edit buffer will be assigned to PATH.