有这样的方法吗

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

使用Bash ?


当前回答

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

这允许使用以下语法:

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

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

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

./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将尝试将文件名作为命令运行。我不知道怎么解决这个问题。我认为使用命令?...Args可能会工作,但是,没有骰子。

其他回答

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

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

注意“”引号。它们将防止将所有空白转换为单个空格。

如果$b等于一个测试操作符(例如,如果$b等于一个测试操作符,则双方括号与单方括号相反,可以防止错误的操作。“- z”;一个变通方法是[是["x$b" == "xyes"],它只适用于字符串比较);他们还取消了报价的要求。

这是通解

也适用于字符串测试 感觉更像是一种表达 避免在条件失效时产生任何微妙的副作用

数值比较试验

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

字符串比较测试

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

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

例子

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

tern

#!/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"
(ping -c1 localhost&>/dev/null) && { echo "true"; } || {  echo "false"; }

上面的答案[[$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将每个命令单独分开,除了由于&&和||操作符而应该跳过的命令。当命令以!开头时,它使用[]。或者一面旗帜。

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

传递单个命令时不加引号,但一定要引用;、&&和||操作符。

t ls / ? ls qqq '||' echo aaa : echo bbb '&&' ls qq

传递所有引用的命令:

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'