Err. I thought Olive was a shade of green (and it is Cthulhu's favourite colour, I think.). That looks distinctly yellow to me.
Some more before I lose my DSL and go off-line (moving to new flat):
- Finding files with a set of names you want is easy:
find PATH \( -name '*.c' -o -name '*.h' \)
, but recently, I had to find some filenames which began with mod_
and were not either .c
or .obj
files. Here's how I did it:
# Find files with a common name, excluding those with a specific
# extension. For example, find all mod_* files which don't end
# in *.c or *.obj
$ find /usr/src/ -iname mod_* -a \! \( -iname *.c -o -iname *.obj \)
...output elided...
$
The -iname
option is for a case-insensitive pattern match.
- Read single characters in a POSIX shell:
$ cat ~/bin/bl.sh
#!/bin/sh
#
# Set LCD backlight on *BSD.
RC=1 # default is failure.
trap 'stty -cbreak echo; exit $RC' exit hup int quit term
stty cbreak -echo
while :
do c=$(dd bs=1 count=1 status=none)
case $c in
\<|,|\-) intel_backlight decr
;;
\>|.|\+) intel_backlight incr
;;
q) RC=0 # exit success
break
;;
esac
done
bash(1)
has a read -n 1 -s
which does the same thing.
- Change directories conveniently using abbreviations:
The q()
function:
#
# Don't execute this file; source it.
#
# Bourne-shell string quoting
# turns:
# hello world -> 'hello world'
# hello 'world' -> 'hello '\'world\'
# \hello "$world" -> '\hello "$world"'
q() {
awk -f - -- "$@" <<\EoF
BEGIN {
PAT = "^[-_./[:alnum:]]+$"
for (i = 1; i < ARGC; i++) {
s = ARGV[i]
while (n = index(s, "'")) {
ss = substr(s, 1, n-1)
if (match(ss, PAT))
S = S ss
else if (length(ss))
S = S "'" ss "'"
S = S "\\'"
s = substr(s, n+1)
}
if (match(s, PAT))
S = S s
else if (length(s) || !length(ARGV[i]))
S = S "'" s "'"
S = S ((i < ARGC-1) ? " " : "")
}
printf "%s", S
}
EoF
}
The ch
function:
# Change directory using abbreviations.
#
# To use this function, source this file.
#
# Test dirs:
# for n in {100..150}; do mkdir $' \t\n'$n$'\n"d\'\i"$r * * * \n\n'; done
. ~/bin/q.sh
ch()
{
local arg dirs d i num OIFS
# short-circuits
if [ $# -eq 0 ]
then cd
return $?
fi
if [ "$1" = - ]
then cd -
return $?
fi
arg=$1
OIFS=$IFS
IFS=
for d in $(printf %s "$arg" | sed -Ee 's,/+,/,g; s,[^/]+,&*,g')
do IFS=$OIFS
test -d "$d" || continue
dirs="$dirs $(q "$d")"
done
eval "set -- $dirs"
# no match
if [ $# -eq 0 ]
then printf >&2 '%s: %s: no match\n' "$0" "$arg"
return 1
fi
# single match
if [ $# -eq 1 ]
then cd -- "$1"
return $?
fi
# multiple matches
i=0
for d
do i=$((i + 1))
printf '%d) %s\n' $i "$d"
done
printf '%s\n? ' '-----'
read num
# do nothing if no number
if [ -z "$num" ]
then return 0
fi
# invalid number
if [ "$num" -lt 1 ] || [ "$num" -gt $# ]
then printf >&2 '%s: %d: bad number\n' "$0" "$num"
return 1
fi
eval "cd -- \"\${$num}\""
}
Use as follows:
work$ source ~/bin/ch.sh
work$ ch /u/l/b
1) /usr/libexec/bsdconfig
2) /usr/libexec/bsdinstall
3) /usr/local/bin
-----
? 3
/usr/local/bin$
Use more chars. to disambiguate and avoid the prompt. The ch
function should work even if the directory name contains blanks and new-lines. Report bugs if you find them.