bash-completion
bash-completion copied to clipboard
new occasions of unbound variables
Describe the bug
There are some new occasions of unbound variables, which AFAIU, #44 are now considered bugs.
To reproduce
Invoke "set -u" before invoking bash-completion in .bashrc. Launching a new shell, gives:
bash: filenames: unbound variable
bash: filenames: unbound variable
bash: filenames: unbound variable
Versions (please complete the following information)
- [ ] Operating system name/distribution and version: Debian sid
- [ ] bash version,
echo "$BASH_VERSION"
: 5.1.8(1)-release - [ ] bash-completion version,
(IFS=.; echo "${BASH_COMPLETION_VERSINFO[*]}")
: 2.11
Thanks for the report. Please follow instructions in the bug report template -- this does not happen for me nor in CI, load time debug trace is needed to diagnose further.
I'm guessing that the trace will reveal that the errors come from completions not included with bash-completion but some 3rd party ones, per the above, and because there is no variable named filenames
in the bash-completion source tree that I can see.
Indeed you're right.
I was just looking at the occurrence of filenames
in /usr/share/bash-completion/bash_completion
and once I've found it, didn't think about the possibility of 3rd party completions.
Reported the issues at their respective upstreams: [0] [1]
Sorry for the noise
Cheers, Chris.
Hey again.
This time I might have found a real occasion ;-)
If I have set -u and a command like:
find ../new/ -type f -printf '%f\n' | while IFS='' read -r FILENAME; do printf 'ln -s /dev/nu %q\n' "${FILENAME}" ; done | sh
now when I go to the /dev/nu
and complete it with tab to /dev/null I get:
../new/ -type f -printf '%f\n' | while IFS='' read -r FILENAME; do printf 'ln -s /dev/nubash: COMP_WORDS[i]: unbound variable
This seems to happen in:
+ offset=1
+ (( i = 1 ))
+ (( i <= COMP_CWORD ))
+ [[ printf != -* ]]
+ offset=1
+ break
+ _command_offset 1
+ local word_offset=1 i j
+ (( i = 0 ))
+ (( i < word_offset ))
+ (( j = 0 ))
+ (( j <= 55 ))
+ [[ do printf 'ln -s /dev/n %q\n' "${FILENAME}" ; done | sh == \d\o* ]]
+ break
+ COMP_LINE=' printf '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( COMP_POINT -= 2 ))
+ (( i++ ))
+ (( i < word_offset ))
+ (( i = 0 ))
+ (( i <= COMP_CWORD - word_offset ))
+ COMP_WORDS[i]=printf
+ (( i++ ))
+ (( i <= COMP_CWORD - word_offset ))
+ COMP_WORDS[i]=''\''ln -s /dev/n %q\n'\'''
+ (( i++ ))
+ (( i <= COMP_CWORD - word_offset ))
+ (( i ))
+ (( i <= COMP_CWORD ))
+ unset 'COMP_WORDS[i]'
+ (( i++ ))
+ (( i <= COMP_CWORD ))
+ (( COMP_CWORD -= word_offset ))
+ COMPREPLY=()
+ local cur
+ _get_comp_words_by_ref cur
+ local exclude flag i OPTIND=1
+ words=()
+ local cur cword words
+ upargs=()
+ upvars=()
+ local upargs upvars vcur vcword vprev vwords
+ getopts c:i:n:p:w: flag cur
+ [[ 1 -ge 1 ]]
+ case ${!OPTIND} in
+ vcur=cur
+ (( OPTIND += 1 ))
+ [[ 1 -ge 2 ]]
+ __get_cword_at_cursor_by_ref '' words cword cur
+ words=()
+ local cword words
+ __reassemble_comp_words_by_ref '' words cword
+ local exclude i j line ref
+ [[ -n '' ]]
+ printf -v cword %s 1
+ [[ -v exclude ]]
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s printf
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s ''\''ln -s /dev/n %q\n'\'''
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s '"${FILENAME}"'
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s ';'
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s done
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s '|'
+ for i in "${!COMP_WORDS[@]}"
+ printf -v 'words[i]' %s sh
+ local i cur= index=21 'lead= printf '\''ln -s /dev/n'
+ [[ 21 -gt 0 ]]
+ [[ -n printf 'ln -s /dev/n ]]
+ [[ -n printf'ln-s/dev/n ]]
+ cur=' printf '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( i = 0 ))
+ (( i <= cword ))
+ [[ 53 -ge 6 ]]
+ [[ print != \p\r\i\n\t\f ]]
+ cur='printf '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( index > 0 ))
+ (( index-- ))
+ [[ 52 -ge 6 ]]
+ [[ printf != \p\r\i\n\t\f ]]
+ (( i < cword ))
+ local old_size=52
+ cur=' '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ local new_size=46
+ (( index -= old_size - new_size ))
+ (( ++i ))
+ (( i <= cword ))
+ [[ 46 -ge 19 ]]
+ [[ 'ln -s /dev/n %q\n != \'\l\n\ \-\s\ \/\d\e\v\/\n\ \%\q\\\n\' ]]
+ cur=''\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( index > 0 ))
+ (( index-- ))
+ [[ 45 -ge 19 ]]
+ [[ 'ln -s /dev/n %q\n' != \'\l\n\ \-\s\ \/\d\e\v\/\n\ \%\q\\\n\' ]]
+ (( i < cword ))
+ (( ++i ))
+ (( i <= cword ))
+ [[ -n 'ln -s /dev/n %q\n' "${FILENAME}" ; done | sh ]]
+ [[ ! -n 'ln-s/dev/n%q\n'"${FILENAME}";done|sh ]]
+ (( index < 0 ))
+ local words cword cur
+ _upvars -a7 words printf ''\''ln -s /dev/n %q\n'\''' '"${FILENAME}"' ';' done '|' sh -v cword 1 -v cur ''\''ln -s /dev/n'
+ (( 15 ))
+ (( 15 ))
+ case $1 in
+ [[ -n 7 ]]
+ printf %d 7
+ [[ -n words ]]
+ unset -v words
+ eval 'words=("${@:3:7}")'
words=("${@:3:7}")
++ words=("${@:3:7}")
+ shift 9
+ (( 6 ))
+ case $1 in
+ [[ -n cword ]]
+ unset -v cword
+ eval 'cword="$3"'
cword="$3"
++ cword=1
+ shift 3
+ (( 3 ))
+ case $1 in
+ [[ -n cur ]]
+ unset -v cur
+ eval 'cur="$3"'
cur="$3"
++ cur=''\''ln -s /dev/n'
+ shift 3
+ (( 0 ))
+ [[ -v vcur ]]
+ upvars+=("$vcur")
+ upargs+=(-v $vcur "$cur")
+ [[ -v vcword ]]
+ [[ -v vprev ]]
+ [[ -v vwords ]]
+ (( 1 ))
+ local cur
+ _upvars -v cur ''\''ln -s /dev/n'
+ (( 3 ))
+ (( 3 ))
+ case $1 in
+ [[ -n cur ]]
+ unset -v cur
+ eval 'cur="$3"'
cur="$3"
++ cur=''\''ln -s /dev/n'
+ shift 3
+ (( 0 ))
+ (( COMP_CWORD == 0 ))
+ local cmd=printf compcmd=printf
++ complete -p printf
+ local 'cspec=complete -F _minimal printf'
+ [[ ! -n complete -F _minimal printf ]]
+ [[ ! -n complete -F _minimal printf ]]
+ [[ -n complete -F _minimal printf ]]
+ [[ _minimal printf != \c\o\m\p\l\e\t\e\ \-\F\ \_\m\i\n\i\m\a\l\ \p\r\i\n\t\f ]]
+ local 'func=_minimal printf'
+ func=_minimal
+ (( 7 >= 2 ))
+ _minimal printf sh '|'
+ local cur prev words cword split
+ _init_completion -s
+ local exclude= flag outx errx inx OPTIND=1
+ getopts n:e:o:i:s flag -s
+ case $flag in
+ split=false
+ exclude+==
+ getopts n:e:o:i:s flag -s
+ COMPREPLY=()
+ local 'redir=@(?([0-9])<|?([0-9&])>?(>)|>&)'
+ _get_comp_words_by_ref -n '=<>&' cur prev words cword
+ local exclude flag i OPTIND=1
+ words=()
+ local cur cword words
+ upargs=()
+ upvars=()
+ local upargs upvars vcur vcword vprev vwords
+ getopts c:i:n:p:w: flag -n '=<>&' cur prev words cword
+ case $flag in
+ exclude='=<>&'
+ getopts c:i:n:p:w: flag -n '=<>&' cur prev words cword
+ [[ 6 -ge 3 ]]
+ case ${!OPTIND} in
+ vcur=cur
+ (( OPTIND += 1 ))
+ [[ 6 -ge 4 ]]
+ case ${!OPTIND} in
+ vprev=prev
+ (( OPTIND += 1 ))
+ [[ 6 -ge 5 ]]
+ case ${!OPTIND} in
+ vwords=words
+ (( OPTIND += 1 ))
+ [[ 6 -ge 6 ]]
+ case ${!OPTIND} in
+ vcword=cword
+ (( OPTIND += 1 ))
+ [[ 6 -ge 7 ]]
+ __get_cword_at_cursor_by_ref '=<>&' words cword cur
+ words=()
+ local cword words
+ __reassemble_comp_words_by_ref '=<>&' words cword
+ local exclude i j line ref
+ [[ -n =<>& ]]
+ exclude='[=<>&]'
+ printf -v cword %s 1
+ [[ -v exclude ]]
+ line=' printf '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( i = 0, j = 0 ))
+ (( i < 7 ))
+ [[ 0 -gt 0 ]]
+ ref='words[0]'
+ printf -v 'words[0]' %s printf
+ line=' '\''ln -s /dev/n %q\n'\'' "${FILENAME}" ; done | sh'
+ (( i == COMP_CWORD ))
+ (( i++, j++ ))
+ (( i < 7 ))
+ [[ 1 -gt 0 ]]
+ [[ 'ln -s /dev/n %q\n' == +([=<>&]) ]]
+ ref='words[1]'
+ printf -v 'words[1]' %s ''\''ln -s /dev/n %q\n'\'''
+ line=' "${FILENAME}" ; done | sh'
+ (( i == COMP_CWORD ))
+ printf -v cword %s 1
+ (( i++, j++ ))
+ (( i < 7 ))
+ [[ 2 -gt 0 ]]
bash: COMP_WORDS[i]: unbound variable
Thanks, Chris.
Yup, reproduced, thanks.
I think I might have found another case:
scp someKnownHostname:fo<TAB>
bash: $1: unbound variable
also fails for me, when I try to complete
the remote pathnames.
I can't seem to be able to reproduce that one. Please file a new issue about that and complete the info requested in it so we can debug that one further.