Is there a way to do something like this
int a = (b == 5) ? c : d;
using Bash?
ternary operator ? :
is just a short form of if/then/else
case "$b" in
5) a=$c ;;
*) a=$d ;;
esac
Or
[[ $b = 5 ]] && a="$c" || a="$d"
=
operator tests for string equality, not numeric equality (i.e. [[ 05 = 5 ]]
is false). If you want numeric comparison, use -eq
instead.
Commented
Oct 17, 2010 at 19:54
cond && op1 || op2
construct has an inherent bug: if op1
has nonzero exit status for whatever reason, the result will silently become op2
. if cond; then op1; else op2; fi
is one line too and doesn't have that defect.
Commented
Apr 15, 2017 at 17:19
Code:
a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
echo "$c"
is an aliased, multi-lined command (like echo 1; echo 2
), you should enclose it in parentheses.
&&
won't have a non-zero exit status. Otherwise, a && b || c
will "unexpectedly" run c
if a
succeeds but b
fails.
echo
and doesn't quote the result. To be honest this approach is shoehorning the concept of a ternary operator into a language that doesn't have one. If I came across this in the wild I would be bewildered
If the condition is merely checking if a variable is set, there's even a shorter form:
a=${VAR:-20}
will assign to a
the value of VAR
if VAR
is set, otherwise it will assign it the default value 20
-- this can also be a result of an expression.
This approach is technically called "Parameter Expansion".
a=${1:-'my-hyphenated-text'}
Commented
Apr 26, 2016 at 16:06
:-
)
Commented
Oct 9, 2018 at 14:07
${VAR:-yes:-no}
.
Commented
Dec 6, 2018 at 21:40
:+
. You can do ${${VAR:+yes}:-no}
... if VAR
is set & non-null, this will expand to yes
and if VAR
is unset or null it expands to no
.
Commented
Oct 19, 2021 at 17:28
if [[ $b -eq 5 ]]; then a="$c"; else a="$d"; fi
The cond && op1 || op2
expression suggested in other answers has an inherent bug: if op1
has a nonzero exit status, op2
silently becomes the result; the error will also not be caught in -e
mode. So, that expression is only safe to use if op1
can never fail (e.g., :
, true
if a builtin, or variable assignment without any operations that can fail (like division and OS calls)).
Note the ""
quotes. They will prevent translation of all whitespace into single spaces.
Double square brackets as opposed to single ones prevent incorrect operation if $b
is equal to a test
operator (e.g. "-z
"; a workaround with [
is [ "x$b" == "xyes" ]
and it only works for string comparison); they also lift the requirement for quoting.
(( a = b==5 ? c : d )) # string + numeric
(( ))
treats any/all strings as 0
[ $b == 5 ] && { a=$c; true; } || a=$d
This will avoid executing the part after || by accident when the code between && and || fails.
-e
mode: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)
-> success 0 further on
Commented
Apr 1, 2016 at 14:14
:
bulit-in instead of true
to save exec
-ing an external program.
&&
.. ||
and still catch a failed command in between them?
&&
and ||
by definition apply to the entire command before them. So, if you have two commands before it (regardless of how they are combined), you cannot apply it to only to one of them and not the other. You can emulate if
/then
/else
logic with flag variables, but why bother if there's if
/then
/else
proper?
Commented
Jan 13, 2017 at 5:35
We can use following three ways in Shell Scripting for ternary operator :
[ $numVar == numVal ] && resVar="Yop" || resVar="Nop"
Or
resVar=$([ $numVar == numVal ] && echo "Yop" || echo "Nop")
Or
(( numVar == numVal ? (resVar=1) : (resVar=0) ))
Update: Extending the answer for string computations with below ready-to-run example. This is making use of second format mentioned above.
$ strVar='abc';resVar=$([[ $strVar == 'abc' ]] && echo "Yop" || echo "Nop");echo $resVar
Yop
$ strVar='aaa';resVar=$([[ $strVar == 'abc' ]] && echo "Yop" || echo "Nop");echo $resVar
Nop
The let command supports most of the basic operators one would need:
let a=b==5?c:d;
Naturally, this works only for assigning variables; it cannot execute other commands.
Here is another option where you only have to specify the variable you're assigning once, and it doesn't matter whether what your assigning is a string or a number:
VARIABLE=`[ test ] && echo VALUE_A || echo VALUE_B`
Just a thought. :)
[ test ]
. So the construct is only safe to use if you "know" that the command doesn't output anything to stdout.
Commented
Jan 9, 2018 at 11:43
VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`"
.
Commented
Jan 23, 2018 at 20:33
There's also a very similar but simpler syntax for ternary conditionals in bash:
a=$(( b == 5 ? 123 : 321 ))
test
results.
The following seems to work for my use cases:
$ tern 1 YES NO
YES
$ tern 0 YES NO
NO
$ tern 52 YES NO
YES
$ tern 52 YES NO 52
NO
and can be used in a script like so:
RESULT=$(tern 1 YES NO)
echo "The result is $RESULT"
#!/usr/bin/env bash
function show_help()
{
ME=$(basename "$0")
IT=$(cat <<EOF
Returns a ternary result
usage: BOOLEAN VALUE_IF_TRUE VALUE_IF_FALSE
e.g.
# YES
$ME 1 YES NO
# NO
$ME 0 YES NO
# NO
$ME "" YES NO
# YES
$ME "STRING THAT ISNT BLANK OR 0" YES NO
# INFO contains NO
INFO=\$($ME 0 YES NO)
EOF
)
echo "$IT"
echo
exit
}
if [ "$1" = "help" ] || [ "$1" = '?' ] || [ "$1" = "--help" ] || [ "$1" = "h" ]; 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" ] || [ "$1" = '' ]; then
echo $3
exit;
fi;
echo $2
}
main "$1" "$2" "$3"
chmod 700 tern
in the same folder. Now you'll have a tern
command in your terminal
Commented
Jul 30, 2017 at 15:48
Here's a general solution, that
Test with numerical comparison
a=$(if [ "$b" -eq 5 ]; then echo "$c"; else echo "$d"; fi)
Test with String comparison
a=$(if [ "$b" = "5" ]; then echo "$c"; else echo "$d"; fi)
(ping -c1 localhost&>/dev/null) && { echo "true"; } || { echo "false"; }
echo "false"
may occur when ping
is successful, but where, for some reason, however unlikely, the echo "true"
part returns a non-zero exit status (see ShellCheck SC2015). To echo "false"
only when ping
fails, regardless of echo "true"
’s exit status, move the first grouping brace {
to the head: { (ping -c1 localhost&>/dev/null) && echo "true"; } || { echo "false"; }
.
You can use this if you want similar syntax
a=$(( $((b==5)) ? c : d ))
a=$(((b==5) ? : c : d))
- $((...))
is needed only when we want to assign the result of the arithmetic to another variable.
Commented
Sep 1, 2018 at 19:09
Some people have already presented some nice alternatives. I wanted to get the syntax as close as possible, so I wrote a function named ?
.
This allows for the syntax:
[[ $x -eq 1 ]]; ? ./script1 : ./script2
# or
? '[[ $x -eq 1 ]]' ./script1 : ./script2
In both cases, the :
is optional. All arguments that have spaces, the values must be quoted since it runs them with eval
.
If the <then>
or <else>
clauses aren't commands, the function echo
s the proper value.
./script; ? Success! : "Failure :("
?() {
local lastRet=$?
if [[ $1 == --help || $1 == -? ]]; then
echo $'\e[37;1mUsage:\e[0m
? [<condition>] <then> [:] <else>
If \e[37;1m<then>\e[0m and/or \e[37;1m<else>\e[0m are not valid commands, then their values are
printed to stdOut, otherwise they are executed. If \e[37;1m<condition>\e[0m is not
specified, evaluates the return code ($?) of the previous statement.
\e[37;1mExamples:\e[0m
myVar=$(? "[[ $x -eq 1 ]] foo bar)
\e[32;2m# myVar is set to "foo" if x is 1, else it is set to "bar"\e[0m
? "[[ $x = *foo* ]] "cat hello.txt" : "cat goodbye.txt"
\e[32;2m# runs cat on "hello.txt" if x contains the word "foo", else runs cat on
# "goodbye.txt"\e[0m
? "[[ $x -eq 1 ]] "./script1" "./script2"; ? "Succeeded!" "Failed :("
\e[32;2m# If x = 1, runs script1, else script2. If the run script succeeds, prints
# "Succeeded!", else prints "failed".\e[0m'
return
elif ! [[ $# -eq 2 || $# -eq 3 || $# -eq 4 && $3 == ':' ]]; then
1>&2 echo $'\e[37;1m?\e[0m requires 2 to 4 arguments
\e[37;1mUsage\e[0m: ? [<condition>] <then> [:] <else>
Run \e[37;1m? --help\e[0m for more details'
return 1
fi
local cmd
if [[ $# -eq 2 || $# -eq 3 && $2 == ':' ]]; then
cmd="[[ $lastRet -eq 0 ]]"
else
cmd="$1"
shift
fi
if [[ $2 == ':' ]]; then
eval "set -- '$1' '$3'"
fi
local result=$(eval "$cmd" && echo "$1" || echo "$2")
if command -v ${result[0]} &> /dev/null; then
eval "${result[@]}"
else
echo "${result[@]}"
fi
}
Obviously if you want the script to be shorter, you can remove the help text.
EDIT: I was unaware that ?
acts as a placeholder character in a file name. Rather than matching any number of characters like *
, it matches exactly one character. So, if you have a one-character file in your working directory, bash will try to run the filename as a command. I'm not sure how to get around this. I thought using command "?" ...args
might work but, no dice.
brew list | grep -q bat && echo 'yes' || echo 'no'
This example will determine if you used homebrew
to install bat
or not yet
If true you will see "yes"
If false you will see "no"
I added the -q
to suppress the grep
ped string output here, so you only see "yes" or "no"
doSomethingAndCheckTruth && echo 'yes' || echo 'no'
Tested with bash
and zsh
&&
fails, then the ||
will be run, which is not quite the desired behavior.
Commented
Jun 18, 2021 at 16:53
Here are some options:
1- Use if then else in one line, it is possible.
if [[ "$2" == "raiz" ]] || [[ "$2" == '.' ]]; then pasta=''; else pasta="$2"; fi
2- Write a function like this:
# 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 inside script like this
result=`iif "$expr" 'yes' 'no'`
# or even interpolating:
result=`iif "$expr" "positive" "negative, because $1 is not true"`
3- Inspired in the case answer, a more flexible and one line use is:
case "$expr" in ''|false|FALSE|null|NULL|0) echo "no...$expr";;*) echo "yep $expr";;esac
# Expression can be something like:
expr=`expr "$var1" '>' "$var2"`
to answer to : int a = (b == 5) ? c : d;
just write:
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
remember that " expression " is equivalent to $(( expression ))
Here's some ways of thinking about this
In addition to, dutCh, Vladimir and ghostdog74's corrects answers and because this question is regarding integer and tagged bash:
Is there a way to do something like this
int a = (b == 5) ? c : d;
There is a nice and proper way to work with integers under bash:
declare -i b=' RANDOM % 3 + 4 ' c=100 d=50 a=' b == 5 ? c : d '; echo $b '-->' $a
The output line from this command should by one of:
4 --> 50
5 --> 100
6 --> 50
Of course, declaring integer type of variable is to be done once:
declare -i a b c d
c=100 d=50 b=RANDOM%3+4
a=' b == 5 ? c : d '
echo $a $b
100 5
b=12 a=b==5?c:d
echo $a $b
50 12
Note: This syntax is recommanded. Declaring property of a variable, then assigning a value is better to be done separately.
mathString=' b == 5 ? c : d '
b=5 a=$mathString
echo $a $b
100 5
b=1 a=$mathString
echo $a $b
50 1
Following answers from Brad Parks and druid62, here is a way not limited to integer:
c=50 d=100 ar=([5]=c)
read -p 'Enter B: ' b
e=${ar[b]:-d};echo ${!e}
b==5
, then ar[b]
is c
and indirection do c
is 50
.ar[any value other than 5]
is empty, so parameter expansion will default to d
, where indirection give 100
.ternArrayDemo(){
local -a c d e ar
local b
c=(foo bar) d=(foo bar baz) e=(empty) ar=([5]=c [2]=d)
b=${ar[$1]:-e}
b+=[@] # For array indirection
printf ' - %s\n' "${!b}"
}
Then
ternArrayDemo 0
- empty
ternArrayDemo 2
- foo
- bar
- baz
ternArrayDemo 4
- empty
ternArrayDemo 5
- foo
- bar
ternArrayDemo 6
- empty
ternAssocArrayDemo(){
local -a c d e
local -A ar
local b
c=(foo bar) d=(foo bar baz) e=(empty)
ar=([foo]=c[@] [bar]=d[@] [baz]=d[-1])
b=${ar[$1]:-e[@]}
printf ' - %s\n' "${!b}"
}
Then
ternAssocArrayDemo hello
- empty
ternAssocArrayDemo foo
- foo
- bar
ternAssocArrayDemo bar
- foo
- bar
- baz
ternAssocArrayDemo baz
- baz
declare -i b=' RANDOM % 3 + 4 '
: TIL that in bash, when a variable is declared as an integer, then the value assigned to it is evaluated as an arithmetic expression.
This is much like Vladimir's fine answer. If your "ternary" is a case of "if true, string, if false, empty", then you can simply do:
$ 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
A string-oriented alternative, that uses an array:
spec=(IGNORE REPLACE)
for p in {13..15}; do
echo "$p: ${spec[p==14]}";
done
which outputs:
13: IGNORE
14: REPLACE
15: IGNORE
What about such approach:
# any your function
function check () {
echo 'checking...';
# Change the following to 'true' to emulate a successful execution.
# Note: You can replace check function with any function you wish.
# Be aware in linux false and true are funcitons themselves. see 'help false' for instance.
false;
}
# double check pattern
check && echo 'update' \
|| check || echo 'create';
See how conditional statements works in the RxJs (i.e. filter pipe). Yes, it is code duplication but more functional approach from my point of view.
The top answer [[ $b = 5 ]] && a="$c" || a="$d"
should only be used if you are certain there will be no error after the &&
, otherwise it will incorrectly excute the part after the ||
.
To solve that problem I wrote a ternary function that behaves as it should and it even uses the ?
and :
operators:
Edit - new solution
Here is my new solution that does not use $IFS
nor ev(a/i)l
.
function executeCmds()
{
declare s s1 s2 i j k
declare -A cmdParts
declare pIFS=$IFS
IFS=$'\n'
declare results=($(echo "$1" | grep -oP '{ .*? }'))
IFS=$pIFS
s="$1"
for ((i=0; i < ${#results[@]}; i++)); do
s="${s/${results[$i]}/'\0'}"
results[$i]="${results[$i]:2:${#results[$i]}-3}"
results[$i]=$(echo ${results[$i]%%";"*})
done
s="$s&&"
let cmdParts[t]=0
while :; do
i=${cmdParts[t]}
let cmdParts[$i,t]=0
s1="${s%%"&&"*}||"
while :; do
j=${cmdParts[$i,t]}
let cmdParts[$i,$j,t]=0
s2="${s1%%"||"*};"
while :; do
cmdParts[$i,$j,${cmdParts[$i,$j,t]}]=$(echo ${s2%%";"*})
s2=${s2#*";"}
let cmdParts[$i,$j,t]++
[[ $s2 ]] && continue
break
done
s1=${s1#*"||"}
let cmdParts[$i,t]++
[[ $s1 ]] && continue
break
done
let cmdParts[t]++
s=${s#*"&&"}
[[ $s ]] && continue
break
done
declare lastError=0
declare skipNext=false
for ((i=0; i < ${cmdParts[t]}; i++ )) ; do
let j=0
while :; do
let k=0
while :; do
if $skipNext; then
skipNext=false
else
if [[ "${cmdParts[$i,$j,$k]}" == "\0" ]]; then
executeCmds "${results[0]}" && lastError=0 || lastError=1
results=("${results[@]:1}")
elif [[ "${cmdParts[$i,$j,$k]:0:1}" == "!" || "${cmdParts[$i,$j,$k]:0:1}" == "-" ]]; then
[ ${cmdParts[$i,$j,$k]} ] && lastError=0 || lastError=1
else
${cmdParts[$i,$j,$k]}
lastError=$?
fi
if (( k+1 < cmdParts[$i,$j,t] )); then
skipNext=false
elif (( j+1 < cmdParts[$i,t] )); then
(( lastError==0 )) && skipNext=true || skipNext=false
elif (( i+1 < cmdParts[t] )); then
(( lastError==0 )) && skipNext=false || skipNext=true
fi
fi
let k++
[[ $k<${cmdParts[$i,$j,t]} ]] || break
done
let j++
[[ $j<${cmdParts[$i,t]} ]] || break
done
done
return $lastError
}
function t()
{
declare commands="$@"
find="$(echo ?)"
replace='?'
commands="${commands/$find/$replace}"
readarray -d '?' -t statement <<< "$commands"
condition=${statement[0]}
readarray -d ':' -t statement <<< "${statement[1]}"
success="${statement[0]}"
failure="${statement[1]}"
executeCmds "$condition" || { executeCmds "$failure"; return; }
executeCmds "$success"
}
executeCmds
separates each command individually, apart from the ones that should be skipped due to the &&
and ||
operators. It uses []
whenever a command starts with !
or a flag.
There are two ways to pass commands to it:
;
, &&
, and ||
operators.t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq
t 'ls /a ? ls qqq || echo aaa : echo bbb && ls qq'
NB I found no way to pass in &&
and ||
operators as parameters unquoted, as they are illegal characters for function names and aliases, and I found no way to override bash operators.
Old solution - uses ev(a/i)l
function t()
{
pIFS=$IFS
IFS="?"
read condition success <<< "$@"
IFS=":"
read success failure <<< "$success"
IFS=$pIFS
eval "$condition" || { eval "$failure" ; return; }
eval "$success"
}
t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq
t 'ls /a ? ls qqq || echo aaa : echo bbb && ls qq'
bash
does have something similar to the "ternary operator" however inbash
this is called the "conditional operator"expr?expr:expr
(seeman bash
goto section "Arithmetic Evaluation"). Keep in mind thebash
"conditional operator" is tricky and has some gotchas.((...))
. See Shell Arithmetic.$(( ))
and arithmethic evaluation(( ))
. See alsohttps://mywiki.wooledge.org/ArithmeticExpression
.