Paréntesis en aritmética expr: 3 * (2 + 1)

expr no parece que le gusten los paréntesis (usado en matemáticas a la prioridad explícita del operador):

expr 3 * (2 + 1) bash: syntax error near unexpected token `(" 

¿Cómo expresar la prioridad del operador en bash?

Respuesta

Otra forma de utilizar let bash incorporado:

$ let a="3 * (2 + 1)" $ printf "%s\n" "$a" 9 

Nota

Como @ Stéphane Chazelas señaló , en bash debe usar ((...)) para hacer aritmética sobre expr o let para legibilidad.

Para portabilidad, use $((...)) como @Bernhard respuesta .

Comentarios

  • +1 ¡Aún más legible! Publiqué mi pregunta + respuesta pensando que sería útil para mis compañeros usuarios de Linux, pero ahora me estoy beneficiando mucho de las otras respuestas 🙂
  • Hay ‘ no hay razón para usar let. ‘ no es más estándar o portátil que (( a = 3 * (2 + 1) )) (ambos provienen de ksh y sólo están disponibles en ksh, bash y zsh) y ‘ es menos legible o fácil de citar. Use a=$((3 * (2 + 1))) para ser portátil.
  • Yo ‘ no lo digo ‘ es incorrecto, ‘ solo digo que no debería usarse porque hay mejores alternativas (una para legibilidad ((a = 3 * (2 + 1) )), una para la portabilidad a=$((3 * (2 + 1)))), por lo que ‘ no es una nota en su contra o su respuesta, sino en contra de que sea la respuesta seleccionada y la máxima puntuación .
  • @St é phaneChazelas: ¡Actualicé mi respuesta!
  • Siempre he usado a=1 $[a+2] o a=1 b=2 $[a+b]. ¿Es su razón para evitar esa sintaxis?

Respuesta

Puede usar la expansión aritmética en su lugar.

echo "$(( 3 * ( 2 + 1 ) ))" 9 

En mi opinión personal, esto se ve un poco mejor que usar expr.

De man bash

Expansión aritmética La expansión aritmética permite la evaluación de una expresión aritmética y la sustitución del resultado. El formato para la expansión aritmética es:

 $((expression)) 

La expresión se trata como si estuviera entre comillas dobles, pero una comilla doble entre paréntesis no se trata especialmente. Todos los tokens de la expresión experimentan expansión de parámetros, expansión de cadenas, sustitución de comandos y eliminación de comillas. Las expansiones aritméticas pueden estar anidadas.

La evaluación se realiza de acuerdo con las reglas enumeradas a continuación en EVALUACIÓN ARITMÉTICA. Si la expresión no es válida, bash imprime un mensaje que indica un error y no se produce ninguna sustitución.

Comentarios

  • Aparte de la legibilidad, tampoco ‘ no requiere bifurcar un proceso adicional para hacer la aritmética; ‘ s manejado por el propio shell.
  • Tenga en cuenta que en los shells POSIX, ‘ está sujeto a la palabra dividir, por lo que ‘ es un buen hábito citarlo en contextos de lista.
  • Cuando intento esto en el shell bash obtengo ‘ Nombre de variable ilegal. »

Respuesta

No hay razón para usar expr para aritmética en shells modernos.

POSIX define el $((...)) operador de expansión. De modo que puede usarlo en todos los shells compatibles con POSIX (el sh de todos los gustos modernos de Unix, dash, bash, yash, mksh, zsh, posh, ksh … ).

a=$(( 3 * (2 + 1) )) a=$((3*(2+1))) 

ksh también introdujo un let incorporado que se le pasa el mismo tipo de expresión aritmética, no se expande en algo pero devuelve un estado de salida basado en si la expresión resolv es a 0 o no, como en expr:

if let "a = 3 * (2 + 1)"; then echo "$a is non-zero" fi 

Sin embargo, como las citas lo hacen incómodo y no muy legible (no en la misma medida que expr por supuesto), ksh también introdujo un ((...)) forma alternativa:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then echo "$a is non-zero and 3 > 1" fi ((a+=2)) 

que es mucho más legible y debería usarse en su lugar.

let y ((...)) solo están disponibles en ksh, zsh y bash.Se debe preferir la sintaxis $((...)) si se necesita portabilidad a otros shells, expr solo se necesita para shells tipo Bourne anteriores a POSIX (normalmente el shell Bourne o versiones anteriores del shell Almquist).

En el frente que no es Bourne, hay algunos shells con operador aritmético incorporado:

  • csh / tcsh (en realidad, el primer shell de Unix con evaluación aritmética incorporada):

    @ a = 3 * (2 + 1) 
  • akanga (basado en rc)

    a = $:"3 * (2 + 1)" 
  • como nota histórica, la versión original del shell de Almquist, como se publicó en Usenet en 1989, tenía un expr incorporado (en realidad se fusionó con test), pero se eliminó más tarde.

Comentarios

  • Aprendo algo nuevo todos los días de ti, St é phane. ¡Aprecio mucho su conocimiento del shell POSIX!
  • ¿Qué tal : $((a = a*2))?
  • ¿Qué pasa si tengo un punto flotante? Mi expresión es a = $ ((-14 + 0.2 * (1 + 2 + 3))). El token de error es » .2 * (1 + 2 + 3) »
  • @Blaise, entonces tú ‘ d necesita un shell que admita puntos flotantes en $((...)) como zsh, ksh93 o yash.

Answer

expr es un comando externo, no es una sintaxis de shell especial. Por lo tanto, si desea que expr vea los caracteres especiales del shell, debe protegerlos del análisis del shell citandolos. Además, expr necesita que cada número y operador se pase como un parámetro separado. Por lo tanto:

expr 3 \* \( 2 + 1 \) 

A menos que esté trabajando en un sistema Unix antiguo de los años 70 o 80, hay muy pocas razones para usar expr. En los viejos tiempos, los shells no tenían una forma incorporada de realizar operaciones aritméticas, y en su lugar había que llamar a la utilidad expr. Todos los shells POSIX tienen aritmética incorporada a través de la sintaxis de expansión aritmética .

echo "$((3 * (2 + 1)))" 

El La construcción $((…)) se expande al resultado de la expresión aritmética (escrita en decimal). Bash, como la mayoría de los shells, solo admite aritmética de enteros módulo 2 64 (o módulo 2 32 para versiones anteriores de bash y algunos otros shells en máquinas de 32 bits).

Bash ofrece una sintaxis de conveniencia adicional cuando desea realizar asignaciones o probar si una expresión es 0 pero no le importa el resultado. Esta construcción también existe en ksh y zsh pero no en sh simple.

((x = 3 * (2+1))) echo "$x" if ((x > 3)); then … 

Además de la aritmética de enteros, expr ofrece algunas funciones de manipulación de cadenas. Estas también están incluidas en las características de los shells POSIX, excepto por una: expr STRING : REGEXP prueba si la cadena coincide con la expresión regular especificada. Un shell POSIX no puede hacer esto sin herramientas externas, pero bash puede con [[ STRING =~ REGEXP ]] (con una diferente sintaxis de expresiones regulares expr es una herramienta clásica y usa BRE, bash usa ERE).

A menos que esté manteniendo scripts que se ejecutan en sistemas de hace 20 años, no necesita saber que expr existió alguna vez. Utilice aritmética de shell.

Comentarios

  • expr foo : '\(.\)' también realiza extracción de texto. bash ‘ s BASH_REMATCH logra algo similar. También hace una comparación de cadenas, algo que POSIX [ no hace (aunque uno podría imaginar formas de usar sort para eso).
  • subrayar como marcador de posición de sintaxis — ¿eres un Schemer, @Giles? :]
  • @RubyTuesdayDONO No ‘ t usé un guión bajo aquí. ¿Está malinterpretando la ELIPSIS HORIZONTAL U + 2026? Si es así, intente usar una fuente más grande.
  • @Giles – está bien, sí, solo parece un guión bajo debido a mi tamaño de fuente. para mí, » Schemer » es un complemento, y ‘ no es como puntos suspensivos versus guión bajo cambia el significado de todos modos … no hay necesidad de ser sarcástico al respecto: /

Responder

Use paréntesis con comillas:

expr 3 "*" "(" 2 "+" 1 ")" 9 

Las comillas evitan que bash interprete el paréntesis como sintaxis bash.

Comentarios

  • Lo que Nicolas ilustra pero no explica es que los tokens en la línea de comando expr deben estar separados por espacios; asi que; por ejemplo, expr 3 "*" "(2" "+" "1)" no funcionará . (Además, por cierto, probablemente no necesite citar +.)
  • Los paréntesis no son ‘ t palabras clave como while y [[, ‘ hacen referencia a la sintaxis. Si fueran palabras clave, no ‘ t se interpretarían como tales en los argumentos de los comandos. Necesita comillas para que bash no ‘ t las analice, sino que vea una cadena literal.

Respuesta

Si tiene bc ..

echo "3 * (2 + 1)"|bc 9 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *