Re: [gentoo-dev] Re: evar_push/pop helpers
On Monday 17 June 2013 01:42:15 Mike Frysinger wrote: On Sunday 02 June 2013 13:38:04 Steven J. Long wrote: On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote: +# is not specified, the var will be unset. +evar_push_set() { + local var=$1 + evar_push ${var} + case $# in + 1) unset ${var} ;; + 2) eval ${var}=\$2 ;; I wish you wouldn't use eval for this. I know it's technically okay here, or would be if you verified the parameter, but bash has printf -v for this purpose: interesting, i hadn't seen that before ... looks new to bash-3.1. /me tucks that into his tool belt. although it doesn't quite work in the edge case where the value is an empty string. consider: unset x printf -v x '' echo ${x+set} that should show set, but it does not. i'll have to keep `eval ${var}=` when the value we're setting is empty. or just keep the eval code since i have to do eval anyways at that point. i'll report it upstream to the bash guys. looks like it can be worked around by doing: printf -v x '%s' '' which is arguably what we want anyways -mike signature.asc Description: This is a digitally signed message part.
Re: [gentoo-dev] Re: evar_push/pop helpers
On Sunday 02 June 2013 13:38:04 Steven J. Long wrote: On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote: --- eutils.eclass 22 May 2013 05:10:29 - 1.421 +++ eutils.eclass 2 Jun 2013 03:00:46 - @@ -146,6 +146,77 @@ estack_pop() { eval unset ${__estack_name}\[${__estack_i}\] } Just in passing, one of the places you don't want nullglob messing things up. not sure what you mean. that escapes the [] so that bash doesn't accidentally glob things. when the code actually executes, you don't need to escape things. touch f i d x unset arr declare -A arr arr[f]=1234 arr[idx]=4321 echo ${arr[f]} ${arr[idx]} __estack_name=arr __estack_i=f eval unset ${__estack_name}\[${__estack_i}\] echo ${#arr[@]} __estack_i=idx eval unset ${__estack_name}\[${__estack_i}\] echo ${#arr[@]} seems to work +# is not specified, the var will be unset. +evar_push_set() { + local var=$1 + evar_push ${var} + case $# in + 1) unset ${var} ;; + 2) eval ${var}=\$2 ;; I wish you wouldn't use eval for this. I know it's technically okay here, or would be if you verified the parameter, but bash has printf -v for this purpose: interesting, i hadn't seen that before ... looks new to bash-3.1. /me tucks that into his tool belt. although it doesn't quite work in the edge case where the value is an empty string. consider: unset x printf -v x '' echo ${x+set} that should show set, but it does not. i'll have to keep `eval ${var}=` when the value we're setting is empty. or just keep the eval code since i have to do eval anyways at that point. i'll report it upstream to the bash guys. printf -v $1 '%s' $2 2/dev/null || die unable to set: '$1' to: '$2' Note you should verify the variable name, ime, irrespective of a die on the printf (or eval in sh.) It's much better feedback to the developer using the routine. i don't think it's worth it. if you screw up the name, it tends to be a one- time cost, and the error shown is pretty clear as to where it's coming from. printf -v also works with array members btw, if you do decide to extend in that direction: i don't think i will, but i probably can convert the other core stack code to use this ... + : ${cnt:=bad} + [[ -n ${cnt//[0-9]} ]] die ${FUNCNAME}: first arg must be a number: $* + ;; Though a generic is_int function comes in much handier, ime. yeah, and we have other code that wants this (a simple grep for 0-9 in eclass/ shows a bunch of hits) - # Some people like to make dirs of patches w/out suffixes (vim) + # We have to force sorting to C so that the wildcard expansion # is consistent #471666. + evar_push_set LC_COLLATE C + # Some people like to make dirs of patches w/out suffixes (vim). set -- $1/*${EPATCH_SUFFIX:+.${EPATCH_SUFFIX}} + evar_pop Have to say I'd just do this in a function adding to a locally-scoped array, if it were me, eg local args; foo $1 $EPATCH_SUFFIX; set -- ${args[@]}; unset args foo() { local LC_COLLATE=C args=($1/*${2:+.$2}) } since i plan on using these funcs in other places, using the existing api keeps things simple though I'd prefer it if EPATCH_SUFFIX were allowed to be a list, personally. wouldn't really make this any easier -mike signature.asc Description: This is a digitally signed message part.
[gentoo-dev] Re: evar_push/pop helpers
On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote: simple set of helpers to save/restore a variable in a limited section of code you can see an example of it in action at the end of the file where i need to tweak epatch (and no, doing `LC_COLLATE=C set -- ` does not work). -mike --- eutils.eclass 22 May 2013 05:10:29 - 1.421 +++ eutils.eclass 2 Jun 2013 03:00:46 - @@ -146,6 +146,77 @@ estack_pop() { eval unset ${__estack_name}\[${__estack_i}\] } Just in passing, one of the places you don't want nullglob messing things up. +# @FUNCTION: evar_push +# @USAGE: variable to save [more vars to save] +# @DESCRIPTION: +# This let's you temporarily modify a variable and then restore it (including +# set vs unset semantics). Arrays are not supported at this time. +# +# For example: +# @CODE +#evar_push LC_ALL +#export LC_ALL=C +#... do some stuff that needs LC_ALL=C set ... +#evar_pop +# +## You can also save/restore more than one var at a time +#evar_push BUTTERFLY IN THE SKY +#... do stuff with the vars ... +#evar_pop # This restores just one var, SKY +#... do more stuff ... +#evar_pop 3 # This pops the remaining 3 vars +# @CODE +evar_push() { + local var val + for var ; do + [[ ${!var+set} == set ]] \ + val=${!var} \ + || val=${___ECLASS_ONCE_EUTILS} + estack_push evar ${var} ${val} + done +} + +# @FUNCTION: evar_push_set +# @USAGE: variable to save [new value to store] +# @DESCRIPTION: +# This is a handy shortcut to save and temporarily set a variable. If a value +# is not specified, the var will be unset. +evar_push_set() { + local var=$1 + evar_push ${var} + case $# in + 1) unset ${var} ;; + 2) eval ${var}=\$2 ;; I wish you wouldn't use eval for this. I know it's technically okay here, or would be if you verified the parameter, but bash has printf -v for this purpose: printf -v $1 '%s' $2 2/dev/null || die unable to set: '$1' to: '$2' Note you should verify the variable name, ime, irrespective of a die on the printf (or eval in sh.) It's much better feedback to the developer using the routine. Here's what we currently use in sh: # lib_key_ok $input_name # is the input name ok as a variable or key identifier lib_key_ok(){ case $1 in ''|[![:alpha:]_]*|*[![:alnum:]_]*) return 1 ;; *) return 0 esac } # lib_check_key foo bar.. # die if any param is not lib_key_ok lib_check_key() { local i for i; do lib_key_ok $i || die bad key: '$i' done } So I'd probably just use: lib_key_ok $1 || die $FUNCNAME: bad varname: '$1' in this context, or amend the check_key function to use: .. || die ${FUNCNAME[1]}: bad key '$i' - since you're working in bash by definition. printf -v also works with array members btw, if you do decide to extend in that direction: $ set -- 'foo[2]' 'array madness' $ printf -v $1 %s $2 $ echo '${foo[2]}' 'array madness' And yeah, you can compose the variable name, eg: set -- foo 'array 1' 1 printf -v $1${3:+[$3]} %s $2 echo '${foo[1]}' So long as the developer using the routines knows about quoting, that works fine. (If they don't, you have bigger problems.) + *) die ${FUNCNAME}: incorrect # of args: $* ;; + esac +} + +# @FUNCTION: evar_pop +# @USAGE: [number of vars to restore] +# @DESCRIPTION: +# Restore the variables to the state saved with the corresponding +# evar_push call. See that function for more details. +evar_pop() { + local cnt=$1 might as well use: cnt=${1:-bad} + case $# in + 0) cnt=1 ;; + 1) and lose this line. + : ${cnt:=bad} + [[ -n ${cnt//[0-9]} ]] die ${FUNCNAME}: first arg must be a number: $* + ;; Though a generic is_int function comes in much handier, ime. is_int() { [[ $1 $1 != *[^0-9]* ]] } 1) is_int $1 || die .. cnt=$1 (I tend to guard against 0? as well, as octal inputs tend to mess things up later. I'm sure you can see the case for sh.) + *) die ${FUNCNAME}: only accepts one arg: $* ;; + esac + + local var val + while (( cnt-- )) ; do + estack_pop evar val || die ${FUNCNAME}: unbalanced push + estack_pop evar var || die ${FUNCNAME}: unbalanced push + [[ ${val} == ${___ECLASS_ONCE_EUTILS} ]] \ + unset ${var} \ + || eval ${var}=\${val} again: printf -v $var %s $val Though I'd rather this were in an if, so you can test for error better: if [[ $val = $___ECLASS_ONCE_EUTILS ]]; then unset $var 2/dev/null || die unable to unset: '$var' else printf -v $var %s $val 2/dev/null