Skip to content

Commit

Permalink
review suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
glennj committed Dec 25, 2024
1 parent 8a31522 commit 95ba5c4
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 15 deletions.
2 changes: 2 additions & 0 deletions concepts/functions/.meta/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"glennj"
],
"contributors": [
"kotp",
"IsaacG"
],
"blurb": "Functions in bash programs."
}
63 changes: 48 additions & 15 deletions concepts/functions/introduction.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
# Functions

Many Bash scripts are written in a strictly imperative style: execute one command, then execute another command, and so on.
But often you'll need to have a group of commands that conceptually perform a single purpose.
Sometimes you need to group together a sequence of commands that conceptually perform a single purpose.
This is where _functions_ come in.

## Defining a Function

You declare a function is one of two ways.
The first is a "portable" style
You can declare a function in two ways.
The first is a "portable" style:

```bash
funcname () { COMMANDS; }
my_function () { COMMANDS; }
```

This is the style that was created with the original Bourne shell.
This is the style from the original Bourne shell.

Alternately, you can use the `function` keyword

```bash
function funcname { COMMANDS; }
function my_function { COMMANDS; }
```

There is no difference between the two styles.

## Function Parameters

Functions, once defined, act like any other command (builtin or not).
Like any command, you can provide _arguments_ for your functions.
Like any command, you can provide _arguments_ to your functions.
Inside the functions, you access the arguments using the _positional parameters_, `$1`, `$2`, etc.
(Recall, we learned about positional parameters in the [Variables][variables] concept.)

~~~~exercism/advanced
The special parameter `$0` is not changed inside a function; it is still the name of the executing script.
The currently executing function can access its name with the `$FUNCNAME` variable.
See [3.4.2 Special Parameters][special] in the manual.
[special]: https://www.gnu.org/software/bash/manual/bash.html#Special-Parameters
~~~~

## Variables

You can define variables inside a function.
If you declare the variables with the `local` command then the _scope_ of the variable is limited to the current function (and to any functions called from it).
If you declare the variables with the `local` command, the _scope_ of the variable is limited to the current function (and to any functions called by it).
Otherwise, the variable is placed in the _global scope_.

Local variables can have the same name as a global variable.
Expand All @@ -59,14 +63,22 @@ myfunc
echo "back in the global scope, $x == 5"
```

This outputs

```none
in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5
```

Inside a function, you can access variables from the _caller_'s scope.
That means you can use global variables, or local variables that are declared in some function that calls this one.
That means you can use global variables, as well as local variables that were declared in the caller (or in some function that calls the caller).

~~~~exercism/advanced
Technically, "global" is not the right word to use.
Assignments to non-local variables will assign to the variable with that name that has been declared in some previous scope, up to the global scope.
To expand a variable in a function, Bash will traverse up the call stack, as far as the global scope, to find a function where that variable name has been declared.
This example is taken from the bash manual
This example is adapted from the [Shell Functions][man-funcs] section of the manual:
```bash
func1() {
Expand All @@ -89,6 +101,9 @@ The output is:
In func2, var = func1 local
In func2, var = global
```
Similarly, _assigning_ a value to a variable will assign it _in the scope where it was declared_.
This "action at a distance" can create hard-to-follow code, as it is not always obvious where a variable was assigned a value.
~~~~

## Return Value
Expand Down Expand Up @@ -116,6 +131,8 @@ else
fi
```

Using `return` with no arguments returns a zero status.

~~~~exercism/note
Note that the `check_password` function can be simplified to:
Expand All @@ -132,25 +149,25 @@ check_password () { [[ $1 == "secret" ]]; }
The return status of a function is just a number.
How can a function produce output?

Your function simply emits output on standard output.
Your function can print to standard output.
Use the familiar _command substitution_ to capture it:

```bash
d6 () { echo $(( 1 + RANDOM % 6 )); }
d6 () { echo "$(( 1 + RANDOM % 6 ))"; }

die=$( d6 )
echo "You rolled a $die."
```

### Using Both the Output and the Status

The exit status of a function is still available to use when you are capturing the output.
The exit status of a function is available to use even when you are capturing the output.

```bash
roll () {
local n=$1
if (( 4 <= n && n <= 20 )); then
echo $(( 1 + RANDOM % n )) # exit status is 0
echo "$(( 1 + RANDOM % n ))" # exit status is 0
else
return 1
fi
Expand All @@ -169,5 +186,21 @@ fi
Functions can call themselves recursively.
By default, there is no limit to the depth of recursion.

An example:

```bash
factorial() {
local n=$1
if ((n <= 1)); then
echo "1"
else
local prev=$("$FUNCNAME" "$((n - 1))")
echo "$((n * prev))"
fi
}

factorial 5 # => 120
```

[variables]: https://exercism.org/tracks/bash/concepts/variables
[man-funcs]: https://www.gnu.org/software/bash/manual/bash.html#Shell-Functions

0 comments on commit 95ba5c4

Please sign in to comment.