During an interview, I asked, in jest, whether I could use Whitespace to solve the problem. The interviewer chuckled and said that if I implemented the algorithm in Bash, I would receive an offer on the spot; jokingly. I did not solve the problem in Bash that day; however, I've been wondering why I haven't tried using Bash for algorithmic interviews. This post is an exploration on the subject.

We break this post into three sections. First, we review some Bash fundamental such as variable assignment, evaluating variables, integer arithmetic, control flow structures, and functions. Second, we show implementations of Fibonacci numbers and the in-place quicksort using Bash. Finally, we discuss the feasibility of using Bash during an algorithmic interview.

## Bash Review

As a brief review, we cover some simple mechanics in Bash such as variable assignment, evaluating variables, integer arithmetic, standard control flow structures, and functions. If you are already familiar with these concepts in Bash, you may skip this section.

### Variables and Values

We begin the review with binding values to variables and evaluating variables. Suppose that we want to bind the string "hello" to the variable x and printing out the value bound to the variable x.

x=hello # binds "hello" to the variable x
echo $x # prints the value bound to the variable x  Binding values to variables and referencing the value in variables are two common operations that we will use. There are two important syntactical nuances to notice here. First, notice that there should be no spaces around the equals sign = for declaration. Second, notice that we prepend the variable x with a dollar sign $x when we want to reference its value.

In Bash, $x is known as parameter expansion where the value bound to the variable is resolved during evaluation. Now that we have some understanding of how variables and values interact, we may begin building functions. ### Integer Arithmetic Operations on integers in addition to equality and inequality relationships require a special syntax known as arithmetic expansion. To return the result of an arithmetic expression, we wrap the expression between $(( and )). The results of this expression are assignable. For example, suppose we wanted express the following assignment:

$$\begin{array}{l} x \leftarrow 2 \\ y \leftarrow x + 2 \end{array}$$

In Bash, we would express this using the arithmetic expansion syntax.

x=2 # binds the number 2 to the variable x
y=$(($x + 3)) # evaluates x + 3 and binds the result to y


There is also a special syntax for expressing integer boolean relationships. We wrap boolean expressions between (( and )). The succeess of the command is determined by the result of the expression: the command succeeds if the expression is true; the command fails otherwise. Suppose we wanted to print a statement if a boolean expression evaluated to true.

(( 3 < 2 )) && echo "3 is less than 2"


Note that the && syntax means that the second command will run only if the previous command succeeded.

### Control Flow Structures

Standard control flow structures include conditionals statements, case statements and loops. We presume that the reader is familiar with these structures and their semantics from other programming languages; hence, we proceed only with a presentation of the syntax.

Conditional statements change their behavior based on the success or failure of an expression expr. An example of an expression could be (( 3 < 2 )).

if expr; then
# statements
else
# statements
fi


Case statements also change their behavior based; however, the behavior is based on pattern matching the results of an expression expr. For example, an expression may be input from a user in a variable x and possible cases may include continue or exit. A special case is * which denotes the default case.

case expr; then
a) # statements
;;
b) # statements
;;
*) # statements
;;
esac


Looping may be achieved through a while statement that continues based on the success or failure of an expression expr. Although for statements exist, we do not cover it in this post.

while expr; do
# statements
done


### Functions

We begin with the syntactic structure of a function in Bash and show how to take function arguments and return values. Suppose that we wanted to implement an increment function that takes a single number as an argument and returns one plus the argument.

increment() {
local x=$1 return$(($x + 1)) }  This example demonstrates four concepts. We began by declaring a function increment. Then, we read the positional parameter $1 into a local variable x.

By default, variables have global scope. We must explicitly declare variables as local to guarantee that a variable has local scope within a function.

Next, before returning from the function, we add one to the value of x using arithmetic expansion $(($x + 1)). Finally, we return the value of the arithmetic expansion.

This function may called by providing a number to increment.

increment 3
echo $? # prints 4  The return value is bound to a special parameter $? which is printed by the echo command.

## Bash Algorithms

Now that we have a fundamental understanding of Bash syntax and semantics, we may proceed implementing some common algorithms. We will not explain the details of the algorithms; only the implementation follows.

### Fibonacci Numbers

Let us begin with a simple problem: Find the $n^\text{th}$ Fibonacci number for any $n > 0$. Recall that Fibonacci numbers follow the sequence

$$\begin{array}{l} f(1) = 0 \\ f(2) = 1 \\ f(n) = f(n - 1) + f(n - 2) \end{array}$$

This is a simple exercise of constructing a recursive function.

fib() {
local n=$1 case$n in
1) return 1 ;;
2) return 1 ;;
esac

fib $(($n - 1))
local fib1=$? fib$(($n - 2)) local fib2=$?

${a[2]} # the element of the array a at index 2  #### Algorithm Implementation Describing the algorithm is outside of the scope of this post. Instead, we refer you to Wikipedia's Quicksort article for the details of the algorithm. Suppose that we have an array a=(4 1 3 2)  We implement the sorting algorithm quicksort that sorts the array a in-place. swap() { a[$1]=$4 a[$3]=$2 } quicksort() { local length=${#a[@]}
quicksort_recursive 0 $(($length - 1))
}

quicksort_recursive() {
local start=$1 local end=$2
if (($start >=$end)); then
return
fi

local pivot=${a[$start]}
local p q pvalue qvalue
p=$(($start + 1))
q=$end while (($p <= $q)); do qvalue=${a[$q]} if (($qvalue < $pivot)); then pvalue=${a[$p]} swap$p $pvalue$q $qvalue p=$(($p + 1)) else q=$(($q - 1)) fi done swap$start $pivot$q $qvalue quicksort_recursive$start $(($q - 1))
quicksort_recursive $(($q + 1)) \$end
}


Glancing at this algorithm, the logic is clear. We are able to express logic using control flows, integer arithmetic, and functions on an array data structures. Many algorithms can be decomposed into similar components.

### Other Classes of Algorithms

Although we have covered two popular algorithms, there may be some concerns that the language would not be able to express other classes of algorithms. These classes may include graph algorithms, numerical algorithms, etc.

A few moments of thought will quickly dismiss many of these concerns. Graphs can be represented densely as matrices which can be simulated by arrays. Alternatively, graphs can be represented sparsely with maps for which Bash has associative arrays. Regarding numerical algorithms, all computations can be piped to the calculator tool bc.

## Bash for Algorithm Interviews

It is clear at this point that Bash is capable of expressing most if not all algorithms commonly given an interview scenario. There are a few soft tradeoffs that we may further outline.

We begin by framing these tradeoffs in a concrete example. Suppose that you are an in an algorithm interview for a company where you are allowed to choose a programming language to interview in. You are the interviewee and the person evaluating you is the interviewer. Suppose now that you wanted to use Bash.