.local/bin:filterHistory: Fix bug and refactor

Fix bug that `cut` only took the second field when splitting on ';'
instead of everything after the first semicolon and thus not properly
handling commands that contain semicolons.

Add die() and hist-sort().
Use here-string instead of process substitution with input redirection.
Rephrase comment.
This commit is contained in:
2020-10-26 14:47:14 +01:00
parent 67c30e9842
commit a1990a751c

View File

@@ -8,25 +8,32 @@
## format of zshs extended history.
## An automatic backup is created before deleting that can be used for recovery.
[[ $# -eq 1 ]] || { echo "Specify history file" >&2; exit 1; }
[[ -e "$1" ]] || { echo "File does not exist" >&2; exit 1; }
[[ "$(stat -c '%a' "$1")" = "600" ]] || { echo "File does not look like a history file" >&2; exit 1; }
die() {
printf "$1" >&2
exit ${2:-1}
}
# Sort the commands per number of occurrences
most_used="$(\
cut -d';' -f2 "$1" \
[[ $# -eq 1 ]] || die "Specify a history file.\n"
[[ -e "$1" ]] || die "File does not exist.\n"
[[ "$(stat -c '%a' "$1")" = "600" ]] || die "File permissions are off.\n"
# Take a history file per stdin and sort the commands per number of occurrences
hist-sort() {
cut -d';' -f2- \
| sort \
| uniq -c \
| sort -nr \
)"
| sort -nr
}
most_used="$(hist-sort <"$1")"
declare -a commands
if command -v fzf >/dev/null 2>&1; then
# Let the user pick in fzf from the most used commands
readarray -t commands < <(\
readarray -t commands <<<"$(
fzf -m --reverse --height 50% <<<"$most_used" \
| sed -E 's/^[ \t]*[0-9]+ //' \
)
| sed -E 's/^[ \t]*[0-9]+ //'
)"
else
echo "Most used commands:"
offset="$(head -1 <<<"$most_used" | sed -E 's/^( *[0-9]+).*$/\1/' | wc -m)"
@@ -46,7 +53,7 @@ fi
printf '%s\n' "${commands[@]}" | column -x
echo "Please confirm the deletion of these commands in $1 ('yes')"
read yn
[[ "$yn" = "yes" ]] || exit 1
[[ "$yn" = "yes" ]] || die
tempd="$(mktemp -d)"
cp "$1" "$tempd/$(basename "$1")"
@@ -55,16 +62,15 @@ echo
for c in "${commands[@]}"; do
# Escape all characters that sed could misinterpret.
pattern="^: [0-9]+:[0-9]+;$(sed -E 's/[./?+|()*\\^$]|\[|\]/\\&/g' <<<"$c")\$"
# Find first occurrence of the command in the history file
first="$(grep -Enm1 "$pattern" "$1" | cut -d: -f1)"
pat="^: [0-9]+:[0-9]+;$(sed -E 's/[./?+|()*\\^$]|\[|\]/\\&/g' <<<"$c")\$"
# Find line number of first occurrence of the command in the history file
first="$(grep -Enm1 "$pat" "$1" | cut -d: -f1)"
# Delete all lines beginning from the line after the first occurrence that
# contain (exactly) the command.
sed -Ei "$((first + 1)),\$ { /$pattern/d }" "$1"
# Delete all occurrences of the command except for the first one
sed -Ei "$((${first} + 1)),\$ { /$pat/d }" "$1"
echo "$c deleted"
done
echo
echo "Following commands were deleted:"
diff "$1" "$tempd/$(basename "$1")" | grep "^>" | cut -d\; -f 2 | sort | uniq -c | sort -nr
diff "$1" "$tempd/$(basename "$1")" | grep "^>" | hist-sort