Operator ternary (? :) di Bash


445

Apakah ada cara untuk melakukan hal seperti ini

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

menggunakan Bash?


1
@ dutCh's jawaban menunjukkan bahwa bashmemang memiliki sesuatu yang mirip dengan "operator ternary" namun dalam bashhal ini disebut "operator kondisional" expr?expr:expr(lihat man bashbagian goto "Evaluasi Aritmatika"). Ingat, bash"operator kondisional" itu rumit dan memiliki beberapa gotcha.
Trevor Boyd Smith

Bash memang memiliki operator ternary untuk bilangan bulat dan berfungsi di dalam ekspresi aritmatika ((...)). Lihat Aritmatika Shell .
codeforester

1
Seperti yang disebutkan oleh @codeforester, operator ternary bekerja dengan ekspansi $(( ))aritmatika dan evaluasi aritmethic (( )). Lihat juga https://mywiki.wooledge.org/ArithmeticExpression.
Kai

Jawaban:


491

operator ternary ? :hanyalah bentuk singkat dariif/else

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

Atau

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

103
Perhatikan bahwa =operator menguji kesetaraan string, bukan kesetaraan numerik (mis. [[ 05 = 5 ]]Salah). Jika Anda ingin perbandingan angka, gunakan -eqsaja.
Gordon Davisson

10
Ini lebih dari bentuk pendek untukif/then/else
vol7ron

15
Ini adalah cara jenius untuk memanfaatkan perilaku hubungan singkat untuk mendapatkan efek operator ternary :) :) :)
mtk

6
mengapa [[dan]]? ini berfungsi dengan baik seperti ini: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs

83
The cond && op1 || op2konstruk memiliki bug yang melekat: jika op1memiliki status keluar nol untuk alasan apa pun, hasilnya diam-diam akan menjadi op2. if cond; then op1; else op2; fijuga satu baris dan tidak memiliki cacat itu.
ivan_pozdeev

375

Kode:

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

58
ini lebih baik daripada yang lain ... intinya tentang operator tersier adalah bahwa itu operator, maka konteks yang tepat adalah dalam ekspresi, maka itu harus mengembalikan nilai.
nic ferrier

2
Ini adalah cara yang paling ringkas. Ketahuilah bahwa jika bagian dengan echo "$c"perintah alias, multi-baris (seperti echo 1; echo 2), Anda harus menyertakannya dalam tanda kurung.
Matt

2
Ini juga akan menangkap setiap output dari perintah yang diuji, (ya, dalam kasus khusus ini, kita "tahu" itu tidak menghasilkan apa-apa).
ivan_pozdeev

8
Ini rantai dari operator hanya berperilaku seperti operator ternary jika Anda positif bahwa perintah setelah &&tidak akan memiliki status exit non-nol. Kalau tidak, a && b || c akan "tiba-tiba" berjalan cjika aberhasil tetapi bgagal.
chepner

155

Jika kondisi hanya memeriksa apakah suatu variabel diset, bahkan ada bentuk yang lebih pendek:

a=${VAR:-20}

akan menetapkan anilai VARjika VARdiatur, jika tidak maka akan menetapkan nilai default20 - ini juga bisa merupakan hasil dari ekspresi.

Seperti yang ditulis oleh alex dalam komentar, pendekatan ini secara teknis disebut "Ekspansi Parameter".


6
Dalam hal melewatkan parameter string dengan tanda hubung di dalamnya, saya harus menggunakan tanda kutip: a=${1:-'my-hyphenated-text'}
saranicole

1
tautan untuk pemalas - ada operator tambahan selain pengganti ( :-)
Justin Wrobel

10
@JustinWrobel - sayangnya tidak ada sintaks seperti ${VAR:-yes:-no}.
Ken Williams

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

The cond && op1 || op2ekspresi disarankan dalam jawaban yang lain memiliki bug yang melekat: jika op1memiliki status nol keluar, op2diam-diam menjadi hasilnya; kesalahan juga tidak akan tertangkap dalam -emode. Jadi, ungkapan itu hanya aman digunakan jika op1tidak pernah gagal (mis. :,true jika builtin, atau tugas variabel tanpa operasi yang bisa gagal (seperti divisi dan panggilan OS)).

Perhatikan ""kutipannya. Pasangan pertama akan mencegah kesalahan sintaksis jika $bkosong atau memiliki spasi putih. Yang lain akan mencegah terjemahan semua spasi putih ke dalam ruang tunggal.


1
Saya juga melihat awalan, saya percaya [ "x$b" -eq "x" ]digunakan untuk menguji string kosong secara khusus. Saya suka menggunakan sintaks yang digunakan oleh zsh ('PARAMETER EXPANSION' di halaman manual zshexpn) tapi saya tidak tahu banyak tentang portabilitasnya.
John P


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

31
Ini bagus untuk perbandingan numerik dan tugas, tetapi ini akan memberikan hasil yang tidak terduga jika Anda menggunakannya untuk perbandingan string dan tugas .... (( ))memperlakukan semua / semua string sebagai0
Peter.O

3
Ini juga dapat ditulis:a=$(( b==5 ? c : d ))
joeytwiddle

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

Ini akan menghindari mengeksekusi bagian setelah || secara tidak sengaja ketika kode antara && dan || gagal


Ini masih tidak akan menangkap kesalahan dalam -emode: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev

2
Gunakan :bulit-in bukan trueuntuk menyimpan exec-ing program eksternal.
Tom Hale

@ivan_pozdeev Apakah ada cara untuk menggunakan &&.. ||dan masih menangkap perintah yang gagal di antara mereka?
Tom Hale

2
@ TomHale No. &&dan ||menurut definisi berlaku untuk seluruh perintah di depan mereka. Jadi, jika Anda memiliki dua perintah sebelumnya (terlepas dari bagaimana mereka digabungkan), Anda tidak dapat menerapkannya hanya untuk salah satu dari mereka dan bukan yang lain. Anda dapat meniru if/ then/ elselogika dengan variabel flag, tetapi mengapa repot jika ada if/ then/ elsepantas?
ivan_pozdeev

14

The membiarkan mendukung perintah sebagian besar operator dasar yang satu akan membutuhkan:

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

Secara alami, ini hanya berfungsi untuk menetapkan variabel; tidak dapat menjalankan perintah lain.


24
itu persis setara dengan ((...)), jadi itu hanya berlaku untuk ekspresi aritmatika
drAlberT

13

Berikut adalah opsi lain di mana Anda hanya perlu menentukan variabel yang Anda tetapkan sekali, dan tidak masalah apakah penugasan Anda adalah string atau angka:

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

Hanya pemikiran saja. :)


Kelemahan utama: itu juga akan menangkap stdout [ test ]. Jadi konstruksinya hanya aman untuk digunakan jika Anda "tahu" bahwa perintah tersebut tidak menghasilkan apa pun untuk stdout.
ivan_pozdeev

Juga sama dengan stackoverflow.com/questions/3953645/ternary-operator-in-bash/… ditambah kebutuhan untuk mengutip dan menghindari kutipan di dalam. Versi yang toleran ruang akan terlihat seperti VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`".
ivan_pozdeev

8

Tampaknya ini berfungsi untuk kasus penggunaan saya:

Contohnya

$ tern 1 YES NO                                                                             
YES

$ tern 0 YES NO                                                                             
NO

$ tern 52 YES NO                                                                            
YES

$ tern 52 YES NO 52                                                                         
NO

dan dapat digunakan dalam skrip seperti:

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

tiga barang

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
Sangat jelas. Sangat lengkap. Sangat bertele-tele. Tiga hal yang saya sukai. ;-)
Jesse Chisholm

2
adalah ternbagian dari Bash? Mac sepertinya tidak memilikinya
nonopolaritas

4
hey @ 太極 者 無極 而 生 - itulah nama skrip bash - simpan bagian "tiga" di atas ke file bernama "tiga", lalu jalankan chmod 700 terndalam folder yang sama. Sekarang Anda akan memiliki ternperintah di terminal Anda
Brad Parks

8

Kita dapat menggunakan tiga cara berikut dalam Shell Scripting untuk operator ternary:

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

Or

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

Or

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

Ini sebenarnya (khusus pertama dan kedua, dari tiga contoh ini) jawaban yang paling tepat untuk pertanyaan ini IMHO.
netpoetica

6

Ada juga sintaks yang sangat mirip untuk kondisional ternary di bash:

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

Sayangnya, ini hanya bekerja dengan angka - sejauh yang saya tahu.


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

4

Anda dapat menggunakan ini jika Anda ingin sintaks yang sama

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

Ini hanya berfungsi dengan bilangan bulat. Anda dapat menulisnya lebih sederhana karena a=$(((b==5) ? : c : d))- $((...))diperlukan hanya ketika kami ingin menetapkan hasil aritmatika ke variabel lain.
codeforester

2

Berikut ini beberapa opsi:

1- Gunakan jika lain dalam satu baris, itu mungkin.

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

2- Tulis fungsi seperti ini:

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

gunakan skrip di dalam seperti ini

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

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

3- Terinspirasi dalam jawaban kasus, penggunaan satu baris yang lebih fleksibel adalah:

 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

Inilah solusi umum, itu

  • bekerja dengan tes string juga
  • terasa agak seperti ekspresi
  • Menghindari efek samping halus saat kondisi gagal

Uji dengan perbandingan angka

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

Uji dengan perbandingan String

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


2

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

cukup tulis:

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

ingat bahwa "ekspresi" sama dengan $ ((ekspresi))


1

Ini mirip dengan jawaban baik Vladimir . Jika "ternary" Anda adalah kasus "jika benar, string, jika salah, kosong", maka Anda dapat melakukannya:

$ 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

0

Alternatif berorientasi string, yang menggunakan array:

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

yang keluaran:

13: IGNORE
14: REPLACE
15: IGNORE
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.