UNIX tips

Last updated on Mon Jul 26 12:57:00 CEST 2021.

This page is a collection of simple tips for working with and on UNIX that don't deserve a page of their own.

sh: Operate on argv

The following sh snippet modifies the members of $@ by processing them with COMMAND. It successfully retains the structure of $@ and handles whitespace correctly.

i=0
while [ $((++i)) -le $# ]; do
	a=`COMMAND $1`
	shift
	set -- "$@" "$a"
done

vi: Useful search and replace

The quickest way to move the cursor to a specific place in the document is to use the search commands, // and ??. Remember that // and ?? can be used as motions, too: d/x^M deletes everything until the next occurrence of "x".

However, you often want to delete everything before "x" as well as "x" itself. While vi offers no built-in command for this, the following mapping often works well enough [1]:

map ^M :s///^M

That maps the Return key to delete the first occurrence of the previously searched phrase on the current line. It works as long as "x" is sufficiently unique.

Fortunately, you're likely to notice if it deletes the wrong thing, as (at least my implementation of) vi will move you to the beginning of the line if it deletes anything before the cursor.

If you really want a command that reliably deletes the found phrase, and not the same phrase earlier in the same line, the following should do the trick:

map ^M i^M^[:s///^Mk:j^Ml

Note that it doesn't work if you move after your search. Another drawback is that it requires multiple undos to undo.

grep: Find function definitions

Well-formatted C code writes function definitions like this:

int
main(...)
{
	...
}

This makes it easy to find function definitions with regular expressions. The following command finds the definition of the parse function, searching all C source files in the current directory:

$ grep -n ^parse\( *.c
util.c:342:parse(char *s)

For quick selection of common source files, I recommend adding a few variable definitions in .shrc:

a='* */*'
c='*.[ch] */*.[ch]'
p='*.p[lm] */*.p[lm]'

For example, the following invocation searches through all C source and header files in the current directory and all subdirectories:

grep -n ^Render $c

To make this pattern even more useful, I use a script called dwim [2] to translate the results (util.c:342) to the corresponding vi invocation:

$ dwim util.c:342 # runs "vi +342 util.c"

vi: Navigate troff documents

To write and format printed documents, I mainly use troff, but the idiosyncratic syntax sometimes makes troff source code difficult to navigate.

Fortunately, vi supports troff syntax by default:

There are two options called paragraphs and sections, which define the names of the matching macros:

set para=IPLPPPQPP\ LIpplpipbp
set sect=NHSHH\ HUnhsh

Note that it only supports matches with one or two characters. Note also the (escaped) spaces for specifying one-character macros.

xterm: Reduce start time

xterm is my personal favorite among terminal emulators, but it takes a substantial amount of time to start (ca 0.40s) if UTF-8 is enabled. The solution is to launch xterm with UTF-8 disabled, but enable it afterwards.

Compare:

$ time LC_ALL=en_US.UTF-8 xterm -e exit
    0.38s real     0.17s user     0.04s system
$ time LC_ALL=en_US.ISO8859-1 xterm -e exit
    0.15s real     0.06s user     0.05s system

UTF-8 can be enabled by sending a special control sequence to xterm:

$ echo -n '^[%G' # set encoding to utf-8
$ echo -n '^[%@' # set encoding to iso8859-1

(^[ represents a literal escape character.)

The following C program does exactly that:

#include <err.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
	/* Enable UTF-8 encoding in xterm. */
	write(0, "\033%G", 3);
	if(argv[1]){
		execvp(argv[1], argv+1);
		err(1, "execvp");
	}
}

I call it u. You can use it like this:

$ xterm -e u ksh # or whichever shell or program

Git: Retrieve previous version of file

Here's something I often want to do, but which Git makes a bit awkward: checkout the previous version of a file without overwriting the current one.

To solve this problem, I've written a script called git-orig:

#!/bin/sh

# git-orig -- checkout original file

[ $# -lt 2 ] && { echo "usage: $0 head file [...]" 1>&2; exit 1; }

h=$1
shift
set -e

for f in "$@"; do
	mv -i "$f" "$f".new
done

git checkout "$h" -- "$@"
git reset "$@" >/dev/null

for f in "$@"; do
	mv -i "$f" "$f".orig
	mv "$f".new "$f"
done

With this script in your PATH, the following command checks out v_sentence.c as of 3f2c76b486dc55336cabdc38ae5dd3ad317b16a4:

$ git orig 3f2c76b486dc55336cabdc38ae5dd3ad317b16a4 v_sentence.c

The checked out file is named v_sentence.c.orig.

xterm: Avoid automatically scrolling to bottom

One of the things I love about Plan 9 is that its default console doesn't automatically scroll to the bottom of command output. Unfortunately, almost no terminal emulators for UNIX support this behavior.

Or so I thought! It turns out that xterm has a resource named allowScrollLock, which is false by default. If you set it to true, you can press Scroll Lock to temporarily disable xterm's automatic scrolling.

This doesn't exactly mirror Plan 9's behavior, as Scroll Lock prevents the screen from scrolling on key press, but it's good enough for most cases.


References

  1. Character combinations beginning with a caret (^) denote control characters. These can be entered into vi by prefixing the control sequence with ^V.
  2. http://git.ankarstrom.se/dwim/