0
 $ factorial() { local N=${1:-1} k=${2:-1} && let k=( k * N ) --N && factorial $N $k || echo $k; }

Nov. 25, 2019, 8:06 a.m.CK

Explanation

Not as short and elegant as the one below, but doesn't call out to other programs and instead uses the let builtin to perform the multiplication. A quick breakdown:

  • local N=${1:=1} k=${2:-1}

    This defines the variables $N and $k and limits their scope to the function, whilst assigning respectively the values of $1 and $2, i.e. the arguments passed to the function. If either of these aren't set, then the default value of 1 is used instead.

  • let k=( k * N ) --N

    let allows mathematical expression to be evaluated and assigned to variables, and this is where $k is overwritten by the product $N with itself. Then the quantity equal to one less than $N is evaluated before $N is overwritten with this quantity. This is because let returns a non-zero status if its final argument is zero, which would not be the case if it were N--. The exit return status lets us determine what happens next.

  • && factorial $N $k || echo $k

    $N now holds a value one less than it did when the function was called. $k holds product of every value of $N passed to each recursive call of the function. If $N is positive, then we recurse once again. If $N is zero, the value of $k is echoed and this should equal the evaluated factorial.