Shell Tips

Shell Tips

Robbert Haarman

2020-12-01


Interactive Use

Editing the Command Line

Most interactive shells offer a variety of navigation and editing commands and associated key bindings. However, especially for long command lines, nothing beats a real text editor. Fortunately, you can invoke your text editor of choice to edit the command line!

Bash offers the function edit-and-execute-command, which is bound to C-x C-e by default. The command is executed as soon as the editing session finishes.

Zsh offers the function edit-command-line. This function can be made available by running the following commands (tip: put these in your .zshrc):

autoload -U edit-command-line
zle -N edit-command-line
bindkey '\C-x\C-e' edit-command-line

Of course, a different key binding can be chosen, but C-x C-e is unbound by default and matches what Bash uses. After editing, the command appears on the command line and can be edited or executed as usual.

Searching in History

One way to quickly re-run a previously executed command is by searching for it in history. Bash offers reverse-search-history, and Zsh offers history-incremental-search-backward, both of which are by default bound to C-r. The commands search history as you type. If you find the command you want to run, you can do so by pressing return. If you want to edit it first, you can use the movement keys to start editing it. If you want to abort the search, you can do so using C-g.

Issuing the history search command again while searching will go to the previous matching command. For example, if you had run the commands:


make foo.c
make bar.c

C-r make will find make bar.c. Pressing C-r again at that point will find make foo.c. If you find that you have gone too far back, you can reverse the search direction using C-x s.

Making the Prompt Stand Out

With commands that output lots of text, it can sometimes be hard to see where the output from a command starts or ends. To make this easier, you can make the prompt stand out by, for example, making it a different color. For example, in Zsh, you could do:

export PS1='%F{cyan}%n%f@%F{cyan}%m %1~%f$ '

Here, %F{cyan} sets the foreground color to cyan, and %f sets it back to its default value. The expressions %n, %m, and %1~ refer to the username, the machine name, and the current directory, respectively, with only the last part of the current directory displayed, and ~ substituted for the home directory. This gives a prompt that looks like:

output from some command is here
and some more text
user@host ~$ some command
the second command also
outputs multiple lines of text
user@host ~$ 

Indicating Failed Commands in the Prompt

Some programs do not exactly make it clear if they succeeded or failed. To make this visually apparent, you can configure your shell to display exit status in the prompt if the previous command failed. In Zsh, you can use something like %(?..%F{red}(%?%)%f ) to show the exit status in parentheses and in red if it was not equal to 0. For example, my prompt looks like:

user@host ~$ true
user@host ~$ false
(1) user@host ~$ 

The expression above parses as: If the exit status is equal to 0 (%(?), then (.) expand to the empty string, else (.) expand %F{red}(%?%)%f , and a close paren ()) to end the conditional.

The expression inside the else-part of the conditional parses as: Set the foreground color to red (%F{red}), an open paren ((), the last command's exit status (%?), a close paren (%)), a space, and set the foreground color back to normal (%f). The close paren needs to have a percent sign in front of it to avoid being interpreted as the close paren of the conditional.

Combined with the tip about using color to make the prompt stand out, this leads to a PS1 of '%(?..%F{red}(%?%)%f )%F{cyan}%n%f@%F{cyan}%m %1~%f$ ', which is some of the less legible code I've ever written.

Previous Directory

Need to return to the directory you were just in? cd - (cd minus) will do that. This works in Bash and Zsh and probably other shells.

If you know in advance you are going to have to return to the directory you are currently in, you can use pushd instead of cd. This puts the current directory on a stack. You can return to the most recently pushed directory using popd, or you can inspect the stack using dirs. In Zsh, dirs -v shows numbers with the directories on the stack, and you can return to a directory by number by using cd +number, where number is the number shown by dirs -v. Here is an example:


user@host ~$ : Initially, dirs only shows the current directory
user@host ~$ dirs
~
user@host ~$ : Do some work on a project
user@host ~$ cd project/src
user@host src$ : Build it and look at the documentation
user@host src$ pushd ..
user@host project$ make
user@host project$ pushd doc/html
user@host html$ w3m index.html
user@host html$ : Take a look in /tmp
user@host html$ pushd /tmp
user@host /tmp$ : Find source directory in the stack
user@host /tmp$ dirs -v
0       /tmp
1       ~/project/doc/html
2       ~/project
3       ~/project/src
user@host /tmp$ cd +3
~/project/src
user@host src$ dirs -v
0       ~/project/src
1       ~/project/doc/html
2       ~/project

If you find this useful, you may also want to use setopt autopushd, which makes cd push the current directory onto the stack (like pushd does). That way, you can change directories without having to think about if the current directory is one you might want to return to; it's always remembered.

Valid XHTML 1.1! Valid CSS! Viewable with Any Browser