Operador ternario (? :) en Bash

441

¿Hay alguna manera de hacer algo como esto?

int a = (b == 5) ? c : d;

usando Bash?

En_t8
fuente
1
La respuesta de @ dutCh muestra que bashtiene algo similar al "operador ternario", sin embargo, en bashesto se llama el "operador condicional" expr?expr:expr(vea la man bashsección de "Evaluación aritmética"). Tenga en cuenta que el bash"operador condicional" es complicado y tiene algunas trampas.
Trevor Boyd Smith
Bash tiene un operador ternario para enteros y funciona dentro de la expresión aritmética ((...)). Ver Shell Aritmética .
codeforester
1
Tal como mencionó @codeforester, el operador ternario trabaja con expansión $(( ))aritmética y evaluación aritmética (( )). Ver también https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Respuestas:

490

operador ternario ? :es solo una forma corta deif/else

case "$b" in
 5) a=$c ;;
 *) a=$d ;;
esac

O

 [[ $b = 5 ]] && a="$c" || a="$d"
ghostdog74
fuente
103
Tenga en cuenta que el =operador prueba la igualdad de cadenas, no la igualdad numérica ( [[ 05 = 5 ]]es decir, es falsa). Si desea una comparación numérica, use -eqen su lugar.
Gordon Davisson
10
Es más una forma corta paraif/then/else
vol7ron
15
Es una forma genial de utilizar el comportamiento de cortocircuito para obtener un efecto de operador ternario :) :) :)
mtk
66
¿Por qué el [[y]]? funciona igual de bien así: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs
83
La cond && op1 || op2construcción tiene un error inherente: si op1tiene un estado de salida distinto de cero por cualquier razón, el resultado se convertirá en silencio op2. if cond; then op1; else op2; fies una línea también y no tiene ese defecto.
ivan_pozdeev
375

Código:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
Vladimir
fuente
58
esto es mejor que los otros ... el punto sobre el operador terciario es que es un operador, por lo tanto, su contexto adecuado está en una expresión, por lo tanto, debe devolver un valor.
nic ferrier
2
Esta es la forma más concisa. Tenga en cuenta que si la parte con echo "$c"es un comando con varias líneas con alias (como echo 1; echo 2), debe encerrarlo entre paréntesis.
Matt
2
Esto también capturará cualquier salida del comando probado (sí, en este caso particular, "sabemos" que no produce ninguno).
ivan_pozdeev
8
Esta cadena de operadores solo se comporta como un operador ternario si está seguro de que el comando posterior &&no tendrá un estado de salida distinto de cero. De lo contrario, a && b || c se ejecutará "inesperadamente" csi atiene éxito pero bfalla.
chepner
155

Si la condición es simplemente verificar si se establece una variable, incluso hay una forma más corta:

a=${VAR:-20}

asignará al avalor de VARsi VARestá configurado, de lo contrario le asignará el valor predeterminado20 ; esto también puede ser el resultado de una expresión.

Como señala Alex en el comentario, este enfoque se denomina técnicamente "Expansión de parámetros".

nemesisfixx
fuente
66
En el caso de pasar un parámetro de cadena con guiones, tuve que usar comillas: a=${1:-'my-hyphenated-text'}
saranicole
1
enlace para los perezosos : hay operadores adicionales que solo sustitutos ( :-)
Justin Wrobel
10
@JustinWrobel - desafortunadamente no hay sintaxis como ${VAR:-yes:-no}.
Ken Williams
76
if [ "$b" -eq 5 ]; then a="$c"; else a="$d"; fi

La cond && op1 || op2expresión sugerida en otras respuestas tiene un error inherente: si op1tiene un estado de salida distinto de cero, en op2silencio se convierte en el resultado; el error tampoco se detectará en el -emodo. Entonces, esa expresión solo es segura de usar si op1nunca puede fallar (por ejemplo :,true si una orden interna, o la asignación de variables sin ningún tipo de operaciones que pueden fallar (como la división y llamadas OS)).

Tenga en cuenta las ""citas. El primer par evitará un error de sintaxis si $bestá en blanco o tiene espacios en blanco. Otros evitarán la traducción de todos los espacios en blanco en espacios individuales.

ivan_pozdeev
fuente
1
También he visto prefijos, creo que [ "x$b" -eq "x" ]solía probar específicamente la cadena vacía. Me gusta usar las sintaxis utilizadas por zsh ('EXPANSIÓN DE PARÁMETROS' en la página de manual de zshexpn) pero no sé mucho sobre su portabilidad.
John P
46
(( a = b==5 ? c : d )) # string + numeric
holandés
fuente
31
Esto es bueno para comparaciones numéricas y asignaciones, pero dará resultados impredecibles si lo usa para comparaciones y asignaciones de cadenas ... (( ))trata cualquiera / todas las cadenas como0
Peter.O
3
Esto también se puede escribir:a=$(( b==5 ? c : d ))
joeytwiddle
33
[ $b == 5 ] && { a=$c; true; } || a=$d

Esto evitará ejecutar la parte después de || por accidente cuando el código entre && y || falla

Sir Athos
fuente
Esto todavía no detectará el error en el -emodo: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev
2
Use el :bulit-in en lugar de trueguardar execun programa externo.
Tom Hale
@ivan_pozdeev ¿Hay alguna forma de usar &&... ||y aún atrapar un comando fallido entre ellos?
Tom Hale
2
@TomHale No. &&y, ||por definición, se aplican a todo el comando antes que ellos. Entonces, si tiene dos comandos antes (independientemente de cómo se combinen), no puede aplicarlo solo a uno de ellos y no al otro. Usted puede emular if/ then/ elselógica con variables bandera, pero ¿por qué molestarse si hay if/ then/ elseadecuada?
ivan_pozdeev
14

El comando let admite la mayoría de los operadores básicos que uno necesitaría:

let a=b==5?c:d;

Naturalmente, esto funciona solo para asignar variables; No puede ejecutar otros comandos.

emú
fuente
24
es exactamente equivalente a ((...)), por lo que solo es válido para expresiones aritméticas
drAlberT
13

Aquí hay otra opción donde solo tiene que especificar la variable que está asignando una vez, y no importa si su asignación es una cadena o un número:

VARIABLE=`[ test ] && echo VALUE_A || echo VALUE_B`

Solo un pensamiento. :)

Jasonovich
fuente
Una desventaja importante: también capturará el stdout de [ test ]. Por lo tanto, la construcción solo es segura de usar si "sabe" que el comando no genera nada para stdout.
ivan_pozdeev
También lo mismo que stackoverflow.com/questions/3953645/ternary-operator-in-bash/… más la necesidad de cotizar y escapar de cotizaciones en su interior. La versión tolerante al espacio se verá así VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev el
8

Lo siguiente parece funcionar para mis casos de uso:

Ejemplos

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

y se puede usar en un script como este:

RESULT=$(tern 1 YES NO)
echo "The result is $RESULT"

golondrina de mar

function show_help()
{
  echo ""
  echo "usage: BOOLEAN VALUE_IF_TRUE VALUE_IF_FALSE {FALSE_VALUE}"
  echo ""
  echo "e.g. "
  echo ""
  echo "tern 1 YES NO                            => YES"
  echo "tern 0 YES NO                            => NO"
  echo "tern "" YES NO                           => NO"
  echo "tern "ANY STRING THAT ISNT 1" YES NO     => NO"
  echo "ME=$(tern 0 YES NO)                      => ME contains NO"
  echo ""

  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$3" ]
then
  show_help
fi

# Set a default value for what is "false" -> 0
FALSE_VALUE=${4:-0}

function main
{
  if [ "$1" == "$FALSE_VALUE" ]; then
    echo $3
    exit;
  fi;

  echo $2
}

main "$1" "$2" "$3"
Brad Parks
fuente
77
Muy explicativo. Muy completo Muy detallado. Tres cosas me gusta. ;-)
Jesse Chisholm
2
es ternparte de Bash? Mac no parece tenerlo
polaridad
44
oye @ 太極 者 無極 而 生 - ese es el nombre del script bash - guarda esa sección "tern" arriba en un archivo llamado "tern", luego ejecútalo chmod 700 ternen la misma carpeta. Ahora tendrá un terncomando en su terminal
Brad Parks, el
8

Podemos usar las siguientes tres formas en Shell Scripting para el operador ternario:

    [ $numVar == numVal ] && resVar="Yop" || resVar="Nop"

Or

    resVar=$([ $numVar == numVal ] && echo "Yop" || echo "Nop")

Or

    (( numVar == numVal ? (resVar=1) : (resVar=0) ))
Sujay UN
fuente
Esta es en realidad (el primero y el segundo específicamente, de estos tres ejemplos) la respuesta más adecuada a esta pregunta en mi humilde opinión.
netpoetica
6

También hay una sintaxis muy similar para condicionales ternarios en bash:

a=$(( b == 5 ? 123 : 321  ))

Desafortunadamente, solo funciona con números, que yo sepa.

Andre Dias
fuente
5
(ping -c1 localhost&>/dev/null) && { echo "true"; } || {  echo "false"; }
wibble
fuente
4

Puede usar esto si desea una sintaxis similar

a=$(( $((b==5)) ? c : d ))
PATEL DE SACERDOTE
fuente
Esto funciona solo con enteros. Puede escribir más simplemente como a=$(((b==5) ? : c : d))- $((...))sólo se necesita cuando queremos asignar el resultado de la aritmética a otra variable.
codeforester
2

Aquí hay algunas opciones:

1- Use si luego en una línea, es posible.

if [[ "$2" == "raiz" ]] || [[ "$2" == '.' ]]; then pasta=''; else pasta="$2"; fi

2- Escribe una función como esta:

 # Once upon a time, there was an 'iif' function in MS VB ...

function iif(){
  # Echoes $2 if 1,banana,true,etc and $3 if false,null,0,''
  case $1 in ''|false|FALSE|null|NULL|0) echo $3;;*) echo $2;;esac
}

use guiones internos como este

result=`iif "$expr" 'yes' 'no'`

# or even interpolating:
result=`iif "$expr" "positive" "negative, because $1 is not true"` 

3- Inspirado en la respuesta del caso, un uso más flexible y de una línea es:

 case "$expr" in ''|false|FALSE|null|NULL|0) echo "no...$expr";;*) echo "yep $expr";;esac

 # Expression can be something like:     
   expr=`expr "$var1" '>' "$var2"`
Sergio Abreu
fuente
2

Aquí hay una solución general, que

  • también funciona con pruebas de cuerda
  • se siente más bien como una expresión
  • evita cualquier efecto secundario sutil cuando la condición falla

Prueba con comparación numérica

a = $(if [ "$b" -eq 5 ]; then echo "$c"; else echo "$d"; fi)

Prueba con comparación de cadenas

a = $(if [ "$b" = "5" ]; then echo "$c"; else echo "$d"; fi)

Stefan Haberl
fuente
1

Esto es muy parecido a la buena respuesta de Vladimir . Si su "ternario" es un caso de "si es verdadero, cadena, si es falso, vacío", entonces simplemente puede hacer:

$ c="it was five"
$ b=3
$ a="$([[ $b -eq 5 ]] && echo "$c")"
$ echo $a

$ b=5
$ a="$([[ $b -eq 5 ]] && echo "$c")"
$ echo $a
it was five
Bruno Bronosky
fuente
1

para responder a: int a = (b == 5) ? c : d;

solo escribe:

b=5
c=1
d=2
let a="(b==5)?c:d"

echo $a # 1

b=6;
c=1;
d=2;
let a="(b==5)?c:d"

echo $a # 2

recuerda que "expresión" es equivalente a $ ((expresión))

André Hillaire
fuente
0

Una alternativa orientada a cadenas, que usa una matriz:

spec=(IGNORE REPLACE)
for p in {13..15}; do
  echo "$p: ${spec[p==14]}";
done

que salidas:

13: IGNORE
14: REPLACE
15: IGNORE
druida62
fuente