Quick hack: automatically resolve "package not found" with guix in zsh

I rely on guix shell $pkg -- $pkg a lot to resolve situations where $pkg is not found on my system:

wilko@rembrandt ~$ hello
bash: hello: command not found
wilko@rembrandt ~$ guix shell hello -- hello
Hello, world!

but always having to type guix shell $pkg -- $pkg is quite cumbersome. The comma-hack Ludo shared a few days ago (Ludovic Courtès : « # #Guix trick borrowed from Nix folks: the comma … » - Aquilepouet) would reduce this to , $pkg which already seems better. So I took this as inspiration and came up with a command_not_found_handler:

command_not_found_handler() {
    printf "zsh: %s: command not found (trying to resolve)\n" "$1" >&2
    pkg="$(guix locate "$1" 2>/dev/null | grep /bin/ | head -1 | cut -d@ -f1)"
    guix shell ${pkg:-$1} -- "$@" || {
        return 127
    }
}

in my .zshrc, that:

  • checks if a package providing a command is available in the local store (as guix locate is only able to query packages that are present there).
  • if it’s available it calls guix shell with the corresponding package, if it’s not, it tries to do guix shell $pkg -- $pkg as a last resort; if both fails it exits non-zero with 127 as its exit-code (standard behaviour of the original command_not_found_handler).
  • so a command which is handled by this is either resolved and run by guix shell or ultimately not found.

In practice it looks like this:

[/home/wilko/Downloads]% hello
zsh: hello: command not found (trying to resolve)
Hello, world!

for packages that are already in store but not available in the current context. And:

[/home/wilko/Downloads]% xsv
zsh: xsv: command not found (trying to resolve)
substitute: updating substitutes from 'https://bordeaux.guix.gnu.org'... 100.0%
The following derivation will be built:
  /gnu/store/38r81gvvwav6bfwa7hjga6afh16in33p-profile.drv

0.0 MB will be downloaded
 xsv-0.13.0  936KiB                                                                                                                                                                                  1.5MiB/s 00:01 ▕██████████████████▏ 100.0%
building CA certificate bundle...
listing Emacs sub-directories...
building fonts directory...
building directory of Info manuals...
building profile with 1 package...
xsv is a suite of CSV command line utilities.

Please choose one of the following commands:
    cat         Concatenate by row or column
    count       Count records
    fixlengths  Makes all records have same length
    flatten     Show one field per line
    fmt         Format CSV output (change field delimiter)
    frequency   Show frequency tables
    headers     Show header names
    help        Show this usage message.
    index       Create CSV index for faster access
    input       Read CSV data with special quoting rules
    join        Join CSV files
    sample      Randomly sample CSV data
    search      Search CSV data with regexes
    select      Select columns from CSV
    slice       Slice records from CSV
    sort        Sort CSV data
    split       Split CSV data into many files
    stats       Compute basic statistics
    table       Align CSV data into columns

for packages that aren’t available locally and have to be fetched first.

I should add a disclaimer that this unfortunately doesn’t cover all cases, as sometimes binaries are named differently than the package itself or are found in different outputs of a package. So:

[/home/wilko/Downloads]% guix shell bind:utils -- dig +short systemcrafters.net 
188.114.96.3
188.114.97.3

works, but:

[/home/wilko/Downloads]% dig +short systemcrafters.net
zsh: dig: command not found (trying to resolve)
guix shell: error: dig: command not found
hint: Did you mean 'mdig'?

would not.

Hope this is helpful for other folks as well!

2 Likes