Last active
December 14, 2015 10:29
-
-
Save hdonnay/5072125 to your computer and use it in GitHub Desktop.
ht, a hashtable in sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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