Today's lesson is: intent. Always tell us what you are trying to do. In a script, that means writing comments. In an email, you can write plain text anywhere you want.
It's helpful! Try it! On Tue, Mar 22, 2016 at 10:47:28AM -0400, Adam Danischewski wrote: > $ alias t2='_() { /tmp/m.bsh -d "clarify $@"; }; _' I don't understand why you are creating an alias. Just create a function named t2 and call it, if that's your intent. $ t2() { /tmp/m.bsh -d "clarify $@"; } $ t2 hi there However, see below. > $ t2 hi there > 3 > the optarg is clarify hi, optind is 3 > ### Incorrectly breaks the argument array variable out as separate > single string arguments. It's almost never desirable to combine "$@" with any other string, such as "clarify $@". "$@" is special magic which expands to each positional parameter as a separate word. If you place that inside another string, then the first or last positional parameter (or both) become "contaminated" by the other parts of the string. imadev:~$ set -- hi there imadev:~$ args "$@" 2 args: <hi> <there> imadev:~$ args "clarify $@" 2 args: <clarify hi> <there> imadev:~$ args "$@ mom" 2 args: <hi> <there mom> imadev:~$ args "clarify $@ mom" 2 args: <clarify hi> <there mom> If your intent was to pass 3 arguments, the first being "clarify" and the others being "$@", then you need to separate the two strings: ... /tmp/m.bsh -d clarify "$@" That said, I don't quite understand what you are trying to do in t2. Did you want 3 arguments? Or did you want 2 arguments with the constant string prefixed to each one ("clarify hi" "clarify there")? Or something else entirely? "args" is a script I use to show how the arguments of a command are expanding. imadev:~$ cat bin/args #!/bin/sh printf "%d args:" "$#" printf " <%s>" "$@" echo > I noticed another interesting occurrence as well, I'm not sure if they are > related, to variable names: > > function update() { > local -i VAR=45 > VAR+=-1 > VAR+=$1 > echo $VAR > } I recommend *against* using the -i attribute on variables, because it makes code do surprising things. Your intent may be clear in this simple example, but in larger scripts you'll be unable to tell whether a given line that contains var+=... is doing string concatenation or integer addition. I recommend always using $((...)) or other math context syntax to do arithmetic. That way the reader can immediately tell just by looking at the line, without having to trace the origin of the variable across hundreds of lines of code, that you are doing math. Of course, that's just my opinion. > $ VAR2=2 > $ update VAR2 > 47 I don't get this result, nor do I see how you got it. I get 46. > $ VAR=3 > $ update VAR > 88 ### !? Remember, you are not passing the *value* of VAR (you did not write $VAR). You are passing the actual string "VAR" as the argument. Therefore, within your function VAR+=$1 becomes VAR+=VAR. The VAR on the right hand side is the local variable, shadowing the global variable, so you're just doubling the content of (local) VAR. 44*2 = 88. The value 3 is never used. If your intent (see lesson) is to play around with "passing variables by reference", the short answer is you can't. Read below for the long answer. Bash has no way to pass variables by reference. The closest it has is "declare -n", which creates a "nameref", which is sort of like a symbolic link from one variable name to another. But the names are both resolved in the same scope -- it does NOT let you safely link a local variable in a function to a variable in the caller's scope. Namerefs have exactly the same problem as your example above: if the function's local variable and the caller's-scope variable both have the same name, the reference points to itself. There's no way to get at the caller's variable, because the local variable is shadowing it. In other words, bash doesn't have an [upvar] command (a la Tcl). Freddy Vulto tried to make one; see his work at http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference It's an ugly hack at best. I've never used Freddy's upvar trick in a script. My own preference for returning values from functions is to use a variable with a specific name, and document that this name is special, and should not be used for any other purpose. For example: # Pick unbiased random number from 0 to N-1 ($1 = N) # Returns value in global variable r. rand() { local max=$((32768 / $1 * $1)) while (( (r=$RANDOM) >= max )); do :; done r=$(( r % $1 )) } The caller is then responsible to ensure that it does not use the variable r for any purpose other than retrieving the "return value" from function rand. The caller should make a copy of r after calling rand, because r will be overwritten the next time rand is called. (You could use _r or _rand or whatever you want; the important part is that it's documented, and that you consistently obey your own restrictions.) This works tolerably well as long as you use a different variable for each function's return value. Don't just use "r" for all of them, or you won't be able to call one function from within another. (The script in which I use rand only has that one function.) This does not work for recursive functions, unfortunately. But that's a topic for a different email.