Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Is there a possibility to write the following script without the loop?

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=() 

for var in ${!IPv4_@}
do
   IPv4_all+=(${!var})
done

printf "'%s'\n" "${IPv4_all[@]}"

Something like:

IPv4_all=${!${!IPv4_@}}
share|improve this question
    
I would have used Python for that, you can do that easily there. –  queueoverflow Jul 25 at 17:42

4 Answers 4

up vote 5 down vote accepted

This might be the ugliest Bash code I've ever written, but...

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

names=(${!IPv4_@})
eval "IPv4_all=(${names[@]/#/$})"
printf "'%s'\n" "${IPv4_all[@]}"

Look Ma, no loop!

${names[@]/#/$} prepends $ to the start of every element of the array, by matching an empty string anchored to the start of each element. That gives an array of variable dereferences, which we can expand inside eval to get the variable references inside the array initialiser. These need to be two separate lines because you can't apply multiple parameter expansions at the same time.

The output is:

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

as expected.

It's possible to replace the line with:

IPv4_all=($(eval "echo ${names[@]/#/$}"))

rather than evalling the array assignment. I'm not sure whether that's any better or not.

If your variable values might contain spaces or other IFS characters, you can change the eval:

eval "IPv4_all=($(printf '"$%s" ' "${names[@]}"))"

That properly double-quotes all the variable dereferences.

share|improve this answer
    
That assumes variable names or values don't contain IFS or wildcard characters. –  Stéphane Chazelas Jul 25 at 10:55
    
I've edited in cover for the values with printf. If your variable names contain IFS characters I question your judgement, but you can modify the code accordingly. –  Michael Homer Jul 25 at 11:09
    
IFS may have been modified beforehand (and contain underscore or digits or letters...). It's my policy to set IFS whenever I use the split+glob operator. –  Stéphane Chazelas Jul 25 at 11:23

You don't need IPv4_all variable:

eval printf "\'%s\'\\\n" $(printf "$%s\n" ${!IPv4_@})

Output:

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'
share|improve this answer

My competition entry on the ugliest/most convoluted bash code ;-):

eval 'declare(){ v=${2%%=*};[[ $v = IPv4_* ]]&&IPv4_all+=("${!v}");};'"$(declare -p)"
unset -f declare
share|improve this answer
    
Hey - what happened to the other thing? –  mikeserv Jul 25 at 13:52

This works for me, I think, though I'll accept that I could be missing some fundamental point:

IPv4_first=1.1.1.1
IPv4_second=2.2.2.2
IPv4_third=3.3.3.3

IPv4_all=( $(set | sed '/IPv4_.*[=)]/!d;s///') )

printf "'%s'\n" "${IPv4_all[@]}"

OUTPUT

'1.1.1.1'
'2.2.2.2'
'3.3.3.3'

This is better:

eval IPv4_all=( "$(set |
    grep -E '^IPv4_[_[:alnum:]]*=([^(]|$)' |
    sed 's/\([^=]*\).*/${\1+"$\1"} /')"
)

grep only gets safe lines that match your target var. sed surrounds them in parameter expansion tokens so they evaluate away to nothing if they're not actually current shell variable names.

share|improve this answer
    
That assumes the variable values are not multi-line and that you don't have variables whose value contains \nIPv4_...= or whose name contains (and doesn't start with) IPv4_. –  Stéphane Chazelas Jul 25 at 10:42
    
Yeah - I just realized that - I have to evaluate them away - return only var names. I'm about to do that now. –  mikeserv Jul 25 at 10:46
    
@mikeserv: I can see what the s/// part is doing but I don't understand why... Can you explain it? –  Ray Jul 25 at 10:58
    
Umm... Actually, as it happens, I am, at the moment, very well disposed to do so, yes. I just answered a question about that: unix.stackexchange.com/a/146509/52934 –  mikeserv Jul 25 at 11:19
1  
A slightly more concise and legible idiom for sed '/.../!d;s///' is sed -n 's/...//p'. –  Stéphane Chazelas Jul 25 at 11:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.