Resta dos variables en Bash

220

Tengo el script a continuación para restar los recuentos de archivos entre dos directorios, pero la COUNT=expresión no funciona. ¿Cual es la sintaxis correcta?

#!/usr/bin/env bash

FIRSTV=`ls -1 | wc -l`
cd ..
SECONDV=`ls -1 | wc -l`
COUNT=expr $FIRSTV-$SECONDV  ## -> gives 'command not found' error
echo $COUNT
toop
fuente

Respuestas:

224

Solo necesita un poco de espacio en blanco adicional alrededor del signo menos y teclas de retroceso:

COUNT=`expr $FIRSTV - $SECONDV`

Tenga en cuenta el estado de salida:

El estado de salida es 0 si EXPRESSION no es nulo ni 0, 1 si EXPRESSION es nulo o 0 .

Tenga esto en cuenta cuando use la expresión en un script bash en combinación con set -e que se cerrará inmediatamente si un comando sale con un estado distinto de cero.

Aaron McDaid
fuente
2
Esta respuesta también funciona en posix shshell. Para la portabilidad, es posible que desee utilizar esta respuesta.
dinkelk
Vale la pena señalar que, según Shellcheck, expr es un código olfativo debido a que es anticuado y difícil de usar: github.com/koalaman/shellcheck/wiki/SC2003
John Hamelink
369

Pruebe esta sintaxis de Bash en lugar de intentar usar un programa externo expr:

count=$((FIRSTV-SECONDV))

Por cierto, la sintaxis correcta de uso expres:

count=$(expr $FIRSTV - $SECONDV)

Pero tenga en cuenta que el uso exprserá más lento que la sintaxis interna de Bash que proporcioné anteriormente.

anubhava
fuente
44
Este formulario es más rápido que usar el programa externo expr.
nsg
Esto funciona sin los backticks, pero ¿puedo saber por qué? +1 para el answe.r
Amal Murali
2
Gracias. Backtick es una vieja sintaxis de shell. BASH admite una nueva $(command)sintaxis para la sustitución de comandos. Además, dado que BASH admite operaciones aritméticas $(( ... )), es mejor no usar una utilidad externaexpr
anubhava
1
Nunca supe que podría hacer referencia a variables sin el "$", muy interesante. Esto funciona en Ubuntu 12,14 solo para su información.
MadHatter
1
@ AlikElzin-kilaka: En bash $(( ... ))se usa para evaluar expresiones aritméticas.
anubhava
30

Puedes usar:

((count = FIRSTV - SECONDV))

para evitar invocar un proceso separado, según la siguiente transcripción:

pax:~$ FIRSTV=7
pax:~$ SECONDV=2
pax:~$ ((count = FIRSTV - SECONDV))
pax:~$ echo $count
5
paxdiablo
fuente
12

El espacio en blanco es importante, exprespera que sus operandos y operadores sean argumentos separados. También tienes que capturar la salida. Me gusta esto:

COUNT=$(expr $FIRSTV - $SECONDV)

pero es más común usar la expansión aritmética integrada:

COUNT=$((FIRSTV - SECONDV))
Karoly Horvath
fuente
12

Así es como siempre hago matemáticas en Bash:

count=$(echo "$FIRSTV - $SECONDV"|bc)
echo $count
Pureferret
fuente
55
eso solo es necesario si se trata de números de coma flotante.
Glenn Jackman
2
Me doy cuenta de eso, pero prefiero acostumbrarme a detectar esos casos con un |bccomando de tipo que omitirlo una o dos veces. Diferentes golpes para diferentes personas como dicen.
Pureferret
5

Para la aritmética de enteros simples, también puede usar el comando let incorporado .

 ONE=1
 TWO=2
 let "THREE = $ONE + $TWO"
 echo $THREE
    3

Para más información sobre let, mira aquí .

Shawn Chin
fuente
@ another.anon.coward Su enlace es mejor que el mío +1. (... y robar el enlace)
Shawn Chin
Tuve muchos problemas para que esto funcionara. Finalmente esto funcionó - let "sanity_check_duration=sanity_check_duration_end_time_delay_sec - sanity_check_duration_start_time_delay_sec"(eliminando el signo de dólar de las variables)
Sandeepan Nath
2

Como alternativa a los 3 métodos sugeridos, puede probar letque realiza operaciones aritméticas en variables de la siguiente manera:

let COUNT=$FIRSTV-$SECONDV

o

let COUNT=FIRSTV-SECONDV

otro.anon.coward
fuente
0

Utiliza Python:

#!/bin/bash
# home/victoria/test.sh

START=$(date +"%s")                                     ## seconds since Epoch
for i in $(seq 1 10)
do
  sleep 1.5
  END=$(date +"%s")                                     ## integer
  TIME=$((END - START))                                 ## integer
  AVG_TIME=$(python -c "print(float($TIME/$i))")        ## int to float
  printf 'i: %i | elapsed time: %0.1f sec | avg. time: %0.3f\n' $i $TIME $AVG_TIME
  ((i++))                                               ## increment $i
done

Salida

$ ./test.sh 
i: 1 | elapsed time: 1.0 sec | avg. time: 1.000
i: 2 | elapsed time: 3.0 sec | avg. time: 1.500
i: 3 | elapsed time: 5.0 sec | avg. time: 1.667
i: 4 | elapsed time: 6.0 sec | avg. time: 1.500
i: 5 | elapsed time: 8.0 sec | avg. time: 1.600
i: 6 | elapsed time: 9.0 sec | avg. time: 1.500
i: 7 | elapsed time: 11.0 sec | avg. time: 1.571
i: 8 | elapsed time: 12.0 sec | avg. time: 1.500
i: 9 | elapsed time: 14.0 sec | avg. time: 1.556
i: 10 | elapsed time: 15.0 sec | avg. time: 1.500
$
Victoria Stuart
fuente