Hacker News new | ask | show | jobs
by arp242 843 days ago
For zsh I use this to setup $PATH, which seems to address most of the use cases of this tool:

  ### Setup PATH
  dirs=(
      /usr/lib/ccache/bin                   # ccache
      ~/.local/script ~/.local/bin          # My local scripts.
      ~/.local/gobin                        # GOBIN
      /usr/local/bin /usr/local/sbin        # local takes precedence
      /bin /sbin /usr/bin /usr/sbin         # Standard Unix
      /usr/pkg/bin   /usr/pkg/sbin          # NetBSD
      /usr/X11R6/bin /usr/X11R6/sbin        # OpenBSD
      /opt/ooce/bin                         # illumos
      ~/.local/share/gem/ruby/*/bin(N[-1])  # Ruby
      ~/.luarocks/bin(N[-1])                # Lua
      ~/.local/dotnet/root ~/.local/dotnet/tools  # .NET
      /usr/lib/plan9/bin                    # Plan 9 from userspace
      ~/code/Vim/gopher.vim/tools/bin       # gopher.vim
      ~/.nimble/bin                         # Nim
      ~/.cargo/bin                          # Rust
      ~/.ghcup/bin                          # Haskell
  )
  typeset -U path=()                        # No duplicates
  # Use full path so /bin and /usr/bin aren't duplicated if it's a symlink.
  for d in $dirs:A; [[ -d $d ]] && path+=($d)
  unset dirs d
typeset -U makes the $path array "unique", and :A ensures the full path is always used (in case of symlinks).

You can probably do something similar with bash, but idk.

$path is tied to $PATH and an array, so you can use it as such for some of the other things:

  print ${(F)path} | grep /bin  # (F) to print one array per line
  print ${(F)path} | sort       # can also use (o) to print ordered
3 comments

I use 'path=( ${(u)^path:A}(N-/) )' instead of that for loop
To just reduce the current entries to those that exist, I'd probably do

    PATH=$(perl -e 'print join ":", grep -d, split ":", $ENV{PATH}')
but that's because as a 20 year perl hacker and thus connoiseur of line noise, I'd rather read that than the shell clever.

(this doesn't mean the shell clever is bad, it just means I don't find -that- dialect of line noise as skimmable)

General point about comfort noted, but they don't do the same thing.

$^path(N) is an equivalent to your perl expression. The snippet throwaway84846 posted also removes duplicates and collapses symlinks from /usr-merge for example.

Plan 9 from User Space binaries are on /usr/local/plan9/bin/ on OpenBSD.
How does (N[-1]) get resolved?
(N) sets the NULL_GLOB option for that pattern:

    % print doesnt-exist*
    zsh: no matches found: doesnt-exist*
    % print doesnt-exist*(N)
    (doesn't print anything, pattern is just ignored)
([n]) is array indexing to get entry n; -1 gets the last entry:

    % print a*
    a-1 a-2 a-3
    % print a*([-1])
    a-3
    % print a*([1])
    a-1
Globbing is automatically sorted by name.

So putting that together, things like this:

      ~/.local/share/gem/ruby/*/bin(N[-1])  # Ruby
will use the latest Ruby version in ~/.local/share/gem/ruby, and it won't error out if it doesn't exist (so I can freely copy this around even to machines without Ruby, or remove Ruby, etc).
Thanks for explaining, looks very clever. Is this syntax part of bash?
No, bash has nothing like this. It's all very specific to zsh. They're called "glob qualifiers" if you want to find out more.