Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

currently doable? Indirect notation used w/a hash

9 views
Skip to first unread message

Linda Walsh

unread,
Jun 9, 2013, 5:02:02 PM6/9/13
to bug-bash
I was wondering if I was missing some syntax somewhere...
but I wanted to be able to pass the name of a hash in
and store stuff in it and later retrieve it... but it
looks like it's only possible with an eval or such?

Would be nice....(??)*sigh*

Pierre Gaston

unread,
Jun 10, 2013, 2:24:41 AM6/10/13
to Linda Walsh, bug-bash
bash4 has associative arrays:

declare -A array
array[foobar]=baz
echo "${array[foobar]}"

Linda Walsh

unread,
Jun 10, 2013, 5:43:54 AM6/10/13
to Pierre Gaston, bug-bash


Pierre Gaston wrote:

> bash4 has associative arrays:
>
> declare -A array
> array[foobar]=baz
> echo "${array[foobar]}"
---

Right, and bash's namespace is also an associative array -- names & values.

In the main namespace you can use '!' to introduce indirection,
but not in a generalized way.

i.e. a=foo ${!a}=1; echo $foo => '1'

What I found myself wanting was having several 'sets' of
the same parameters of info. so I could have
multiple hashes or associative arrays,

eth0=([ip]=1.2.3.4/24 [mtu]=1500 [startmode]=auto)
eth1=([ip]=192.168.0.1/24 [mtu]=9000 [startmode]=onboot)

Then wanted to be able to pass which interface to work on, into
a function i.e. pass in 'eth0' or 'eth1'... etc.

In the function, the parameter would be in a var maybe "IF".

Now I want to access the value for IP for the current "IF" (IF holding
eth0 or eth1 or some other InterFace name).

i.e.
IF=eth0; echo "${!IF[IP]}"

but that doesn't work. You need secondary dereferencing, like:

eval echo \${$IF[IP]}

I ended up using a sub, since after reading in some config files
99% of my usage is reading, so like:

sub read_n_apply_cfg () {
my file="${1:?}"
my this="cfg_$file"
eval "hash -g $this" <- creates per item hash/assoc. array (assigned somewhere
later)

#accessor
sub this () { eval echo "\${$this[$1]:-""}" ;} <- hide indirecton in sub

usage:
local ip=$(this ip) #(with '$this' set at top of routine)

------
A bit contorted, but anyone doing shell programming has to be
used to that. ;-)






Pierre Gaston

unread,
Jun 10, 2013, 6:37:10 AM6/10/13
to Linda Walsh, bug-bash
Well, it's easier to ask for what you really want first.
For your use case, it seems to me that you can use the same trick that
is common for simulating arrays of arrays in awk:

declare -A ip
ip['eth0 ip']=1.2.3.4/24
ip['eth1 mtu']=1200

apply () {
echo "${ip["$1 ip"]}"
}
apply eth0

One drawback is that you can list the interfaces in the array with
this method, but you can use an helper array if this is needed.

eg:
declare -A ip
declare -a interfaces
addInterface () {
interfaces+=( "$1")
ip["$1 ip"]=$2
}

addInterface eth0 1.2.3.4/24

Greg Wooledge

unread,
Jun 10, 2013, 8:27:43 AM6/10/13
to Linda Walsh, bug-bash
On Sun, Jun 09, 2013 at 02:02:02PM -0700, Linda Walsh wrote:
> I was wondering if I was missing some syntax somewhere...
> but I wanted to be able to pass the name of a hash in
> and store stuff in it and later retrieve it... but it
> looks like it's only possible with an eval or such?

Passing arrays "by reference" (by name) to a function will be possible
in bash 4.3. It is not possible in current versions of bash, without
using eval trickery, as you have already noted. And doing it with
eval is so difficult that it's easier to switch languages entirely.

Chris F.A. Johnson

unread,
Jun 10, 2013, 9:10:17 AM6/10/13
to bug-bash
It is not the least bit difficult with eval:

eval "array=( \"\${$1[@]}\" )"

--
Chris F.A. Johnson, <http://cfajohnson.com/>
Author:
Pro Bash Programming: Scripting the GNU/Linux Shell (2009, Apress)
Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)

Chris Down

unread,
Jun 10, 2013, 9:17:23 AM6/10/13
to Chris F.A. Johnson, bug-bash
Enjoy your arbitrary command execution.

Chet Ramey

unread,
Jun 10, 2013, 9:37:15 AM6/10/13
to Linda Walsh, chet....@case.edu, Pierre Gaston, bug-bash
On 6/10/13 5:43 AM, Linda Walsh wrote:

> What I found myself wanting was having several 'sets' of
> the same parameters of info. so I could have
> multiple hashes or associative arrays,
>
> eth0=([ip]=1.2.3.4/24 [mtu]=1500 [startmode]=auto)
> eth1=([ip]=192.168.0.1/24 [mtu]=9000 [startmode]=onboot)
>
> Then wanted to be able to pass which interface to work on, into
> a function i.e. pass in 'eth0' or 'eth1'... etc.
>
> In the function, the parameter would be in a var maybe "IF".
>
> Now I want to access the value for IP for the current "IF" (IF holding
> eth0 or eth1 or some other InterFace name).

This is an excellent statement of the rationale for nameref variables,
which will be implemented in bash-4.3.

Chet

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, ITS, CWRU ch...@case.edu http://cnswww.cns.cwru.edu/~chet/

Greg Wooledge

unread,
Jun 10, 2013, 9:39:56 AM6/10/13
to bug-bash
> On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
> > It is not the least bit difficult with eval:
> >
> > eval "array=( \"\${$1[@]}\" )"

On Mon, Jun 10, 2013 at 09:17:23PM +0800, Chris Down wrote:
> Enjoy your arbitrary command execution.

To be fair, Chris Johnson was probably assuming a programming environment
where the function is only callable from within the same script, and
thus has only trustworthy arguments provided by the caller. Sometimes,
this is the case. Sometimes, it isn't.

In a more general sense, using eval SAFELY requires undergoing several
tedious steps, all of which were brushed aside as "not the least bit
difficult", which is certainly an exaggeration.

1) Figure out what command you would want to execute if you had the actual
array name in front of you right now:

arrayname[i++]="$foo $bar"

2) Escape all the metacharacters in such a way that you can stick it inside
double quotes without mangling anything:

arrayname[i++]=\"\$foo \$bar\"

3) Figure out where the arrayname is going to come from, and whether it is
trustworthy (or "untainted" if you prefer perl's wording). If it is
untrustworthy (tainted) then you'll either need to run it through
printf %q, or check it for invalid characters and abort if found,
depending on the needs of the program.

local aref
printf -v aref %q "$1"

4) Replace the "arrayname" placeholder in the escaped command:

$aref[i++]=\"\$foo \$bar\"

5) Wrap eval "..." around it.

All together:

local aref
printf -v aref %q "$1"
eval "$aref[i++]=\"\$foo \$bar\""

To call this "not the least bit difficult" strikes me as somewhat
disingenuous. But I'll concede that it is certainly *possible*, albeit
only with the use of bash's printf %q extension. It is not possible
to do this safely in strict POSIX shells, other than by searching the
input for invalid characters and aborting.

Chris F.A. Johnson

unread,
Jun 10, 2013, 10:23:10 AM6/10/13
to Chris Down, bug-bash
On Mon, 10 Jun 2013, Chris Down wrote:

> Enjoy your arbitrary command execution.

Can you give me an example, using the code I posted, where that would happen?

> On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
>
>> On Mon, 10 Jun 2013, Greg Wooledge wrote:
>>
>> On Sun, Jun 09, 2013 at 02:02:02PM -0700, Linda Walsh wrote:
>>>
>>>> I was wondering if I was missing some syntax somewhere...
>>>> but I wanted to be able to pass the name of a hash in
>>>> and store stuff in it and later retrieve it... but it
>>>> looks like it's only possible with an eval or such?
>>>>
>>>
>>> Passing arrays "by reference" (by name) to a function will be possible
>>> in bash 4.3. It is not possible in current versions of bash, without
>>> using eval trickery, as you have already noted. And doing it with
>>> eval is so difficult that it's easier to switch languages entirely.
>>>
>>
>> It is not the least bit difficult with eval:
>>
>> eval "array=( \"\${$1[@]}\" )"

Greg Wooledge

unread,
Jun 10, 2013, 10:33:11 AM6/10/13
to Chris F.A. Johnson, bug-bash, Chris Down
On Mon, Jun 10, 2013 at 10:23:10AM -0400, Chris F.A. Johnson wrote:
> On Mon, 10 Jun 2013, Chris Down wrote:
>
> >Enjoy your arbitrary command execution.
>
> Can you give me an example, using the code I posted, where that would
> happen?

> >On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
> >>eval "array=( \"\${$1[@]}\" )"

imadev:~$ foobar() { set -x; eval "array=( \"\${$1[@]}\" )"; }
imadev:~$ foobar 'a}"); date; b=("${q'
+ foobar 'a}"); date; b=("${q'
+ set -x
+ eval 'array=( "${a}"); date; b=("${q[@]}" )'
++ array=("${a}")
++ date
Mon Jun 10 10:31:41 EDT 2013
++ b=("${q[@]}")

A really clever attack wouldn't leave those extra variables lying around,
either. I stopped at "working" and didn't spend the extra time for
"clever".

Chris F.A. Johnson

unread,
Jun 10, 2013, 10:34:41 AM6/10/13
to bug-bash
On Mon, 10 Jun 2013, Greg Wooledge wrote:

>> On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
>>> It is not the least bit difficult with eval:
>>>
>>> eval "array=( \"\${$1[@]}\" )"
>
> On Mon, Jun 10, 2013 at 09:17:23PM +0800, Chris Down wrote:
>> Enjoy your arbitrary command execution.
>
> To be fair, Chris Johnson was probably assuming a programming environment
> where the function is only callable from within the same script, and
> thus has only trustworthy arguments provided by the caller. Sometimes,
> this is the case. Sometimes, it isn't.

The example I gave is always safe; it expands, given 'q' as the argument, to:

array=( "${q[@]}" )

What's unsafe about that? Why would it need any preprocessing?

Pierre Gaston

unread,
Jun 10, 2013, 10:50:24 AM6/10/13
to Linda Walsh, bug-bash
On Mon, Jun 10, 2013 at 1:37 PM, Pierre Gaston <pierre...@gmail.com> wrote:
> On Mon, Jun 10, 2013 at 12:43 PM, Linda Walsh <ba...@tlinx.org> wrote:
>>
>>
>> Pierre Gaston wrote:
>>
>>> bash4 has associative arrays:
>>>
>>> declare -A array
>>> array[foobar]=baz
>>> echo "${array[foobar]}"
>>
>> ---
>>
>> Right, and bash's namespace is also an associative array -- names & values.
>>
>> In the main namespace you can use '!' to introduce indirection,
>> but not in a generalized way.
>>
>> i.e. a=foo ${!a}=1; echo $foo => '1'
>>
>> What I found myself wanting was having several 'sets' of
>> the same parameters of info. so I could have
>> multiple hashes or associative arrays,
>>
>> eth0=([ip]=1.2.3.4/24 [mtu]=1500 [startmode]=auto)
>> eth1=([ip]=192.168.0.1/24 [mtu]=9000 [startmode]=onboot)
>>
>> Then wanted to be able to pass which interface to work on, into
>> a function i.e. pass in 'eth0' or 'eth1'... etc.
>>
>> In the function, the parameter would be in a var maybe "IF".
>>
>> Now I want to access the value for IP for the current "IF" (IF holding
>> eth0 or eth1 or some other InterFace name).
>>
>> i.e.
>> IF=eth0; echo "${!IF[IP]}"
>>
>> but that doesn't work. You need secondary dereferencing, like:
>>
>> eval echo \${$IF[IP]}
>>
>> I ended up using a sub, since after reading in some config files
>> 99% of my usage is reading, so like:
>>
>> sub read_n_apply_cfg () {
>> my file="${1:?}"
>> my this="cfg_$file"
>> eval "hash -g $this" <- creates per item hash/assoc. array
>> (assigned somewhere later)
>>
>> #accessor
>> sub this () { eval echo "\${$this[$1]:-""}" ;} <- hide indirecton in
>> sub
>>
>> usage:
>> local ip=$(this ip) #(with '$this' set at top of routine)
>>
>> ------
>> A bit contorted, but anyone doing shell programming has to be
>> used to that. ;-)
>>
>
> Well, it's easier to ask for what you really want first.

my apologies, I misread "name of a hash" and read pass a hash and
thought you were just after an associative array

Chris F.A. Johnson

unread,
Jun 10, 2013, 11:24:46 AM6/10/13
to bug-bash
On Mon, 10 Jun 2013, Greg Wooledge wrote:

> On Mon, Jun 10, 2013 at 10:23:10AM -0400, Chris F.A. Johnson wrote:
>> On Mon, 10 Jun 2013, Chris Down wrote:
>>
>>> Enjoy your arbitrary command execution.
>>
>> Can you give me an example, using the code I posted, where that would
>> happen?
>
>>> On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
>>>> eval "array=( \"\${$1[@]}\" )"
>
> imadev:~$ foobar() { set -x; eval "array=( \"\${$1[@]}\" )"; }
> imadev:~$ foobar 'a}"); date; b=("${q'
> + foobar 'a}"); date; b=("${q'
> + set -x
> + eval 'array=( "${a}"); date; b=("${q[@]}" )'
> ++ array=("${a}")
> ++ date
> Mon Jun 10 10:31:41 EDT 2013
> ++ b=("${q[@]}")
>
> A really clever attack wouldn't leave those extra variables lying around,
> either. I stopped at "working" and didn't spend the extra time for
> "clever".

Point taken, but the only way such a string would be passed as a
variable name is if it was given as user input -- which would,
presumably, be sanitized before being used. Programming it literally
makes as much sense as 'rm -rf /'.

Linda Walsh

unread,
Jun 10, 2013, 6:00:27 PM6/10/13
to bug-bash

> Point taken, but the only way such a string would be passed as a
> variable name is if it was given as user input -- which would,
> presumably, be sanitized before being used. Programming it literally
> makes as much sense as 'rm -rf /'.
---
That still didn't POSIX-Gnu rm from disabling that ability.

Though the one that really causes a pain is them removing
the ability to safely delete all files in a directory with the 'rm' command.

Now, many contortions are necessary.

(i.e.: "cd testing/output/ && rm --one-file-system -fr ."
used to safely deleted everything in output -- except the "." --
but it was 'last' (recursive 'rm' has to be depth first), and
the -f would suppress the error you got about not being able to remove ".".

Now they put in a special check to check the starting arguments first,
before doing the depth-first remove and abort any processing for files
in "." You need to use 'find' with alot more typing to do something similar.

All done in the name of the new posix spec. I was told that the reason
why posix has changed from descriptive to prescriptive is because there are
no more companies representing the SYSV flavors of the utils -- so the BSD
reps decided to use POSIX to force all the flavors to become like BSD by
changing POSIX to describe BSD and forcing others to comply or be branded
not-posix compliant.

Of course it's a little more complicated than that description, but that seems
to a large part of what is going on.



Chris F.A. Johnson

unread,
Jun 10, 2013, 6:20:44 PM6/10/13
to Linda Walsh, bug-bash
On Mon, 10 Jun 2013, Linda Walsh wrote:

>
>> Point taken, but the only way such a string would be passed as a
>> variable name is if it was given as user input -- which would,
>> presumably, be sanitized before being used. Programming it literally
>> makes as much sense as 'rm -rf /'.
> ---
> That still didn't POSIX-Gnu rm from disabling that ability.

Did they? I'm not going to test it :(

> Though the one that really causes a pain is them removing
> the ability to safely delete all files in a directory with the 'rm' command.

Since when?

> Now, many contortions are necessary.
>
> (i.e.: "cd testing/output/ && rm --one-file-system -fr ."
> used to safely deleted everything in output -- except the "." --
> but it was 'last' (recursive 'rm' has to be depth first), and
> the -f would suppress the error you got about not being able to remove ".".

Contortions???

> Now they put in a special check to check the starting arguments first,
> before doing the depth-first remove and abort any processing for files
> in "." You need to use 'find' with alot more typing to do something
> similar.

What's wrong with:

rm -rf *

Linda Walsh

unread,
Jun 10, 2013, 6:24:49 PM6/10/13
to Chris F.A. Johnson, bug-bash


Chris F.A. Johnson wrote:

> What's wrong with:
>
> rm -rf *

1) it may or may not ignore hidden files depending on shell settings.
2) it crosses into mounted files systems


Linda Walsh

unread,
Jun 10, 2013, 7:24:49 PM6/10/13
to Chris F.A. Johnson, bug-bash
====
Forgot an important one:

3) Follows symlinks in the directory you are deleting in. (so if
They point outside the tree, no telling what you are deleting.




Mike Frysinger

unread,
Jun 10, 2013, 9:14:14 PM6/10/13
to bug-...@gnu.org, Chris F.A. Johnson
On Monday 10 June 2013 18:20:44 Chris F.A. Johnson wrote:
> On Mon, 10 Jun 2013, Linda Walsh wrote:
> >> Point taken, but the only way such a string would be passed as a
> >> variable name is if it was given as user input -- which would,
> >> presumably, be sanitized before being used. Programming it literally
> >> makes as much sense as 'rm -rf /'.
> >
> > ---
> >
> > That still didn't POSIX-Gnu rm from disabling that ability.
>
> Did they? I'm not going to test it :(

do it as non-root:
$ rm -rf /
rm: it is dangerous to operate recursively on `/'
rm: use --no-preserve-root to override this failsafe
-mike
signature.asc

Chris Down

unread,
Jun 11, 2013, 3:23:29 AM6/11/13
to Mike Frysinger, Chris F.A. Johnson, bug-bash
If that check didn't exist, rm -rf / would still be dangerous; it would
just give a lot of errors for the files it couldn't delete, and delete the
ones it can. Running it as a normal user doesn't make it safer.

Mike Frysinger

unread,
Jun 11, 2013, 1:03:38 PM6/11/13
to Chris Down, Chris F.A. Johnson, bug-bash
sure it does. you just have to be fast :P.
-mike
signature.asc

Linda Walsh

unread,
Jun 13, 2013, 7:51:44 PM6/13/13
to chet....@case.edu, bug-bash


Chet Ramey wrote:
>> Now I want to access the value for IP for the current "IF" (IF holding
>> eth0 or eth1 or some other InterFace name).
>
> This is an excellent statement of the rationale for nameref variables,
> which will be implemented in bash-4.3.
----
That order fries wouldn't happen to come with
the ability to also export such things would it? ;-)

I mean if what I pass it to in a function is in another
process by virtue of me calling it $(funcname)...

Would be alot easier than my current method of storing such
things as define-strings along side the array/hashes that are
re-instantiated into the next process's environment (assuming it
processes my BASH_ENV -- which presumes it's another bash script).






Chris Down

unread,
Jun 14, 2013, 4:46:54 AM6/14/13
to Linda Walsh, bug-bash, chet....@case.edu
On 14 Jun 2013 00:57, "Linda Walsh" <ba...@tlinx.org> wrote:
>
>
>
> Chet Ramey wrote:
>>>
>>> Now I want to access the value for IP for the current "IF" (IF holding
>>> eth0 or eth1 or some other InterFace name).
>>
>>
>> This is an excellent statement of the rationale for nameref variables,
>> which will be implemented in bash-4.3.
>
> ----
> That order fries wouldn't happen to come with
> the ability to also export such things would it? ;-)

Please, no more brittle export hacks. I'm already crying enough at function
exports.

Linda Walsh

unread,
Jun 14, 2013, 5:20:52 AM6/14/13
to Chris Down, bug-bash
Brittle would be bad. Pliant and chewy would be much better, I agree.

Perhaps you might explain what you mean by brittle? Like the export hacks
you get in the Antarctic in June? The word brittle with export doesn't
seem to fit very well. What happened to your functions? Don't they work?






Chris Down

unread,
Jun 14, 2013, 6:21:42 AM6/14/13
to Linda Walsh, bug-bash
On 14 Jun 2013 10:21, "Linda Walsh" <ba...@tlinx.org> wrote:
>> Please, no more brittle export hacks. I'm already crying enough at
function exports.
>
>
> Brittle would be bad. Pliant and chewy would be much better, I agree.
>
> Perhaps you might explain what you mean by brittle? Like the export hacks
> you get in the Antarctic in June? The word brittle with export doesn't
> seem to fit very well. What happened to your functions? Don't they work?

You are aware of *how* they work, yes? (with apologies for the terse
replies, I'm on my phone)

Dan Douglas

unread,
Jun 14, 2013, 7:02:15 AM6/14/13
to bug-...@gnu.org, Greg Wooledge
On Monday, June 10, 2013 09:39:56 AM Greg Wooledge wrote:
> > On 10 Jun 2013 14:15, "Chris F.A. Johnson" <ch...@cfajohnson.com> wrote:
> > > It is not the least bit difficult with eval:
> > >
> > > eval "array=( \"\${$1[@]}\" )"
>
> On Mon, Jun 10, 2013 at 09:17:23PM +0800, Chris Down wrote:
> > Enjoy your arbitrary command execution.
>
> To be fair, Chris Johnson was probably assuming a programming environment
> where the function is only callable from within the same script, and
> thus has only trustworthy arguments provided by the caller. Sometimes,
> this is the case. Sometimes, it isn't.
>
> In a more general sense, using eval SAFELY requires undergoing several
> tedious steps.

You're aware of this. For the record, there should never be a situation where
a variable name isn't guaranteed. All builtins that accept variable names,
arithmetic variable dereferences, and indirect variable expansions are
exploitable, not just eval. It never makes sense to have the name of some
internal label not controlled entirely internally.

If it's a library function used by some other script, then responsibility for
making that guarantee is inherited by the library consumer. There's no
difference.

To validate a "parameter name" fully requires a full shell parser, because the
subscript of an indexed array is effectively a part of its name as far as Bash
is concerned.

--
Dan Douglas

Dan Douglas

unread,
Jun 14, 2013, 7:25:12 AM6/14/13
to bug-...@gnu.org
Also forgot to mention (though it should be obvious).

$ ~/doc/programs/bash43 -c 'function f { typeset -n x=$1; : "$x"; }; a=(yo jo); f "a[\$(echo yes this even applies to namerefs>&2)0]"'
yes this even applies to namerefs

In a nutshell bash namerefs don't actually enable any new functionality. It's
just a convenient syntax, makes dealing with keys indirectly easier, and
improves portability in some limited cases.

--
Dan Douglas

Pierre Gaston

unread,
Jun 14, 2013, 7:30:19 AM6/14/13
to Dan Douglas, bug-...@gnu.org
Maybe nameref also allows to avoid name clashes (which I believe is
not that easy to workaround now)?

Dan Douglas

unread,
Jun 14, 2013, 7:36:33 AM6/14/13
to Pierre Gaston, bug-...@gnu.org
> Maybe nameref also allows to avoid name clashes (which I believe is
> not that easy to workaround now)?

It doesn't. https://gist.github.com/ormaaj/5682807

--
Dan Douglas

Greg Wooledge

unread,
Jun 14, 2013, 8:23:04 AM6/14/13
to Dan Douglas, bug-...@gnu.org
On Fri, Jun 14, 2013 at 06:25:12AM -0500, Dan Douglas wrote:
> Also forgot to mention (though it should be obvious).
>
> $ ~/doc/programs/bash43 -c 'function f { typeset -n x=$1; : "$x"; }; a=(yo jo); f "a[\$(echo yes this even applies to namerefs>&2)0]"'
> yes this even applies to namerefs

No, that was not obvious.

Also, ouch. :(

Linda Walsh

unread,
Jun 14, 2013, 4:30:35 PM6/14/13
to bug-bash, chris@chrisdown.name >> 'Chris Down'
====
They are put in the environment as strings, no?
That seems fairly sound for most uses. It's far from bullet proof,
but this is unencrypted SHELL we are talking about, so for it's use case,
I don't see why it would be such a problem.

John Kearney

unread,
Jun 15, 2013, 9:03:19 AM6/15/13
to Linda Walsh, bug-bash
In bash there are 2 options that I use.

1.
ArrayName=blah

printf -V "${ArrayName}[Index]" "%s" "Value To Set"


2.
ks_val_ChkName() {
local LC_COLLATE=C
case "${1:?Missing Variable Name}" in
[!a-zA-Z_]* | *[!a-zA-Z_0-9]* | '' ) return 3;;
esac
}
ks_array_SetVal() {
ks_val_ChkName "${1:?Missing Array Name}" || return $?
ks_val_ChkName "a${2:?Missing Array Index}" || return $?
eval "${1}"["${2}"]'="${3:-}"'
}
ks_array_SetVal "${ArrayName}" "Index" "Value"

Cheers

Gesendet: Sonntag, 09. Juni 2013 um 23:02 Uhr
Von: "Linda Walsh" <ba...@tlinx.org>
An: bug-bash <bug-...@gnu.org>
Betreff: currently doable? Indirect notation used w/a hash
I was wondering if I was missing some syntax somewhere...
but I wanted to be able to pass the name of a hash in
and store stuff in it and later retrieve it... but it
looks like it's only possible with an eval or such?
Would be nice....(??)*sigh*

John Kearney

unread,
Jun 15, 2013, 9:41:31 AM6/15/13
to John Kearney, Linda Walsh, bug-bash

Sorry forgot the bit to retrive values
It is possible to retrive numeric values without eval
i.e.
val=$((${ArrayName}[Index]))
works quiet well and is quick, in fact I used to use this quiet a lot.


There is also a backdoor approach that I don't really advise.
val="${ArrayName}[Index]"
echo "${!val}"
What I actually tend to do is.
ks_array_ChkName() {
local LC_COLLATE=C
case "${1:?Missing Variable Name}" in
[!a-zA-Z_]* | *[!][a-zA-Z_0-9]* ) return 3;;
esac
}
ks_val_Get() {
ks_array_ChkName "${1:?Missing Destination Variable Name}" ||
return $?
ks_array_ChkName "${2:?Missing Source Variable Name}" || return $?
eval "${1}=\"\${${2}:-}\""
}
ks_array_GetVal() { ks_val_Get "${1}" "${2}[${3}]" ; }
ks_array_SetVal() { ks_val_Set "${1}[${2}]" "${3:-}" ; }

Cheers

Gesendet: Samstag, 15. Juni 2013 um 15:03 Uhr
Von: "John Kearney" <dethr...@web.de>
An: "Linda Walsh" <ba...@tlinx.org>
Cc: bug-bash <bug-...@gnu.org>
Betreff: Aw: currently doable? Indirect notation used w/a hash

Linda Walsh

unread,
Jun 15, 2013, 3:36:22 PM6/15/13
to John Kearney, bug-bash


John Kearney wrote:
>
> Sorry forgot the bit to retrive values
> It is possible to retrive numeric values without eval
> i.e.
> val=$((${ArrayName}[Index]))
> works quiet well and is quick, in fact I used to use this quiet a lot.
>
>
> There is also a backdoor approach that I don't really advise.
> val="${ArrayName}[Index]"
> echo "${!val}"
-----
Don't advise? Any particular reason? or stylistic?

Some interesting ways of doing these things -- will have to play
around with them to see howuseful they are in place of some of my
existing hacks -- see if they help them look cleaner ... am always
looking for new ways to clean up my code and make it simpler/easier
to understand even it looks non-standard to language purists...

thanks!

>
> What I actually tend to do is.
> ks_array_ChkName() {
> local LC_COLLATE=C
> case "${1:?Missing Variable Name}" in
> [!a-zA-Z_]* | *[!][a-zA-Z_0-9]* ) return 3;;
> esac
> }
> ks_val_Get() {
> ks_array_ChkName "${1:?Missing Destination Variable Name}" || return $?
> ks_array_ChkName "${2:?Missing Source Variable Name}" || return $?
> eval "${1}=\"\${${2}:-}\""
> }
> ks_array_GetVal() { ks_val_Get "${1}" "${2}[${3}]" ; }
> ks_array_SetVal() { ks_val_Set "${1}[${2}]" "${3:-}" ; }
>
>
> }

Greg Wooledge

unread,
Jun 17, 2013, 7:57:33 AM6/17/13
to Linda Walsh, John Kearney, bug-bash
On Sat, Jun 15, 2013 at 12:36:22PM -0700, Linda Walsh wrote:
> John Kearney wrote:
> >There is also a backdoor approach that I don't really advise.
> >val="${ArrayName}[Index]"
> >echo "${!val}"
> -----
> Don't advise? Any particular reason? or stylistic?

I'd shared this advice ("don't use it"), because I cannot for the
life of me tell whether this is a bug or a feature. As near as I
can tell, it is an unforeseen consequence of the parser implementation,
not documented anywhere. As such, I would not rely on it to continue
working in future Bash releases.

P.S. you meant printf -v, not -V.

John Kearney

unread,
Jun 17, 2013, 8:33:09 AM6/17/13
to Greg Wooledge, Linda Walsh, bug-bash
Like I said its a back door aproach, it circumvents the parser. which
doesn't allow this syntax
${${Name}[1]}
I didn't actually find this myself it was reproted on this list a long
time ago. I do remember Chet saying he wouldn't break it. But other
than that I can't remember the discussion all that well. As always with
this topic it was a pretty lively debate.


Yhea its a constant fight getting my email clients to stop
capitialising various things in code.

Gesendet: Montag, 17. Juni 2013 um 13:57 Uhr
Von: "Greg Wooledge" <woo...@eeg.ccf.org>
An: "Linda Walsh" <ba...@tlinx.org>
Cc: "John Kearney" <dethr...@web.de>, bug-bash <bug-...@gnu.org>
Betreff: Re: currently doable? Indirect notation used w/a hash

Dan Douglas

unread,
Jun 18, 2013, 9:35:22 PM6/18/13
to bug-...@gnu.org, Linda Walsh, Greg Wooledge, John Kearney
This has been discussed many times before but I'll say again that this is consistent with almost every mechanism that accepts variable names in Bash. I would be very surprised if the exact same code wasn't used for evaluating parameter names (and their subscripts) because even the little quirks are consistent nearly everywhere.

Oh look, Bash supports multi-dimensional arrays:

#!/usr/bin/env bash

function getElem {
while
[[ ${!1+_} ]] || return 1
typeset -a "__getElemArr=${!1}"
set -- "__getElemArr${1#*]}"
[[ $1 == *\[!(@]|\*]) ]]
do :
done
printf '<%s> ' "${!1}"
echo
}

a=( '(a b c)' $'([3]=\'(d e f)\' [5]=1 2 3)' '(g h i)' )
# typeset -a a=(([0]=a [1]=b [2]=c) ([3]=([0]=d [1]=e [2]=f) [5]=1 [6]=2 [7]=3) ([0]=g [1]=h [2]=i) )

getElem 'a[1][3][@]' || echo unset

--
Dan Douglas

0 new messages