Re: language inconsistency(wart) & RFE
On Wed, Oct 21, 2015 at 07:19:30PM -0700, Linda Walsh wrote: > Not to mention your statement doesn't work: My code worked. I tested it. If you take working code and mangle it according to your incredibly bizarre notions of how bash code should look, and it doesn't work, then the problem is on your end. > function nint { > local i j k > for ((i=0; i<10; ++i)) ; do j+=i; k+=j ;done THAT. IS. NOT. HOW. YOU. DO. ARITHMETIC. You want to perform integer addition? Then you use the syntax that specifies integer addition. j+=i is the syntax that specifies string concatenation. To perform arithmetic, you use $((..)) or ((..)) or let. nint() { local i j k for ((i=0; i<10; i++)); do ((j+=i, k+=j)); done declare -p i j k } imadev:~$ nint declare -- i="10" declare -- j="45" declare -- k="165" I have stated this before, but apparently it didn't sink it, so I'll try again in all caps: DO NOT USE declare -i EVER. If you use declare -i then you BREAK the ability to read your script and see what a command is doing. You pervert the string concatention syntax into performing integer addition, but only some of the time, and you can't tell which times that is. (OO people call this "overloading" and think that it's a feature rather than an abomination.) (I do not and never will understand OO people. They must have been brainwashed.) So now that you know not to use declare -i, all of your other problems just go away! You use local when you want local variables, and you don't use any declaration at all when you want global (or dynamically scoped) variables. That provides the ability you have been asking for: to decide on a case-by-case basis which variables will be local and which will not. The end.
Re: language inconsistency(wart) & RFE
Greg Wooledge wrote: On Sat, Oct 17, 2015 at 08:55:31AM -0700, Linda Walsh wrote: If I needed a way to declare something global, yes... But what I am wanting is a way to allow changing the defaults of the implicit variable creation (which could still be explicitly declared with "-g" if one wanted their result to be made global. So you are basically saying you want all of your function variables to be local --- No... only ones where 'shopt -s auto_local' was in effect. but you are too lazy to write 'local i j k' and you want bash to do it for you? local i j k doesn't define j as an array or k as a hash or g (w/-g ) as a global or any case modification vars. Not to mention your statement doesn't work: function nint { local i j k for ((i=0; i<10; ++i)) ; do j+=i; k+=j ;done echo "i=$i, j=$j, k=$k" } nint && echo "($i, $j, $k)" i=10, j=ii, k=jj (, , ) You declared them all the same, but at the end of the function, j and k do not have integer values. Trying to init them: function nint { local i=0 j=0 k=0 for ((i=0; i<10; ++i)) ; do j+=i k+=j done my -p i j k; echo "i=$i, j=$j, k=$k" } nint && echo "($i, $j, $k)" declare -- i="10" declare -- j="0ii" declare -- k="0jj" i=10, j=0ii, k=0jj (, , ) --- Now we see that i & j & k all have the same attributes ... yet only 'i' has an integer value. If you could declare all the vars in 1 statement w/different types: declare -i INT -a ARRAY -A HASH --- That would be a huge improvement. But instead,if you use a 'for' statement, without pre-declaring all the vars used, you end up leaking variables: function nint { for ((i=0; i<10; ++i)) ; do j+=i k+=j done my -p i j k; echo "i=$i, j=$j, k=$k" } nint && echo "($i, $j, $k)" declare -- i="10" declare -- j="ii" declare -- k="jj" i=10, j=ii, k=jj (10, ii, jj) --- "leaking variables" into the external environment is almost always considered bad-practice. The default that bash encourages with its default behavior is for all implicitly used vars in a function to be leaked to the global level. From a program maintenance and development standpoint, having such a horrible default with no way to override it just seems really icky. Also I think you are completely misrepresenting the dynamic variable scope system that bash uses. Variables are not just global or local. There's an entire stack of them. When you reference a variable (let's say i) inside a function, bash searches up through the call stack looking for a variable named i until it finds one. Since functions cannot return values to their callers, the entire system of "put values into an upper-scope variable so the caller can see them" would break if your proposal of automatic localization were to be adopted. # Pick unbiased random number from 0 to N-1 ($1 = N) # Returns value in variable r. rand() { local max=$((32768 / $1 * $1)) while (( (r=$RANDOM) >= max )); do :; done r=$(( r % $1 )) } foo() { local r rand 6 echo "I rolled $((r+1))" } foo # r is not visible here Under your proposal, the variable r which is defined locally in foo, and is up-scope-visible to rand (so that rand can put a return value into it), would also be defined locally within r, so there would be no way to return a value from rand to foo. (If you want to attack "language warts", start with the inability to return values from functions to their callers!)
Re: language inconsistency(wart) & RFE
Here is an example of what I consider to be strange and/or inconsistent behavior ( tests 2 & 3 fail). test 3's failure is a real puzzler: - #!/bin/bash shopt -s expand_aliases ; alias my=declare alias array='my -a' int='my -i' dmp () { printf "%q\n" "${*:?}" ; } ip=10.20.30.40 array parts=() (IFS=.; parts=( $ip ) ) array answers=( '10 20 30 40' '1 .2 .3 .4' '1 222 .3 .4' '192.168.0.1/24::{ [ipaddr]="192.168.0.1/24" [address]="192.168.0.1" [prefixlen]="24" ; }' ) # array addr_fields=(ipaddr address prefixlen) assignparts() { parts=( $ip ) ; } tst0 () { my IFS=. ; assignparts ; echo -E "${parts[@]}"; } tst1 () { my IFS=0 ; assignparts ; echo -E "${parts[@]}"; } tst2 () { parts[1]=222; echo -E "${parts[@]}" ; } tst3 () { my ipaddr="$1"; shift; int status=0 my pat='^([^/]+)/([0-9]+)\s*$' my address prefixlen if [[ ! $ipaddr =~ $pat ]]; then echo >&2 "Error in ip/netsize format: \"$ipaddr\"" status=1 else address=${BASH_REMATCH[1]} prefixlen=${BASH_REMATCH[2]} my out="" for flds in "${addr_fields[@]}"; do out+="[$flds]=\"${!flds}\" " done printf "{ %s; }" "$out" fi return $status } int passno=0 tests=0 my fmt="Case %d got/Expected:\n \"%q\"\n \"%q\"\n" testor () { int tstno my out="" for tstno in {0..3}; do tests+=1 exp="${answers[tstno]}" if [[ $exp =~ :: ]]; then my args="${exp%::*}" exp="${exp#*::}" out="$(tst$tstno $args)" else out="$(tst$tstno)" fi if [[ $out != $exp ]]; then printf >&2 "$fmt" "$tstno" "$out" "$exp" continue fi passno+=1 done } testor echo "Passed $passno/$tests tests." output: Case 2 got/Expected: "222" "1\ 222\ .3\ .4" Case 3 got/Expected: "\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\ \[prefixlen\]=\"24\"\ \;\ \}" "\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\ \[prefixlen\]=\"24\"\ \;\ \}" Passed 2/4 tests. The outputs for case 3 look identical -- I was using %s to print them out, but switched to "%q", to ensure no hidden chars... case 2 -- ??? Why didn't it only change the 1 member?
Re: language inconsistency(wart) & RFE
Greg Wooledge wrote: --- please note locations of 'auto_local' # at beginning shopt -s auto_local # Pick unbiased random number from 0 to N-1 ($1 = N) # Returns value in variable r. rand() { shopt -u auto_local local max=$((32768 / $1 * $1)) while (( (r=$RANDOM) >= max )); do :; done r=$(( r % $1 )) shopt -s auto_local } foo() { local r rand 6 echo "I rolled $((r+1))" } foo # r is not visible here Under your proposal, the variable r which is defined locally in foo, and is up-scope-visible to rand (so that rand can put a return value into it), would also be defined locally within r, so there would be no way to return a value from rand to foo. Under my proposal, it is a shopt option that can be turned on or off. When it is off, things behave as they do no. When it is on, vars w/o a specific declarator, or declarator+"-g switch" would be default to be local. (If you want to attack "language warts", start with the inability to return values from functions to their callers!) --- They can -- unfortunately, it is by printing to, usually, to stdout, and reading it in the parent (I agree this is a rather roundabout and inefficient way to send return vals... I've thought about that, but running into inconsistencies in the dynamic variable propagation. It's hard to define an "alternate syntax" when the primary syntax doesn't consistently yield the same answer. So need to figure that out first. Hopefully the toggling of 'auto_local' isn't hard to understand: it's like adding a -i or -l flag to var declaration -- doesn't affect current values in vars, but only new assignments.
Re: language inconsistency(wart) & RFE
On 10/16/15 9:18 PM, Linda Walsh wrote: > Ok, thinking this from a different way. > > shopt -s implicit_vars_local > or > shopt -s localize_func_implicit_vars whatever... > > Right now, in a function, you *can* use local in a function > to make a local var. Thing is, both 'declare' and 'typeset' also > make a *local* var in a function, unless the "-g" switch is used. > > I.e. All standard, overt ways (local declare typeset) of creating > a var in a function all result in it being local, BUT, (and I think this is > an ugly wart), any *implicit vars* without local, or the misleading declare > or typeset, become global. All variables are created at the global scope unless explicitly designated as local with local/declare/typeset. Simple and straightforward. (Dynamic scoping does change that a little, as has been covered ad nauseum.) -- ``The lyf so short, the craft so long to lerne.'' - Chaucer ``Ars longa, vita brevis'' - Hippocrates Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/
Re: language inconsistency(wart) & RFE
On Sat, Oct 17, 2015 at 08:55:31AM -0700, Linda Walsh wrote: > If I needed a way to declare something global, yes... > But what I am wanting is a way to allow changing the defaults > of the implicit variable creation (which could still be > explicitly declared with "-g" if one wanted their result to be > made global. So you are basically saying you want all of your function variables to be local, but you are too lazy to write 'local i j k' and you want bash to do it for you? Also I think you are completely misrepresenting the dynamic variable scope system that bash uses. Variables are not just global or local. There's an entire stack of them. When you reference a variable (let's say i) inside a function, bash searches up through the call stack looking for a variable named i until it finds one. Since functions cannot return values to their callers, the entire system of "put values into an upper-scope variable so the caller can see them" would break if your proposal of automatic localization were to be adopted. # Pick unbiased random number from 0 to N-1 ($1 = N) # Returns value in variable r. rand() { local max=$((32768 / $1 * $1)) while (( (r=$RANDOM) >= max )); do :; done r=$(( r % $1 )) } foo() { local r rand 6 echo "I rolled $((r+1))" } foo # r is not visible here Under your proposal, the variable r which is defined locally in foo, and is up-scope-visible to rand (so that rand can put a return value into it), would also be defined locally within r, so there would be no way to return a value from rand to foo. (If you want to attack "language warts", start with the inability to return values from functions to their callers!)
Re: language inconsistency(wart) & RFE
isabella parakiss wrote: Maybe you can just use this?alias declare='declare -g' If I needed a way to declare something global, yes... But what I am wanting is a way to allow changing the defaults of the implicit variable creation (which could still be explicitly declared with "-g" if one wanted their result to be made global. For example, I use some aliases in documenting the intended usage of vars... especially with respect to integers, arrays and hashes. Declaring a hash by calling it a 'hash' is somewhat generic, but if I call it a 'map', it is more clear from a documentation standpoint that I am trying to use something as a way to map names to a value. So I will tend to pre-declare many of my vars, but some are just an annoyance to predeclare. Like readarray -t config <~/.toolrc -- read a config file into an array, and then parse it for relevant options && then throw the "config array" out when done parsing it. Having to "declare" a temporary, _first_, before usage is a bit backwards... Obvious example is using throw-away names as index or iterator vars. I believe it to be rare, that people use "for nam in "${names[@]}"; or "for ((i=0;i<10;++i));" as a means to declare nam or 'i' as "global variables. They are too meaningless for use as a global, and much more likely to collide with usage of such 'tmp' names in other functions that may be called in a nested fashion. Allowing the option to default to private state, would take care of accident collisions where one loop uses 'i', but calls some util function elsewhere that also uses 'i' and ending up with strange side-effects. It removes default behavior to suppress accidental "spooky actions at a distance", where one or both functions didn't pre-declare 'i' in a func. It seems to me, that pre-declaring a variable draws attention to it -- which is more wanted if it is a global, but drawing attention to 'i' in a forloop by pre-declaring it makes the code more confusing, as you are drawing attention to a temporary who's value you usually won't care about after the function has exit'ed. Allowing an option to change this default, allows for more portable, more reliable and less confusing code to be written, which would seem like a desirable goal.
Re: language inconsistency(wart) & RFE
On 10/17/15, Linda Walsh wrote: > Ok, thinking this from a different way. > > shopt -s implicit_vars_local > or > shopt -s localize_func_implicit_vars whatever... > > Right now, in a function, you *can* use local in a function > to make a local var. Thing is, both 'declare' and 'typeset' also > make a *local* var in a function, unless the "-g" switch is used. > > I.e. All standard, overt ways (local declare typeset) of creating > a var in a function all result in it being local, BUT, > (and I think this is an ugly wart), > any *implicit vars* without local, or the > misleading declare or typeset, become global. > > examples: In these two for statements, used in functions, 'i' > becomes global: > > for((i=0; i<10; ++i)); do : done > for i in {1..10}; do : done > > And same with 'myarray': > readarray myarray=$(echo "one";echo "two") > > and reads and assignments, and 'lets' > read ln < <(echo "one"; echo "two") > ln2="one two" > read ln3 <<< "one two" > > but if this isn't a potential for confusion: > >> function aa { > read ln < <(echo "one"; echo "two") > ln2="12" > read ln3 <<< "one two" > ar1=(one two) > typeset -i ln2=34 > typeset -a ar1=(three four) > } >> whence aa > aa is a function > aa () > { > read ln < <(echo "one"; echo "two"); > ln2="12"; > read ln3 <<< "one two"; > ar1=(one two); > typeset -i ln2=34; > typeset -a ar1=(three four) > } >> aa >> declare -p ln ln2 ln3 ar1 > declare -- ln="one" > declare -- ln2="12" > declare -- ln3="one two" > declare -a ar1='([0]="one" [1]="two")' > > !!! -- sure looks like I was trying to set the "type" of ln2 > and ar1... boy could that be confusing... > > > So, how about a "shopt" > to declare that **what's implicity declared in funcs, stays in funcs** > maybe shopt -s vegasvars ?. > > but seriously -- it's so odd that anything you declare explicitly > becomes local, while implicit vars default to global -- > I know standards and compat must keep it that way... BUT > it would have made more sense to have > implicit vars in a function always be 'local' > and maybe have explicit declarators be global > (which has effectively been done with -g)...but > originally, I also thought it strange that 'declare/typeset' > were equivalent to 'local' inside a function. > > This way, you wouldn't have to change any syntax > parsing functions and there certain isn't ANYTHING > that would look like perl, even though perl was > originally designed to be like shell with many shell standard > functions built-in. > > ??? > > > Maybe you can just use this?alias declare='declare -g' --- xoxo iza
language inconsistency(wart) & RFE
Ok, thinking this from a different way. shopt -s implicit_vars_local or shopt -s localize_func_implicit_vars whatever... Right now, in a function, you *can* use local in a function to make a local var. Thing is, both 'declare' and 'typeset' also make a *local* var in a function, unless the "-g" switch is used. I.e. All standard, overt ways (local declare typeset) of creating a var in a function all result in it being local, BUT, (and I think this is an ugly wart), any *implicit vars* without local, or the misleading declare or typeset, become global. examples: In these two for statements, used in functions, 'i' becomes global: for((i=0; i<10; ++i)); do : done for i in {1..10}; do : done And same with 'myarray': readarray myarray=$(echo "one";echo "two") and reads and assignments, and 'lets' read ln < <(echo "one"; echo "two") ln2="one two" read ln3 <<< "one two" but if this isn't a potential for confusion: function aa { read ln < <(echo "one"; echo "two") ln2="12" read ln3 <<< "one two" ar1=(one two) typeset -i ln2=34 typeset -a ar1=(three four) } whence aa aa is a function aa () { read ln < <(echo "one"; echo "two"); ln2="12"; read ln3 <<< "one two"; ar1=(one two); typeset -i ln2=34; typeset -a ar1=(three four) } aa declare -p ln ln2 ln3 ar1 declare -- ln="one" declare -- ln2="12" declare -- ln3="one two" declare -a ar1='([0]="one" [1]="two")' !!! -- sure looks like I was trying to set the "type" of ln2 and ar1... boy could that be confusing... So, how about a "shopt" to declare that **what's implicity declared in funcs, stays in funcs** maybe shopt -s vegasvars ?. but seriously -- it's so odd that anything you declare explicitly becomes local, while implicit vars default to global -- I know standards and compat must keep it that way... BUT it would have made more sense to have implicit vars in a function always be 'local' and maybe have explicit declarators be global (which has effectively been done with -g)...but originally, I also thought it strange that 'declare/typeset' were equivalent to 'local' inside a function. This way, you wouldn't have to change any syntax parsing functions and there certain isn't ANYTHING that would look like perl, even though perl was originally designed to be like shell with many shell standard functions built-in. ???