Bash puede hacer aritmética, pero solo con números enteros. Si necesitas hacer cálculos de coma flotante, tendrás que recurrir a algún programa externo (como awk o bc).
Los operadores aritméticos aparecen listados (en orden de precedencia) en el manual: Aritmética del shell.
Además, Bash solo realiza operaciones aritméticas en determinados lugares (conocidos como «contextos» aritméticos).
La expansión aritmética se parece a la sustitución de comandos, pero con paréntesis dobles.
$(( expression ))
Esta sintaxis se expande al resultado de evaluar la expresión. Se usa habitualmente para capturar el resultado en una variable:
x=$(( 3 + 4 ))
La cantidad de espacios en blanco dentro de los paréntesis depende de ti. Es una cuestión de estilo y legibilidad.
x=$(( 3 + 4 ))
y=$((5+6))
A tu amable autor le gusta añadir espacios para que la expresión aritmética sea más visible dentro de los paréntesis.
El condicional aritmético se realiza entre paréntesis dobles sin el $ inicial:
(( expression ))
Esto no se expande al resultado. Sin embargo, sí produce un estado de salida.
Dentro de ((, Bash usa el mismo sentido de verdadero/falso que el lenguaje C: cero es falso, distinto de cero es verdadero.
¡Esto es lo contrario de cómo Bash evalúa los estados de salida!
Esta sintaxis se usa normalmente en dos lugares:
como la parte de COMANDO de un condicional if o un bucle while.
if (( x == y - 1 )); then ...
while (( y > 0 )); do ...
como un comando independiente para modificar el valor de una variable.
(( x = x + 1 ))
echo "new value for x is $x"
Ten cuidado al usar la aritmética de esta forma junto con set -e.
Si la expresión se evalúa a cero, el estado de salida será distinto de cero y Bash terminará.
x=0
(( x++ ))
echo "you will see this"
set -e
x=0
(( x++ ))
echo "shell exits on the previous line; you won't see this"
Para un análisis más detallado del operador ++, consulta este artículo sobre postincremento frente a preincremento
Hay algunos otros lugares que son contextos aritméticos:
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
La parte del índice de un array indexado numéricamente.
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
Para saber más sobre los arrays, lee el documento del concepto Arrays.
Las partes de desplazamiento y longitud de la expansión ${var:offset:length}.
(( expression )) a let "expresson".
A diferencia de la construcción del condicional aritmético, la expresión let está sujeta a la expansión de nombres de fichero y a la división de palabras, y requiere comillas.La asignación de variables, cuando la variable tiene el «atributo de entero».
# variable x does not have the integer attribute
x="3 + 4"
declare -p x # => declare -- x="3 + 4"
# variable y has the integer attribute
declare -i y
y="4 + 5"
declare -p y # => declare -i y="9"
En opinión del autor, declare -i tiene un uso limitado.
Su principal ventaja es que permite aritmética «desnuda» (sin la sintaxis de expresión aritmética).
Esto puede causar confusión a quienes leen tu código, ya que las expresiones aritméticas se evalúan en lugares inesperados.
Aquí tienes un aspecto de los contextos aritméticos que mejora la legibilidad.
height=10
width=20
area=$(( height * width ))
¿Te has dado cuenta de que faltan los signos de dólar $ en las variables de la expresión aritmética?
Bash te lo permite.
Hace que las expresiones sean mucho más fáciles de leer.
El manual dice:
Dentro de una expresión, también se puede hacer referencia a las variables del shell por su nombre sin usar la sintaxis de expansión de parámetros.
Incluso funciona con los elementos de un array:
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
Pero otras expresiones de parámetros deben usar su sintaxis especial. Por ejemplo, duplicar la longitud de un string:
s="hello world"
len=$(( ${#s} * 2 ))
Y los parámetros posicionales también. Si los dos primeros parámetros posicionales representan la anchura y la altura:
area=$(( $1 * $2 ))