#!/bin/sh set -e die() { fmt=$1 shift printf "nextbin %s failed: $fmt\n" "$orig_exe" "$@" >&2 exit 1 } usage() { # shellcheck disable=SC2016 echo 'usage: nextbin [-v] "$0" "$@"' >&2 } case "$1" in -v) action="echo"; shift;; -*|'') usage; exit 1;; *) action="exec" esac orig_exe=$1 shift exe_name=${orig_exe##*/} case ":$PATH:" in ::) die 'empty PATH not supported' ;; *:[!/]*) die 'relative PATH elements not supported' ;; *:*/./*:*|*:*/../*:*) die 'PATH elements containing . or .. not supported' ;; esac case $orig_exe in # theoretically this could be optimized but it's negligible cost */*) orig_dir=$(cd -L "${orig_exe%/*}" && pwd -L) ;; *) orig_dir=$PWD ;; esac # clean up path. theoretically we could allow e.g. PATH=/dir://dir but which # would we match for cd /dir; ./exe tmp_path=$(printf '%s\n' ":$PATH:" | sed -e 's|/*:|:|g' -e 's|//|/|g') case $tmp_path in *:$orig_dir:$orig_dir:*|*:$orig_dir:*:$orig_dir:*) die 'duplicate entry in PATH: %s' "$orig_dir" esac new_path=${tmp_path#*":$orig_dir:"} new_path=${new_path%:} exe=$(PATH="$new_path" command -v "$exe_name") || true [ -n "$exe" ] || die 'PATH lookup failed using %s' "$new_path" $action "$exe" "$@"