From: Christian Goeschel Ndjomouo Date: Fri, 27 Feb 2026 23:29:27 +0000 (-0500) Subject: bash-completion: add improvements to unshare(1) completions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=79917c42728860186c729a0ab94a00ab135929b5;p=thirdparty%2Futil-linux.git bash-completion: add improvements to unshare(1) completions This change adds the ability to complete the command of the executable that the unshare command is going to execute. Additionally, it includes support for completions of long options that have optional arguments and are thus specified with a suffixed '=' sign, which delimits the option name from the actual option argument. Lastly, it also adds file/directory completions for options that require file or directory paths as arguments. Addresses: #4073 Signed-off-by: Christian Goeschel Ndjomouo --- diff --git a/bash-completion/unshare b/bash-completion/unshare index fdd40e5cb..75fe07eda 100644 --- a/bash-completion/unshare +++ b/bash-completion/unshare @@ -1,64 +1,110 @@ _unshare_module() { - local cur prev OPTS - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - case $prev in - '--propagation') - COMPREPLY=( $(compgen -W "slave shared private unchanged" -- $cur) ) - return 0 - ;; - '-s'|'--setgroups') - COMPREPLY=( $(compgen -W "allow deny" -- $cur) ) - return 0 - ;; - '-h'|'--help'|'-V'|'--version') - return 0 - ;; - esac - case $cur in - -*) - OPTS="--mount - --uts - --ipc - --net - --pid - --user - --cgroup - --time - --fork - --forward-signals - --kill-child - --keep-caps - --mount-proc - --map-current-user - --map-root-user - --owner - --propagation - --setgroups - --help - --version - --root - --wd - --load-interp - --map-auto - --map-group - --map-groups - --map-user - --map-users - --map-subids - --mount-binfmt - --monotonic - --boottime - --setuid - --setgid" + local cur prev words cword + local OPTS NOARGOPTS OPTARGOPTS REQARGOPTS REQARGOPTS_SHORT NOARGOPTS_SHORT + _init_completion -n = -- "$@" || return + + OPTARGOPTS="--mount=|--uts=|--ipc=|--net=|--pid=|--user=|--cgroup=|--time=|--kill-child=|\ + --mount-proc=|--mount-binfmt=" + + REQARGOPTS="--owner|--propagation|--setgroups|--root|--wd|--load-interp|--map-group|\ + --map-groups|--map-user|--map-users|--monotonic|--boottime|--setuid|--setgid" + + REQARGOPTS_SHORT="-l|-R|-w|-S|-G" + + NOARGOPTS="--fork|--forward-signals|--map-root-user|--map-current-user|--map-auto|\ + --map-subids|--keep-caps|--help|--version|${OPTARGOPTS//=/}" + + NOARGOPTS_SHORT="-r|-f|-c|-h|-V|-m|-u|-i|-n|-p|-U|-C|-T" + + OPTS="${NOARGOPTS//[|]/ } ${NOARGOPTS_SHORT//[|]/ } ${OPTARGOPTS//[|]/ }\ + ${REQARGOPTS//[|]/ } ${REQARGOPTS_SHORT//[|]/ }" + + # Check if we are still completing unshare(1) and set + # the offset so that it points to the command that is + # going to be executed, in order to complete it with + # its own completion specification. + local offset=0 i + for ((i = 1; i < cword; i++)); do + if [[ "${words[i]}" =~ ^--$ ]]; then + ((i++)) + elif [[ "${words[i]}" =~ ^($NOARGOPTS|$NOARGOPTS_SHORT)$ ]]; then + continue + elif [[ "${words[i]}" =~ ^($OPTARGOPTS).* ]]; then + continue + elif [[ "${words[i]}" =~ ^($REQARGOPTS|$REQARGOPTS_SHORT)$ ]]; then + [[ "${words[$((i+1))]}" != -* ]] && ((i++)) + continue + fi + offset=$i + break + done + + if (( offset > 0 )); then + # Find completion specification for the wrapped command + _command_offset $offset + else + # $OPTARGOPTS deserve special treatment due + # to the '=' that delimits the actual argument + # that we wish to complete and the option name + if [[ "$cur" =~ ^($OPTARGOPTS).* ]]; then + # Cut also backslash before '=' in case it ended up there + # for some reason. + prev=${cur%%?(\\)=*} + cur=${cur#*=} + + compopt -o nospace + case $prev in + '--kill-child') + COMPREPLY=( $(compgen -A signal -- $cur) ) + return 0 + ;; + *) + COMPREPLY=( $(compgen -f -d -- $cur) ) + return 0 + ;; + esac + fi + + case $prev in + '--propagation') + COMPREPLY=( $(compgen -W "slave shared private unchanged" -- $cur) ) + return 0 + ;; + '--setgroups') + COMPREPLY=( $(compgen -W "allow deny" -- $cur) ) + return 0 + ;; + '-w'|'--wd'|'-l'|'--load-interp'|'-R'|'--root') + # It's better than simply using 'compgen -f', + # because it honours spaces in filenames. + _filedir + return 0 + ;; + '-S'|'--setuid') + local uids + uids="$(awk -F: '{print $3}' /etc/passwd | sort --numeric-sort)" + COMPREPLY=( $(compgen -W "$uids" -- $cur) ) + return 0 + ;; + '-G'|'--setgid') + local gids + gids="$(awk -F: '{print $3}' /etc/group | sort --numeric-sort)" + COMPREPLY=( $(compgen -W "$gids" -- $cur) ) + return 0 + ;; + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return 0 - ;; - esac - compopt -o bashdefault - COMPREPLY=( $(compgen -c -- $cur) ) + else + COMPREPLY=( $(compgen -c -- "$cur") ) + fi + fi return 0 } -complete -F _unshare_module unshare +complete -F _unshare_module -o bashdefault -o default unshare