Skip to content

Instantly share code, notes, and snippets.

@hdonnay
Last active December 14, 2015 10:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hdonnay/5072125 to your computer and use it in GitHub Desktop.
Save hdonnay/5072125 to your computer and use it in GitHub Desktop.
ht, a hashtable in sh
#!/bin/sh
VERSION='1.0.2'
usage() {
cat >&2 <<EOF
$(basename $0) -- $VERSION
USAGE:
$(basename $0) [-r] [-d dir] { -o | [-v] KEY }
If stdin is attached, it will be inserted into key KEY. If it's not,
look up KEY. The 'v' switch (reVerse) will treat KEY as an md5 hash, and
look up all keys that have that content.
'd' specifies a hashtable directory.
The 'o' will output deplicated key/value pairs. If a tty is attached, output
will be pretty:
1 2
+----
| one
\`----
Otherwise it will be grepable:
1 2
one
.
That's tab separated keys on one line, the value, then a line with a single
'.' and an empty line.
The 'r' option will clear the hashtable after performing the given
action. Admittedly, something like
echo "value" | $(basename $0) -r key
is pretty useless.
NOTES:
The hashtable is implemented in flatfiles, and therefore probably isn't
highly performant.
The files live in '/tmp/$(basename $0)', interesting files are:
* map
* (md5 hash)
EOF
}
clean() {
cut -f 2 < "$dir/map" | sort | uniq > "$dir/.ref"
ls -1 $dir | grep -v map > "$dir/.ex"
paste -d \\n "$dir/.ref" "$dir/.ex" | sort | uniq -u | \
awk "{print \"$dir/\"\$0}" | > "$dir/.clean"
[ `wc -l < "$dir/.clean"` -gt 0 ] && xargs rm < "$dir/.clean"
rm "$dir/.ex" "$dir/.ref" "$dir/.clean"
}
out() {
[ ! -f "$dir/map" ] && exit 0
clean
for hash in `cut -f 2 < "$dir/map" | sort | uniq | paste -d ' '`; do
if [ -t 1 ]; then
awk "/\t$hash/{print \$1}" < "$dir/map" | paste | fmt
printf "+----\n"
awk '{print "| "$0}' < "$dir/$hash"
printf "\`----\n\n"
else
awk "/\t$hash/{print \$1}" < "$dir/map" | paste
cat "$dir/$hash"
printf ".\n\n"
fi
done
}
while getopts 'rvocd:h' OPT; do
case "$OPT" in
r) clr="y" ;;
v) rev="y" ;;
o) out="y" ;;
d) dir="$OPTARG" && shift ;;
h|?) usage && exit 1 ;;
esac
shift
done
dir="${dir:=/tmp/$(basename $0)}"
[ ! -d "$dir" ] && mkdir "$dir"
[ ! -f "$dir/map" ] && touch "$dir/map"
[ $out ] && { out && exit 0; }
[ $1 ] && key="$1" || { usage; exit 2; }
if [ ! -t 0 ]; then
(
flock 9
cat - > "$dir/.value"
hash=$(md5sum "$dir/.value" | awk '{print $1}')
[ ! -f "$dir/$hash" ] && cat "$dir/.value" > "$dir/$hash"
sed -i "/^$key\t/d" "$dir/map"
printf "%s\t%s\n" "$key" "$hash" >> "$dir/map"
printf "%s\n" "$hash"
rm "$dir/.value"
) 9>"$dir/lock"
exit 0
else
[ ! -f "$dir/map" ] && exit 0
if [ $rev ]; then
awk "/\t$key/" < "$dir/map" > "$dir/.format"
[ -s "$dir/.format" ] || { rm "$dir/.format" && exit 1; }
cut -f 1 < "$dir/.format" | paste
rm "$dir/.format"
else
cat "$dir/$(awk "/$key\t/" < "$dir/map" | cut -f 2 )"
fi
fi
[ $clr ] && rm -r "$dir"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment