Toán tử ternary (? :) trong Bash


441

Có cách nào để làm một cái gì đó như thế này không

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

sử dụng Bash?


1
Câu trả lời của @ dutCh cho thấy bashcó một cái gì đó tương tự như "toán tử ternary" tuy nhiên trong bashphần này được gọi là "toán tử điều kiện" expr?expr:expr(xem man bashphần goto "Đánh giá số học"). Hãy nhớ rằng bash"toán tử có điều kiện" là khó khăn và có một số vấn đề.
Trevor Boyd Smith

Bash có một toán tử ternary cho các số nguyên và nó hoạt động bên trong biểu thức số học ((...)). Xem số học Shell .
codeforester

1
Như @codeforester đã đề cập, toán tử ternary hoạt động với mở rộng $(( ))số học và đánh giá số học (( )). Xem thêm https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Câu trả lời:


489

toán tử ternary ? :chỉ là hình thức ngắn củaif/else

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

Hoặc là

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

103
Lưu ý rằng =toán tử kiểm tra sự bằng nhau của chuỗi, không phải là đẳng thức số (nghĩa [[ 05 = 5 ]]là sai). Nếu bạn muốn so sánh số, sử dụng -eqthay thế.
Gordon Davisson

10
Đây là một dạng ngắn hơn choif/then/else
vol7ron

15
Đó là một cách thiên tài để sử dụng hành vi ngắn mạch để có được hiệu ứng toán tử ternary :) :) :)
mtk

6
tại sao [[và]]? nó hoạt động tốt như thế này: [$ b = 5] && a = "$ c" || a = "$ d"
kdub

83
Cấu cond && op1 || op2trúc có một lỗi cố hữu: nếu op1có trạng thái thoát khác không vì bất kỳ lý do gì, kết quả sẽ âm thầm trở thành op2. if cond; then op1; else op2; filà một dòng quá và không có khuyết điểm đó.
ivan_pozdeev

375

Mã số:

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

58
điều này tốt hơn các yếu tố khác ... điểm quan trọng của toán tử bậc ba là nó là toán tử, do đó bối cảnh thích hợp của nó nằm trong một biểu thức, do đó nó phải trả về một giá trị.
nic ferrier

2
Đây là cách ngắn gọn nhất. Xin lưu ý rằng nếu phần có echo "$c"là một lệnh bí danh, nhiều dòng (như echo 1; echo 2), bạn nên đặt nó trong ngoặc đơn.
Matt

2
Điều này cũng sẽ nắm bắt bất kỳ đầu ra nào của lệnh đã được thử nghiệm (vâng, trong trường hợp cụ thể này, chúng tôi "biết" nó không tạo ra bất kỳ).
ivan_pozdeev

8
Đây chuỗi các nhà khai thác chỉ hoạt động như một nhà điều hành ternary nếu bạn đang tích cực mà các lệnh sau &&sẽ không có một trạng thái thoát khác không. Nếu không, a && b || c sẽ "bất ngờ" chạy cnếu athành công nhưng bthất bại.
chepner

155

Nếu điều kiện chỉ đơn thuần là kiểm tra xem một biến có được đặt hay không, thậm chí còn có dạng ngắn hơn:

a=${VAR:-20}

sẽ gán cho agiá trị của VARif VARđược đặt, nếu không nó sẽ gán cho nó giá trị mặc định 20- đây cũng có thể là kết quả của một biểu thức.

Như alex ghi chú trong bình luận, cách tiếp cận này về mặt kỹ thuật được gọi là "Mở rộng tham số".


6
Trong trường hợp truyền tham số chuỗi có dấu gạch nối trong đó, tôi đã phải sử dụng dấu ngoặc kép: a=${1:-'my-hyphenated-text'}
saranicole

1
liên kết cho người lười biếng - có các nhà khai thác bổ sung thay vì chỉ thay thế ( :-)
Justin Wrobel

10
@JustinWrobel - tiếc là không có cú pháp như thế nào ${VAR:-yes:-no}.
Ken Williams

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

Các cond && op1 || op2biểu hiện gợi ý trong câu trả lời khác có lỗi cố hữu: nếu op1có một trạng thái thoát khác không, op2âm thầm trở thành kết quả; lỗi cũng sẽ không bị bắt trong -echế độ. Vì vậy, biểu hiện đó là chỉ an toàn để sử dụng nếu op1không bao giờ có thể thất bại (ví dụ :, truenếu một dựng sẵn, hoặc gán biến mà không cần bất kỳ hoạt động mà có thể thất bại (như phân chia và gọi điện thoại OS)).

Lưu ý các ""trích dẫn. Cặp đầu tiên sẽ ngăn lỗi cú pháp nếu $btrống hoặc có khoảng trắng. Những người khác sẽ ngăn dịch tất cả các khoảng trắng thành các không gian duy nhất.


1
Tôi cũng đã thấy tiền tố, tôi tin rằng [ "x$b" -eq "x" ]được sử dụng để kiểm tra chuỗi trống cụ thể. Tôi thích sử dụng các cú pháp được sử dụng bởi zsh ('MỞ RỘNG PARAMETER' trong trang web của zshExn) nhưng tôi không biết nhiều về tính di động của chúng.
John P


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

31
Điều này tốt cho so sánh số và bài tập, nhưng nó sẽ cho kết quả không thể đoán trước nếu bạn sử dụng nó để so sánh chuỗi và bài tập .... (( ))xử lý bất kỳ / tất cả các chuỗi như0
Peter.O

3
Điều này cũng có thể được viết:a=$(( b==5 ? c : d ))
joeytwiddle

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

Điều này sẽ tránh thực hiện phần sau | | một cách tình cờ khi mã giữa && và || thất bại


Điều này vẫn sẽ không bắt lỗi trong -echế độ: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev

2
Sử dụng :bulit-in thay vì truelưu exec-ing một chương trình bên ngoài.
Tom Hale

@ivan_pozdeev Có cách nào để sử dụng &&.. ||và vẫn bắt được một lệnh thất bại ở giữa chúng không?
Tom Hale

2
@TomHale Số &&||theo định nghĩa áp dụng cho toàn bộ lệnh trước chúng. Vì vậy, nếu bạn có hai lệnh trước nó (bất kể chúng được kết hợp như thế nào), bạn không thể áp dụng nó cho chỉ một trong số chúng và không phải cho các lệnh khác. Bạn có thể mô phỏng if/ then/ elselogic với các biến cờ, nhưng tại sao phải bận tâm nếu có if/ then/ elseđúng?
ivan_pozdeev

14

Lệnh let hỗ trợ hầu hết các toán tử cơ bản mà người ta sẽ cần:

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

Đương nhiên, điều này chỉ hoạt động để gán các biến; nó không thể thực thi các lệnh khác.


24
nó chính xác tương đương với ((...)), vì vậy nó chỉ hợp lệ cho các biểu thức số học
drAlberT

13

Đây là một tùy chọn khác trong đó bạn chỉ phải xác định biến bạn đang gán một lần và không quan trọng việc gán của bạn là chuỗi hay số:

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

Chỉ là một ý nghĩ. :)


Một nhược điểm lớn: nó cũng sẽ nắm bắt được thiết bị xuất chuẩn [ test ]. Vì vậy, cấu trúc chỉ an toàn để sử dụng nếu bạn "biết" rằng lệnh không xuất bất cứ thứ gì ra thiết bị xuất chuẩn.
ivan_pozdeev

Cũng giống như stackoverflow.com/questions/3953645/ternary-operator-in-bash/. Cộng với sự cần thiết phải trích dẫn và thoát dấu ngoặc kép bên trong. Phiên bản dung sai không gian sẽ như thế nào VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev

8

Sau đây dường như hoạt động cho các trường hợp sử dụng của tôi:

Ví dụ

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

và có thể được sử dụng trong một tập lệnh như vậy:

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

chim nhạn

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"

7
Rất giải thích. Rất đầy đủ. Rất dài dòng. Ba điều tôi thích. ;-)
Jesse Chisholm

2
ternmột phần của Bash? Mac dường như không có nó
phân cực

4
hey @ 太極 者 - đó là tên của tập lệnh bash - lưu phần "tern" đó ở trên vào một tệp có tên "tern", sau đó chạy chmod 700 terntrong cùng một thư mục. Bây giờ bạn sẽ có một ternlệnh trong thiết bị đầu cuối của mình
Brad

8

Chúng ta có thể sử dụng ba cách sau trong Shell Scripting cho toán tử ternary:

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

Or

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

Or

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

Đây thực sự là (cụ thể thứ nhất và thứ hai, trong ba ví dụ này) là câu trả lời thích hợp nhất cho câu hỏi này IMHO.
netpoetica

6

Ngoài ra còn có một cú pháp rất giống nhau cho các điều kiện ternary trong bash:

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

Thật không may, nó chỉ hoạt động với những con số - theo như tôi biết.



4

Bạn có thể sử dụng điều này nếu bạn muốn cú pháp tương tự

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

Điều này chỉ hoạt động với số nguyên. Bạn có thể viết nó đơn giản hơn a=$(((b==5) ? : c : d))- $((...))chỉ cần thiết khi chúng ta muốn gán kết quả của số học cho một biến khác.
codeforester

2

Dưới đây là một số tùy chọn:

1- Sử dụng nếu sau đó khác trong một dòng, nó là có thể.

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

2- Viết một hàm như thế này:

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

sử dụng kịch bản bên trong như thế này

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

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

3- Lấy cảm hứng trong câu trả lời trường hợp, cách sử dụng linh hoạt hơn và một dòng là:

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

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

2

Đây là một giải pháp chung, đó là

  • cũng hoạt động với các bài kiểm tra chuỗi
  • cảm thấy giống như một biểu hiện
  • Tránh mọi tác dụng phụ tinh tế khi tình trạng không thành công

Kiểm tra với so sánh số

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

Kiểm tra so sánh chuỗi

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


1

Điều này giống như câu trả lời tốt của Vladimir . Nếu "ternary" của bạn là trường hợp "nếu đúng, chuỗi, nếu sai, trống", thì bạn có thể chỉ cần làm:

$ 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

1

để trả lời: int a = (b == 5) ? c : d;

chỉ viết:

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

hãy nhớ rằng "biểu thức" tương đương với $ ((biểu thức))


0

Một thay thế theo định hướng chuỗi, sử dụng một mảng:

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

đầu ra nào:

13: IGNORE
14: REPLACE
15: IGNORE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.