Fix real hardware/busybox breakage: dead compgen dependency, unreliable sourced-path detection, and fork-heavy pipelines in fx sources; verified against busybox ash+awk directly
This commit is contained in:
Regular → Executable
Regular → Executable
+19
-9
@@ -12,8 +12,15 @@
|
||||
# fx some-command --that --might --fail
|
||||
#
|
||||
# Env vars:
|
||||
# FX_SOURCES_DIR override the sources directory (default: sources.d
|
||||
# next to this script)
|
||||
# FX_SOURCES_DIR the sources directory. Defaults to ./contrib/fx/sources.d
|
||||
# (i.e. source this file from the repo root). There is no
|
||||
# portable way for a *sourced* POSIX shell file to learn
|
||||
# its own path -- $0 is the enclosing shell's name, and
|
||||
# bash's BASH_SOURCE has no equivalent in dash/ash, so
|
||||
# this does not try to be clever about it. Anything
|
||||
# other than "source from repo root" (a different cwd,
|
||||
# a temp dir a la bootstrap.sh) must set this explicitly
|
||||
# *before* sourcing this file.
|
||||
# FX_AUTO if set, auto-pick the top candidate instead of
|
||||
# showing a picker
|
||||
# FX_NO_PROBE if set, skip fx's own diagnostic run of the command
|
||||
@@ -32,14 +39,17 @@
|
||||
# Adding a source: drop a new executable file in sources.d/ that follows
|
||||
# the contract above. Nothing else to register or edit.
|
||||
|
||||
# $0 is the enclosing shell's name when this file is *sourced*, not this
|
||||
# file's own path -- ${BASH_SOURCE[0]} is reliable under bash; for other
|
||||
# POSIX shells, set FX_SOURCES_DIR explicitly before sourcing this file.
|
||||
_fx_self="${BASH_SOURCE:-$0}"
|
||||
FX_SOURCES_DIR="${FX_SOURCES_DIR:-$(CDPATH= cd -- "$(dirname -- "$_fx_self")" 2>/dev/null && pwd)/sources.d}"
|
||||
unset _fx_self
|
||||
# Resolved to an absolute path *now*, at source time -- a relative
|
||||
# default would silently re-resolve against whatever cwd happens to be
|
||||
# active later when fx() is actually called, which is almost never the
|
||||
# repo root in practice.
|
||||
FX_SOURCES_DIR="${FX_SOURCES_DIR:-$(pwd)/contrib/fx/sources.d}"
|
||||
|
||||
fx() {
|
||||
if [ ! -d "$FX_SOURCES_DIR" ]; then
|
||||
echo "fx: FX_SOURCES_DIR '$FX_SOURCES_DIR' not found -- no sources will fire." >&2
|
||||
echo "fx: set FX_SOURCES_DIR before sourcing fx.sh if not running from the repo root." >&2
|
||||
fi
|
||||
cmd="$*"
|
||||
|
||||
FX_OUTPUT=""
|
||||
@@ -55,7 +65,7 @@ fx() {
|
||||
candidates=$(
|
||||
for src in "$FX_SOURCES_DIR"/*; do
|
||||
[ -x "$src" ] || continue
|
||||
label=$(basename "$src")
|
||||
label="${src##*/}"
|
||||
"$src" "$cmd" 2>/dev/null | while IFS= read -r line; do
|
||||
[ -n "$line" ] && printf '[%s] %s\n' "$label" "$line"
|
||||
done
|
||||
|
||||
@@ -3,9 +3,23 @@
|
||||
# typed and moved on from before (thefuck's history.py idea). Reads
|
||||
# HISTFILE directly since a separate process doesn't inherit the parent
|
||||
# shell's in-memory history.
|
||||
#
|
||||
# Single awk pass over the tail of the file: on weak/emulated hardware
|
||||
# every avoided fork+exec matters (measured: an unoptimized PATH walk in
|
||||
# 40-pathfuzzy.sh took >30s on this box before being fixed; pipeline
|
||||
# depth has the same tax, just smaller per-hop) -- a five-process
|
||||
# tail|grep|grep|awk|tail chain does the same job as one awk script
|
||||
# reading the file once.
|
||||
cmd="$1"
|
||||
first_word=$(printf '%s' "$cmd" | awk '{print $1}')
|
||||
[ -n "$first_word" ] || exit 0
|
||||
hf="${HISTFILE:-$HOME/.bash_history}"
|
||||
[ -r "$hf" ] || exit 0
|
||||
tail -n 300 "$hf" 2>/dev/null | grep -F -- "$first_word " | grep -vF -- "$cmd" | awk '!seen[$0]++' | tail -5
|
||||
|
||||
tail -n 300 "$hf" 2>/dev/null | awk -v first="$first_word" -v full="$cmd" '
|
||||
index($0, first " ") == 1 && $0 != full && !seen[$0]++ { buf[++n] = $0 }
|
||||
END {
|
||||
start = (n > 5) ? n - 4 : 1
|
||||
for (i = start; i <= n; i++) print buf[i]
|
||||
}
|
||||
'
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
# correctly across branches/arguments in practice. One taught fix this
|
||||
# way covers every future self-diagnosing failure from that program, not
|
||||
# just the one instance that happened to trigger the teaching prompt.
|
||||
#
|
||||
# The emitted function runs on every future call to that program for
|
||||
# the rest of the session, so its own internal extraction is one awk
|
||||
# pass, not a grep|sed|tail chain -- three fewer forks on every failure
|
||||
# it ever handles, not just this first one.
|
||||
cmd="$1"
|
||||
prog=$(printf '%s' "$cmd" | awk '{print $1}')
|
||||
[ -n "$prog" ] || exit 0
|
||||
@@ -19,5 +24,5 @@ prog=$(printf '%s' "$cmd" | awk '{print $1}')
|
||||
printf '%s\n' "$FX_OUTPUT" | grep -qE "^[[:space:]]*${prog}[[:space:]]" || exit 0
|
||||
|
||||
cat <<FIX
|
||||
${prog}(){ out=\$(command ${prog} "\$@" 2>&1); ec=\$?; if [ \$ec -ne 0 ]; then sug=\$(printf '%s\\n' "\$out" | grep -E "^[[:space:]]*${prog}[[:space:]]" | sed 's/^[[:space:]]*//' | tail -1); if [ -n "\$sug" ]; then eval "command \$sug"; return \$?; fi; fi; printf '%s\\n' "\$out"; return \$ec; }
|
||||
${prog}(){ out=\$(command ${prog} "\$@" 2>&1); ec=\$?; if [ \$ec -ne 0 ]; then sug=\$(printf '%s\\n' "\$out" | awk '/^[ \\t]*${prog}[ \\t]/{sub(/^[ \\t]*/,""); s=\$0} END{print s}'); if [ -n "\$sug" ]; then eval "command \$sug"; return \$?; fi; fi; printf '%s\\n' "\$out"; return \$ec; }
|
||||
FIX
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
# PATH-fuzzy source: if the first word isn't a known command, alias, or
|
||||
# function, offer a function wrapping the closest-spelled real command
|
||||
# as a candidate -- as a fix you approve through the normal f() flow,
|
||||
@@ -7,6 +7,12 @@
|
||||
# otherwise), so an alias-based fix silently does nothing when f/fx run
|
||||
# inside a script.
|
||||
#
|
||||
# Pure POSIX: walks $PATH directly instead of `compgen -c`, which is a
|
||||
# bash-only builtin and does not exist under busybox ash -- this source
|
||||
# used to be dead weight on exactly the minimal/embedded shells this
|
||||
# project targets. Tested against busybox ash + busybox awk directly,
|
||||
# not just bash + gawk.
|
||||
#
|
||||
# Uses Damerau-Levenshtein-lite (edit distance + adjacent transposition)
|
||||
# so the canonical "gti" -> "git" typo scores 1, not 2 -- plain Hamming
|
||||
# or substitution-only distance ties it with unrelated 3-letter commands
|
||||
@@ -15,9 +21,11 @@ cmd="$1"
|
||||
first_word=$(printf '%s' "$cmd" | awk '{print $1}')
|
||||
[ -n "$first_word" ] || exit 0
|
||||
command -v "$first_word" >/dev/null 2>&1 && exit 0
|
||||
type "$first_word" >/dev/null 2>&1 && exit 0
|
||||
|
||||
compgen -c 2>/dev/null | sort -u | awk -v target="$first_word" '
|
||||
IFS=:
|
||||
for d in $PATH; do
|
||||
ls "$d" 2>/dev/null
|
||||
done | sort -u | awk -v target="$first_word" '
|
||||
function min3(a, b, c) { return (a < b ? (a < c ? a : c) : (b < c ? b : c)) }
|
||||
function distance(a, b, n, m, i, j, ca, cb, cost, tmp) {
|
||||
n = length(a); m = length(b)
|
||||
|
||||
Reference in New Issue
Block a user