[PATCH bash ordered autocomplete] Order the autocompletion in bash

J Rt jean.rblt at gmail.com
Wed Apr 1 11:17:37 CEST 2020


Interestingly on my machine:

- the version I sent last time works.

- the version with the local var taken away and the "" protection works:

_append_to_compreply () {
    local IFS=" "
    for word in "$(_sort_entries_string "$1")"; do
        COMPREPLY+=($word)
    done
}

- this does not work (with uncommented IFS it works as shown on the
previous point), so I think the IFS var is needed. :

_append_to_compreply () {
    #local IFS=" "
    for word in "$(_sort_entries_string "$1")"; do
        COMPREPLY+=($word)
    done
}

I agree with you, it is weird that IFS is needed. Not sure why (though
I have good hints below). Actually, when I implemented this solution
using string instead of arrays following your 1st code review, it took
me half an hour debugging to find that IFS was needed there otherwise
it does not work. Maybe I should have written to you about that.

I really did not understand initially why IFS is needed there, as
running a small test snippet on a new terminal on my machine shows I
have not 'broken' my environment (this is well space tab newline in
hex):

$ echo -n "$IFS" | xxd
00000000: 2009 0a                                   ..

By contrast this:

_append_to_compreply () {
    echo " "
    echo "IFS before"
    echo -n "$IFS" | xxd
    local IFS=" "
    echo "IFS after"
    echo -n "$IFS" | xxd
    for word in "$(_sort_entries_string "$1")"; do
        COMPREPLY+=($word)
    done
}

gives the following:

jrlab at jrlab-ThinkPad-T490:~$ echo -n "$IFS" | xxd
00000000: 2009 0a                                   ..
jrlab at jrlab-ThinkPad-T490:~$ pass
IFS before
00000000: 0a                                       .
IFS after
00000000: 20
^C
jrlab at jrlab-ThinkPad-T490:~$ echo -n "$IFS" | xxd
00000000: 2009 0a                                   ..

so it looks that even just calling pass TAB changes IFS to newline. I
looked in the code, IFS is set at quite a few places:

$ grep -n IFS pass.bash-completion
12:    local IFS=" "
26:    local IFS=$'\n'
82:    local IFS=$'\n'
97:    local IFS=$'\n'

$ grep -n IFS password-store.sh
125:                IFS=";" eval 'GPG_RECIPIENTS+=( $group )' #
http://unix.stackexchange.com/a/92190
358:            git_add_file "$gpg_id.sig" "Signing new GPG id with
${key//[$IFS]/,}."
413:    IFS="," eval 'echo "Search Terms: $*"'

I think this can explain things. I would have expected that 1) this
has nothing to do with password-store.sh as it is not called, only the
autocomplete script, and this fits well (though IFS instead of local
IFS is used in password-store.sh, it does not look like it gets set to
/n anyways), 2) the use of local protects IFS in pass.bash-completion,
where IFS does get set to /n a few times, but it is possible that even
using local does not help when IFS is already present from before?

Looking on SO: https://unix.stackexchange.com/questions/393928/global-local-variable-assignment

"
One way to think of it is to imagine that the local var1="local 1" has
the effect of saving the current value of var1, with a promise that at
the end of the function it will be restored, and then setting it to
"local 1". With this mental model you can then think of all variables
as global, and variables being restored at the end of functions.
"

So this may explain maybe that in functions that set a local IFS
different from the global one, calling the _append_to_compreply
function that relies on IFS is tricky, since the global IFS would be
restored only when exiting function? I think this is well what is
happening:

bash-4.4$ VAR="global"
bash-4.4$ print_var(){
> echo "$VAR"
> }
bash-4.4$ set_var(){
> print_var
> local VAR="local"
> print_var
> }
bash-4.4$ print_var
global
bash-4.4$ set_var
global
local
bash-4.4$ print_var
global

So local is tricky to use, and may give a false sense of security when
using local on a global variable, as the global value seems to be
restored only when exiting the function at the end of it, not when
entering a function inside the function....

I originally picked this up in order to train a bit and improve my
bash scripting level, I guess the more I write some bash, the more I
get afraid of further using it for slightly complex projects, it is so
tricky on many places.

On Tue, Mar 31, 2020 at 7:12 PM Reed Wade <reedwade at misterbanal.net> wrote:
>
> > --- a/src/completion/pass.bash-completion
> > +++ b/src/completion/pass.bash-completion
> > @@ -4,7 +4,21 @@
> >  # Brian Mattern <rephorm at rephorm.com>. All Rights Reserved.
> >  # This file is licensed under the GPLv2+. Please see COPYING for more information.
> >
> > +_sort_entries_string () {
> > +     echo $1 | tr ' ' '\n' | sort | tr '\n' ' '
>
> Be carreful and almost always enquote your variables usages in shell
> scripts.
>
> > +}
> > +
> > +_append_to_compreply () {
> > +     sorted_crrt_compreply_entries=$(_sort_entries_string $1)
>
> enquote $(_sort_entries_string $1)
>
> > +     local IFS=" "
>
> Is it really necessary? Unset IFS is equal to spaces, tab and newline.
>
> > +     for word in ${sorted_crrt_compreply_entries}; do
>
> You could probably remove the tmp variable and directly call
> _sort_entries_string here.
>
> rest lgtm


More information about the Password-Store mailing list