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

时间:2010-10-17 14:38:52

标签: bash syntax conditional-operator

有没有办法做这样的事情

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

使用Bash?

21 个答案:

答案 0 :(得分:384)

三元运算符? :只是if/else

的缩写形式
case "$b" in
 5) a=$c ;;
 *) a=$d ;;
esac

或者

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

答案 1 :(得分:292)

代码:

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

答案 2 :(得分:118)

如果条件只是检查是否设置了变量,那么甚至还有一个较短的形式:

a=${VAR:-20}
如果设置了a

会将VAR的值分配给VAR,否则会为其分配默认值20 - 这也可能是表达。

正如Alex在评论中指出的那样,这种方法在技术上被称为“参数扩展”。

答案 3 :(得分:56)

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

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

请注意""引号。如果$b为空或具有空格,则第一对将阻止语法错误。其他人会阻止将所有空格转换为单个空格。

答案 4 :(得分:41)

(( a = b==5 ? c : d )) # string + numeric

答案 5 :(得分:29)

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

这将避免在||之后执行该部分在&&和和||失败。

答案 6 :(得分:11)

这是另一个选项,您只需要指定一次分配的变量,分配字符串或数字无关紧要:

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

只是一个想法。 :)

答案 7 :(得分:10)

let 命令支持大多数人需要的基本操作符:

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

当然,这仅适用于分配变量;它无法执行其他命令。

答案 8 :(得分:7)

以下似乎适用于我的用例:

实施例

$ 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"

燕鸥

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"

答案 9 :(得分:5)

(ping -c1 localhost&>/dev/null) && { echo "true"; } || {  echo "false"; }

答案 10 :(得分:5)

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

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

Or

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

Or

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

答案 11 :(得分:4)

这是一个通用的解决方案,

  • 也可以使用字符串测试
  • 感觉就像一个表情
  • 在条件失败时避免任何细微的副作用

通过数值比较进行测试

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

使用字符串比较进行测试

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

答案 12 :(得分:3)

如果你想要类似的语法

,你可以使用它
a=$(( $((b==5)) ? c : d ))

答案 13 :(得分:3)

bash中的三元条件也有一个非常相似的语法:

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

不幸的是,据我所知,它仅适用于数字。

答案 14 :(得分:2)

以下是一些选项:

1-如果在一行中使用if else,则可以。

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

2-写一个这样的函数:

 # 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"`

答案 15 :(得分:2)

最简单的三元

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

此示例将确定您是否使用 homebrew 来安装 bat

如果为 true,您将看到“是”

如果为 false,您将看到“否”

我在此处添加了 -q 以抑制 grep ped 字符串输出,因此您只能看到“是”或“否”

你真正想要的模式是这样的

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

使用 bashzsh 测试

答案 16 :(得分:1)

回答: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))

答案 17 :(得分:0)

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

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

输出:

13: IGNORE
14: REPLACE
15: IGNORE

答案 18 :(得分:0)

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

为了解决这个问题,我编写了一个行为正常的三元函数,它甚至使用了 ?: 运算符:

编辑 - 新解决方案

这是我的新解决方案,既不使用 $IFS 也不使用 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 单独分隔每个命令,除了由于 &&|| 运算符而应该跳过的命令。每当命令以 [] 或标志开头时,它都会使用 !

有两种方法可以向它传递命令:

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

NB 我发现没有办法将 &&|| 运算符作为未加引号的参数传入,因为它们是函数名和别名的非法字符,我发现没有办法覆盖 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'

答案 19 :(得分:0)

这很像exception。如果您的“三元”是“如果为true,则为字符串,如果为false,为空”,则只需执行以下操作:

$ 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

答案 20 :(得分:0)

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

这允许语法:

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

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

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

./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 可能会起作用,但没有骰子。