Bash中的三元运算符(?:)

有这样的方法吗

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

使用Bash ?

375455 次浏览

三元运算符? :只是if/else的缩写形式

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

 [[ $b = 5 ]] && a="$c" || a="$d"

代码:

a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
(( a = b==5 ? c : d )) # string + numeric
(ping -c1 localhost&>/dev/null) && { echo "true"; } || {  echo "false"; }

命令支持大多数基本操作符:

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

当然,这只适用于分配变量;不能执行其他命令。

如果条件仅仅是检查是否设置了变量,甚至还有更短的形式:

a=${VAR:-20}

如果设置了VAR,则将VAR的值分配给a,否则将为其分配默认值20——这也可以是表达式的结果。

这种方法在技术上被称为“参数扩展”。

[ $b == 5 ] && { a=$c; true; } || a=$d

这将避免意外执行||之后的部分,当&&||失败。

这里有另一个选项,你只需要指定你分配一次的变量,不管你分配的是字符串还是数字:

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

只是一个想法。:)

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

在其他答案中建议的cond && op1 || op2表达式有一个固有的错误:如果op1有一个非零的退出状态,op2会默默地成为结果;在-e模式下错误也不会被捕获。因此,该表达式只有在op1永远不会失败的情况下才安全使用(例如,:true如果是内置的,或变量赋值没有任何可能失败的操作(如除法和操作系统调用))。

注意""引号。如果$b为空白或有空格,第一对将防止语法错误。另一些将阻止将所有空白转换为单个空格。

下面的代码似乎适用于我的用例:

例子

$ tern 1 YES NO
YES
    

$ tern 0 YES NO
NO
    

$ tern 52 YES NO
YES
    

$ tern 52 YES NO 52
NO

并且可以像这样在脚本中使用:

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"

以下是一些选择:

在一行中使用if then else,这是可能的。

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

写一个这样的函数:

 # 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
}

像这样使用内部脚本

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


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

3-在案例回答的启发下,更灵活的一行用法是:

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


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

如果您想要类似的语法,您可以使用它

a=$(( $((b==5)) ? c : d ))

在Shell Scripting中,我们可以使用以下三种方式用于三元运算符:

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


Or


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


Or


(( numVar == numVal ? (resVar=1) : (resVar=0) ))

使用以下准备运行的示例扩展字符串计算的答案。这是利用上面提到的第二种格式。

$ strVar='abc';resVar=$([[ $strVar == 'abc' ]] && echo "Yop" || echo "Nop");echo $resVar
Yop
$ strVar='aaa';resVar=$([[ $strVar == 'abc' ]] && echo "Yop" || echo "Nop");echo $resVar
Nop

在bash中还有一个非常相似但更简单的三元条件语句语法:

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

这很像弗拉基米尔的巧妙回答。如果你的“三元”是一个“如果真,字符串,如果假,空”的情况下,那么你可以简单地做:

$ 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=$(if [ "$b" -eq 5 ]; then echo "$c"; else echo "$d"; fi)

字符串比较测试

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

面向字符串的替代方法,使用数组:

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

输出:

13: IGNORE
14: REPLACE
15: IGNORE

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

只写:

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

记住" expression "等价于$((expression))

上面的答案[[ $b = 5 ]] && a="$c" || a="$d"应该只在您确定&&之后不会有错误时才使用,否则它将错误地执行||之后的部分。

为了解决这个问题,我写了一个三元函数,它甚至使用了?:操作符:

编辑-新的解决方案

这是我的新解决方案,不使用$IFSev(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将每个命令分开,除了由于使用&&||操作符而应该跳过的命令。当命令以!或标志开始时,它使用[]

向它传递命令有两种方式:

  1. 传递单个命令时不加引号,但一定要引用;&&||操作符。
t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq
  1. 传递所有引用的命令:
t 'ls /a ? ls qqq || echo aaa : echo bbb && ls qq'

我发现无法将&&||操作符作为不带引号的参数传递,因为它们是函数名和别名的非法字符,而且我发现无法覆盖bash操作符。

旧的解决方案-使用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'

简单的三元

brew list | grep -q bat && echo 'yes' || echo 'no'

这个例子将决定你是否使用homebrew来安装bat

如果是这样,你会看到“yes”;

如果是假的,你会看到“no”;

我添加了-q来抑制这里的grepped字符串输出,所以你只能看到"yes"或“;no"

真正的模式是这样的

doSomethingAndCheckTruth && echo 'yes' || echo 'no'

bashzsh测试

有些人已经提出了一些不错的替代方案。我想让语法尽可能接近,所以我写了一个名为?的函数。

这允许使用以下语法:

[[ $x -eq 1 ]]; ? ./script1 : ./script2
# or
? '[[ $x -eq 1 ]]' ./script1 : ./script2

在这两种情况下,:都是可选的。所有有空格的参数,值必须加引号,因为它使用eval运行它们。

如果<then><else>子句不是命令,则函数echos是正确的值。

./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
}

显然,如果您希望脚本更短,您可以删除帮助文本。

编辑:我不知道?在文件名中充当占位符。它不像*那样匹配任意数量的字符,而是只匹配一个字符。因此,如果您的工作目录中有一个只有一个字符的文件,bash将尝试将文件名作为命令运行。我不知道怎么解决这个问题。我认为使用command "?" ...args可能会工作,但没有骰子。

还有两个答案

这里有一些思考的方法

Bash整数变量

此外,荷兰弗拉基米尔•ghostdog74的纠正答案,因为这个问题是关于整数 而且标记为:

有这样的方法吗

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

在bash下使用整数有一个很好的方法:

declare -i b=' RANDOM % 3 + 4 ' c=100 d=50 a='  b == 5 ? c : d '; echo $b '-->' $a

这个命令的输出行应该是:

4 --> 50
5 --> 100
6 --> 50

当然,声明整数类型的变量只需要执行一次:

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

题外话:使用字符串作为数学函数:

mathString=' b == 5 ? c : d '
b=5 a=$mathString
echo $a $b
100 5


b=1 a=$mathString
echo $a $b
50 1

基于参数扩展间接

根据布拉德公园druid62的回答,这里有一个不限于整数的方法:

c=50 d=100 ar=([5]=c)
read -p 'Enter B: ' b
e=${ar[b]:-d};echo ${!e}
  • 如果b==5,那么ar[b]就是c间接就是c就是50
  • 否则ar[any value other than 5]是空的,所以参数扩展将默认为d,其中间接100

同样的演示使用数组代替整数

ternArrayDemo(){
local -a c=(foo bar) d=(foo bar baz) e=(empty) ar=([5]=c [2]=d)
local b=${ar[$1]:-e}
b+=[@]      # For array indirection
printf ' - %s\n' "${!b}"
}

然后

ternArrayDemo 0
- empty
ternArrayDemo 2
- foo
- bar
- baz
ternArrayDemo 4
- empty
ternArrayDemo 5
- foo
- bar
ternArrayDemo 6
- empty

或者使用关联数组

ternAssocArrayDemo(){
local -a c=(foo bar) d=(foo bar baz) e=(empty)
local -A ar=([foo]=c[@] [bar]=d[@] [baz]=d[-1])
local b=${ar[$1]:-e[@]}
printf ' - %s\n' "${!b}"
}

然后

ternAssocArrayDemo hello
- empty
ternAssocArrayDemo foo
- foo
- bar
ternAssocArrayDemo bar
- foo
- bar
- baz
ternAssocArrayDemo baz
- baz

这样的方法怎么样:

# 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';
看看条件语句是如何在RxJs中工作的(即过滤器管道)。 是的,从我的角度来看,这是代码复制,但更实用的方法