最近書いたシェルスクリプト

注:ロクにテストしていないのもあります。
もっといい方法があったら是非教えてください。

# 先頭から HEADLENGTH 行、末尾から TAILLENGTH 行を取り除く
# これは awk を使わないとちょっとつらい
# http://home.comcast.net/~j.p.h/cus-faq-2.html#19
body() {
	if [ $# -lt 2 ]; then
		echo "Usage: body HEADLENGTH TAILLENGTH [FILE...]"
		exit 1
	fi
	local b=$1; shift
	local e=$1; shift
	awk 'NR>'$b'{a[NR+'$e']=$0;if(NR in a) print a[NR]}' "$@"
}

# ファイルの更新時刻を一時保存しておき、
prestime() {
	touch -r "$1" /tmp/$1.time
}

# それを戻す
restime() {
	touch -r /tmp/"$1".time $1
	command rm /tmp/"$1".time
}

# GNU Development Tools より。
gcc-including-files() {
	`gcc -print-prog-name=cc1` -E -H -quiet "$@" > /dev/null
}

gcc-system-include-path() {
	cpp -v < /dev/null 2>&1 >/dev/null|sed -ne '/#include <.*starts here:/,/End of search list/p' | body 1 1
}

gcc-make-depend() {
	gcc -MM "$@"
}

gcc-make-depend-system() {
	gcc -M "$@"
}

# hoge.c をコンパイルするコマンドを Makefile から捜し出す
show-compile-command() {
	prestime "$1"
	touch "$1"
	command make -n | grep -e "$1"
	restime "$1"
}

# Makefile 通りのコマンドでコンパイルするとき、プリプロセスの結果どうなるか
gcc-preprocess() {
	local compile_command=`show-compile-command $1`
	if [ -z "$compile_command" ]; then
		echo "Don't know how to compile: $1" 1>&2
		return 1
	else
		local preprocess_command=`echo "$compile_command"|sed -e 's@cc@cc -E -C@;s@-c@@g;s@-S@@g;s@ -o *[^ ][^ ]*@@g'`
		echo "$preprocess_command" 1>&2
		eval "$preprocess_command"
	fi
}

# Makefile 通りのコマンドでコンパイルするときのマクロ定義
gcc-show-macros() {
	local compile_command=`show-compile-command $1`
	if [ -z "$compile_command" ]; then
		echo "Don't know how to compile: $1" 1>&2
		return 1
	else
		local preprocess_command=`echo "$compile_command"|sed -e 's@cc@cc -EC -dM@;s@-c@@g;s@-S@@g;s@ -o *[^ ][^ ]*@@g'`
		echo "$preprocess_command" 1>&2
		eval "$preprocess_command"
	fi
}

# ついでに make の出力中の Error と Warning を色付けするようにした
# GNU sed じゃないと case insensitive マッチはできないようだ。
# 引数で指定したパターンを色付けするシェル関数を作っておくといいと思う。
e_normal=`echo -e "\033[0;30m"`
e_RED=`echo -e "\033[1;31m"`
function make() {
	local cdpathold="$CDPATH"; unset CDPATH
    LANG=C command make "$@" 2>&1 | tee out 2>/dev/null | sed -e "s@[Ee]rror@$e_RED&$e_normal@g" -e "s@[Ww]arning@$e_RED&$e_normal@g"
	CDPATH="$cdpathold"
}

#!/bin/sh -x

# vim をデバッグするときはオブジェクトファイルを別なディレクトリに
# 出力してほしかったので、リリース用とデバッグ用を手軽に切替えるために作った。
# もっと汎用的でいい方法はないものだろうか。

progname=`basename "$0"`

usage() {
	echo "Usage:   $progname OBJDIR BUILDTYPE"
	echo "Example: $progname objects debug"
	echo "    Known build types are 'debug' and 'release'"
}

if [ $# -ne 2 ]; then
	usage
	exit 1
fi

dirbasename=`echo "$1"|sed -e 's@/$@@'`
[ ! -d "$dirbasename" ] && echo "Not directory : $dirbasename" && exit 2

case "$2" in
	debug|release)
		realdir="$dirbasename"."$2";;
	*)
		echo "Unknown build type."
		exit 3
esac

if [ ! -d "$realdir" ]; then
	mkdir "$realdir" || exit 4
fi

if [ -h "$1" ]; then
	rm "$1"
	ln -s "$realdir" "$dirbasename"
else
	mv "$1" "$dirbasename".bak || exit 5
	ln -s "$realdir" "$dirbasename"
fi

他にも

$ mv hoge sage peso/

としたとき、履歴を戻って

$ unmv hoge sage peso/

と直して実行すれば元の場所へ戻るようなものを作ったが、

$ mv * peso/

であっけなく破綻することに気づいてがっかり。