bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Enable compgen even when programmable completions are not available?


From: Fabien Orjollet
Subject: Re: Enable compgen even when programmable completions are not available?
Date: Wed, 28 Jun 2023 17:58:15 +0200

On 28/06/2023 14:45, Kerin Millar wrote:
On Wed, 28 Jun 2023 12:42:16 +0200
Fabien Orjollet <of1+bugbash@disroot.org> wrote:

On 28/06/2023 00:40, Kerin Millar wrote:
On Tue, 27 Jun 2023 21:52:53 +0200
of1 <of1@disroot.org> wrote:

On 27/06/2023 21:05, Kerin Millar wrote:
It doesn't work at all for >=5.2. The reason for this is interesting and I may 
make a separate post about it.

Prior to 5.2, it can easily be tricked into printing names that do not exist.

$ VAR=$'\nNONEXISTENT=' ./declare-P | grep ^NONEXISTENT
NONEXISTENT

Thank you. I was just reading the discussion in Gentoo forum and
realizing that I've been to quickly: it doesn't pass the
FOO=$'\nBAR BAZ QUUX=' test. But what about this? (I also quickly
tested with Bash 5.2.15).


FOO=$'\nBAR BAZ QUUX='
VAR=$'\nNONEXISTENT='

declare-P() {
      local curVar
      declare -a curVars

      readarray -t curVars <<<"$1"
      curVars=( "${curVars[@]%%=*}" )
      curVars=( "${curVars[@]##* }" )
      for curVar in "${curVars[@]}"; do
         ### we can use [[ -v "$curVar" ]] at some point!
         [[ "${curVar//[a-zA-Z0-9_]}" || \
            "${curVar:0:1}" == [0-9] || \
            ! -v "$curVar" || \
            ! "$curVar" =~ $2 ]] || printf '%s\n' "$curVar"
      done
}

declare-P "$(declare -p)"
echo "##################"
declare-P "$(declare -p)" "QU|^NON|VAR"r


This use of test -v is probably sufficient to address that particular issue. It 
is just as well that the test occurs after determining that the string is a 
valid identifier because test -v has the power to facilitate arbitrary code 
execution.

My assertion that your code is broken in 5.2 was incorrect. Rather, the problem 
seems to be that regcomp(3) behaviour differs across platforms, where given an 
empty expression to compile. To address that, you could determine whether a 
second positional parameter was given and is non-empty before proceeding to use 
it.

Thank you, good to know. I think that ${2:-.} should be correct.


Curiously, repeated invocation may lead to obvious issues of resource 
consumption.

# This is excruciatingly expensive
time for i in {1..100}; do
        declare-P "$(declare -p)"     
done

# This is not so expensive (!)
time for i in {1..100}; do
        declare-P "$(declare -p)"
        :
done

I made some testing by adding
echo -n "$(( ++COUNT ))>$(wc -c <<<"$1") "
echo "$1" >"/tmp/declare-P$COUNT"
to the function:

1>4651 2>9785 3>15283 4>21504 5>29173 6>39739 7>56095 8>84036
9>135145 10>232592 11>422710 12>798174 13>1544326 14>3031854
15>6002134 16>11937918 ^C

ls -rtnh /tmp/declare-P*
-rw-r--r-- 1 1000 1000 4,6K 28 juin  11:09 /tmp/declare-P1
-rw-r--r-- 1 1000 1000 9,6K 28 juin  11:09 /tmp/declare-P2
-rw-r--r-- 1 1000 1000  15K 28 juin  11:09 /tmp/declare-P3
-rw-r--r-- 1 1000 1000  21K 28 juin  11:09 /tmp/declare-P4
-rw-r--r-- 1 1000 1000  29K 28 juin  11:09 /tmp/declare-P5
-rw-r--r-- 1 1000 1000  39K 28 juin  11:09 /tmp/declare-P6
-rw-r--r-- 1 1000 1000  55K 28 juin  11:09 /tmp/declare-P7
-rw-r--r-- 1 1000 1000  83K 28 juin  11:09 /tmp/declare-P8
-rw-r--r-- 1 1000 1000 132K 28 juin  11:09 /tmp/declare-P9
-rw-r--r-- 1 1000 1000 228K 28 juin  11:09 /tmp/declare-P10
-rw-r--r-- 1 1000 1000 413K 28 juin  11:09 /tmp/declare-P11
-rw-r--r-- 1 1000 1000 780K 28 juin  11:09 /tmp/declare-P12
-rw-r--r-- 1 1000 1000 1,5M 28 juin  11:09 /tmp/declare-P13
-rw-r--r-- 1 1000 1000 2,9M 28 juin  11:09 /tmp/declare-P14
-rw-r--r-- 1 1000 1000 5,8M 28 juin  11:09 /tmp/declare-P15
-rw-r--r-- 1 1000 1000  12M 28 juin  11:09 /tmp/declare-P16

But by adding set -p to the subshell:

Your best option would probably be to run declare -p within the function while 
avoiding the use of command substitution altogether, so as not to 'pollute' the 
_ variable.

You're absolutely right. That was indeed my first option, but I'm used
to declaring variables, especially local ones. But this is clearly a
special case.
The _ thing is absolutely clear to me now.
Thank you very much for all!


FOO=$'\nBAR BAZ QUUX='
VAR=$'\nNONEXISTENT='

declare-P() {
   [[ -v "curVar" || -v "curVars" ]] && {
    echo -e "\n${FUNCNAME[0]}: error: curVar|curVars variable exists.\n"
    return 1; } >&2

   readarray -t curVars < <(declare -p)
   ### Sizes of curVars values should be checked
   ### and sed should be used above a certain size.
   curVars=( "${curVars[@]%%=*}" )
   curVars=( "${curVars[@]##* }" )

   for curVar in "${curVars[@]}"; do
      [[ -z "${curVar//[a-zA-Z0-9_]}" && \
         "${curVar:0:1}" != [0-9] && \
         -v "$curVar" && \
         "$curVar" =~ ${1:-.} ]] && printf '%s\n' "$curVar"
   done

   unset -v curVar curVars
   return 0
}

declare-P
echo "##################"
declare-P "QU|^NON|^VAR$"
time for i in {1..100}; do
   declare-P 1>/dev/null
done



reply via email to

[Prev in Thread] Current Thread [Next in Thread]