factorial() ( IFS=\*; let N=$1-1 k="$*" && factorial "$N" "$k" || echo ${2-1} )
A recursive function that leverages the arithmetic expansions evaluated by the let
command, and the significance of the field separator when arguments are expanded using "$*"
.
IFS=\*
concatenates the arguments "$*"
using a *
symbol, which let
interprets as a multiplication operator
let N=$1-1 k="$*"
assigns arithmetic values to variables N
and k
. N
will have a value one less than the first argument; k
will be the product of all the arguments
&&
performs the command immediately to its right if and only if the expression immediately to its left is successful (has exit code 0)
factorial "$N" "$k"
calls itself with the first argument being one less than it was, and the second argument being the cumulative product of the arguments thus far across all iterations
||
performs the command immediately to its right if the previous command failed (has exit code non-zero)
echo ${2-1}
prints the second argument, if there is one (the factorial), or else 1
Finally, notice that we defined the function using the syntax name() (...)
instead of the more conventional name() { ...; }
. When using the format with (...)
, the function body is executed in a sub-shell. This is important to make it safe to overwrite the value of the IFS
variable.
If a positive integer is not the one and only argument passed to it when invoked by the user, the result will be nonsense or an error will be thrown.
fac() { { echo 1; seq $1; } | paste -s -d\* | bc; }
factorial() { local N; eval let N=1 N*={1..$1}; echo "$N"; }