List variables with prefix where the prefix is stored in another variable

11,473

Solution 1

Bash doesn't parse nested variable expansions. You can do what you want with eval: build the shell snippet ${!apple_@}, then use it in a command that you execute with eval.

prefix=apple_
eval 'vars=(${!'"$prefix"'@})'

Make sure that prefix contains only valid identifier characters, otherwise the snippet could result in anything.

eval is usually necessary to work with variables whose name is calculated dynamically, but in this specific case there's a completely different way to do the same thing: use the completion system. Bash's completion system works even from a script. The compgen builtin lists one completion per line, which is ambiguous when completions can contain newlines, but this isn't the case here — they don't even contain wildcard characters, so the output can be split with a simple unquoted expansion. This will safely output nothing if the prefix contains characters that aren't valid in identifiers.

vars=($(compgen -v "$prefix"))

Solution 2

You could use env | grep '^prefix'.

For example:

$ env | grep '^LESS'
LESSBINFMT=*u.
LESS=-MMqx4ij3
LESSOPEN=| /usr/bin/lesspipe %s
LESSCLOSE=/usr/bin/lesspipe %s %s

If you only want the values and not the variable names, use awk:

$ env | awk -F= '/^LESS/ {print $2}'
*u.
-MMqx4ij3
| /usr/bin/lesspipe %s
/usr/bin/lesspipe %s %s

(or print $1 for just the variable names)


Update 2019-05-17

I was brought back to this answer by an upvote, and realised there's an obvious improvement:

typeset -p works better than env, it outputs all variables whether they're exported or not. For example:

typeset -p | awk '$3 ~ /^apple_/ { print $3 }'

To use a variable rather than a fixed string:

prefix=apple_
typeset -p | awk '$3 ~ "^"pfx { print $3 }' pfx="$prefix" 

or just export $prefix so it's available in awk's ENVIRON array:

export prefix=apple_
typeset -p | awk '$3 ~ "^"ENVIRON["prefix"] { print $3 }'

Note: with a little work, this can be made to work with other shells such as ksh or zsh, but it is important to note that the output format for typeset -p is different in other shells, so the awk script will have to be modified to suit them.

Share:
11,473

Related videos on Youtube

Angelo
Author by

Angelo

Updated on September 18, 2022

Comments

  • Angelo
    Angelo over 1 year

    I'm trying to list all the variables with a certain prefix, but that prefix is dynamically determined. For example:

    prefix=apple_
    apple_one=a1
    apple_two=a2
    

    If I simply do:

    echo ${!apple_@}
    

    I can get the variable names that start with apple_. However, I would like to do this by using the variable prefix somehow. Everything I've tried leads to a bad substitution. For example I cannot:

    echo ${!${prefix}@}
    
    • Sparhawk
      Sparhawk over 8 years
      Depending on your exact problem, you might want to consider using arrays.
    • Sparhawk
      Sparhawk over 8 years
      I only mention it because I've tried to do the same thing. I refactored to use arrays, and even though I had to modify what I was doing conceptually, it made more "sense" in the end. And seemed less hacky.
    • Angelo
      Angelo over 8 years
      I had been trying to avoid associative arrays because I was burned a little while back due to portability issues. (I think they started support for that in Bash 4.)
    • philwalk
      philwalk over 5 years
      I have this same requirement, with the additional stipulation that I be able to export the variables, so bash arrays are excluded.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 8 years
    Not if the variables aren't exported. And this can break if there are environment variables containing line breaks.
  • Alessio
    Alessio over 8 years
    true. nothing's perfect, and this is about as close as it's possible to get to what the OP wanted. set could also be used if you don't mind getting shell functions mixed in with your variables.
  • mikeserv
    mikeserv over 8 years
    well, it wont put the function names out if you tell it to behave: set -o posix; set
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 8 years
    @cas It's possible to get exactly what the OP wanted. It would be difficult if you wanted a POSIX solution, but ${!prefix} is bash-specific in the first place (it isn't even a ksh feature).
  • Alessio
    Alessio almost 5 years
    typeset -p works better than env, outputs all variables whether they're exported or not. e.g. typeset -p | awk '$3 ~ /^BASH/ {print $3}', and avoids the potentially dangerous use of eval. I should have remembered typeset when I first posted this answer. to a variable rather than a fixed string: prefix="BASH"; typeset -p | awk '$3 ~ "^"prefix { print $3}' prefix="$prefix"