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

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.


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.


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

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))

12

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

count=$(echo "$FIRSTV - $SECONDV"|bc)
echo $count

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í .


@ 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


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
$
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.