Operator ternary (? :) di Bash

445

Apakah ada cara untuk melakukan hal seperti ini

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

menggunakan Bash?

En_t8
sumber
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"
ghostdog74
sumber
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")
Vladimir
sumber
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".

nemesisfixx
sumber
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.

ivan_pozdeev
sumber
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
Belanda
sumber
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

Pak Athos
sumber
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.

emu
sumber
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. :)

Jasonovich
sumber
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"
Taman Brad
sumber
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) ))
Sujay UN
sumber
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.

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

Anda dapat menggunakan ini jika Anda ingin sintaks yang sama

a=$(( $((b==5)) ? c : d ))
PRIYESH PATEL
sumber
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"`
Sergio Abreu
sumber
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)

Stefan Haberl
sumber
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))

André Hillaire
sumber
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
Bruno Bronosky
sumber
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
druid62
sumber