ผู้ประกอบการที่สาม (? :) ใน Bash


442

มีวิธีทำอะไรแบบนี้ไหม

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

ใช้ Bash?


1
คำตอบของ @dutChแสดงให้เห็นว่าbashมีบางสิ่งที่คล้ายกับ "ผู้ประกอบการที่ประกอบไปด้วยไตรภาค" อย่างไรก็ตามในbashสิ่งนี้เรียกว่า "ผู้ประกอบการที่มีเงื่อนไข" expr?expr:expr(ดูที่man bashส่วนการข้ามไปสู่ พึงระลึกไว้เสมอว่าbash"โอเปอเรเตอร์ที่มีเงื่อนไข" นั้นยุ่งยากและมี gotchas อยู่บ้าง
เทรเวอร์บอยด์สมิ ธ

((...))ทุบตีจะมีผู้ประกอบการที่ประกอบไปด้วยสำหรับจำนวนเต็มและการทำงานภายในการแสดงออกทางคณิตศาสตร์ ดูเชลล์เลขคณิต
codeforester

1
เช่นเดียวกับ @codeforester กล่าวประกอบ ternary ทำงานร่วมกับการขยายตัวทางคณิตศาสตร์และการประเมินผล$(( )) arithmethic ดูเพิ่มเติม(( )) https://mywiki.wooledge.org/ArithmeticExpression
ไก่

คำตอบ:


490

ผู้ประกอบการที่สาม? :เป็นรูปแบบสั้น ๆ ของif/else

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

หรือ

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

103
โปรดทราบว่า=โอเปอเรเตอร์ทดสอบความเท่าเทียมกันของสตริงไม่ใช่ความเท่าเทียมกันที่เป็นตัวเลข (เช่น[[ 05 = 5 ]]เป็นเท็จ) หากคุณต้องการเปรียบเทียบเป็นตัวเลขให้ใช้-eqแทน
Gordon Davisson

10
มันเป็นแบบสั้น ๆ มากกว่าสำหรับif/then/else
vol7ron

15
เป็นวิธีอัจฉริยะที่ใช้ประโยชน์จากพฤติกรรมการลัดวงจรเพื่อรับเอฟเฟกต์ประกอบการแบบไตรภาค :) :) :)
mtk

6
ทำไม [[และ]] มันทำงานได้ดีเช่นนี้: [$ b = 5] && a = "$ c" || a = "$ d"
kdubs

83
cond && op1 || op2สร้างมีข้อผิดพลาดโดยธรรมชาติถ้าop1มีสถานะทางออกที่ไม่ใช่ศูนย์ด้วยเหตุผลใดก็ตามผลที่ตามมาอย่างเงียบ ๆ op2จะกลายเป็น if cond; then op1; else op2; fiเป็นหนึ่งบรรทัดด้วยและไม่มีข้อบกพร่องนั้น
ivan_pozdeev

375

รหัส:

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

58
สิ่งนี้ดีกว่าคนอื่น ๆ ... จุดเกี่ยวกับโอเปอเรเตอร์ระดับอุดมศึกษาคือมันเป็นโอเปอเรเตอร์ดังนั้นจึงเป็นบริบทที่เหมาะสมในนิพจน์ดังนั้นจึงต้องส่งคืนค่า
nic ferrier

2
นี่เป็นวิธีที่รัดกุมที่สุด ระวังว่าถ้าส่วนที่มีecho "$c"คำสั่งเป็น aliased และมีหลายแถว (เช่นecho 1; echo 2) คุณควรใส่ไว้ในวงเล็บ
แมตต์

2
สิ่งนี้จะจับเอาท์พุทของคำสั่งทดสอบด้วยเช่นกัน (ใช่ในกรณีนี้เรา "รู้" ว่ามันไม่ได้สร้างขึ้นมา)
ivan_pozdeev

8
นี้ห่วงโซ่ของผู้ประกอบการเพียงพฤติกรรมเช่นผู้ประกอบการที่ประกอบไปด้วยถ้าคุณเป็นบวกที่คำสั่งหลังจากที่&&จะไม่ต้องไม่ใช่ศูนย์สถานะออก มิฉะนั้นa && b || c จะทำงานโดยไม่คาดคิดcหากaประสบความสำเร็จ แต่bล้มเหลว
chepner

155

หากเงื่อนไขเพียงตรวจสอบว่าตัวแปรถูกตั้งค่าหรือไม่ก็มีรูปแบบที่สั้นกว่า:

a=${VAR:-20}

จะกำหนดให้aกับค่าของVARถ้าVARมีการตั้งค่ามิฉะนั้นจะกำหนดเป็นค่าเริ่มต้น20- ซึ่งอาจเป็นผลมาจากการแสดงออก

ในฐานะที่เป็นอเล็กซ์บันทึกในความคิดเห็นวิธีการนี้เรียกว่า "การขยายพารามิเตอร์"


6
ในกรณีที่ส่งพารามิเตอร์สตริงพร้อมเครื่องหมายขีดคั่นในนั้นฉันต้องใช้เครื่องหมายอัญประกาศ: a=${1:-'my-hyphenated-text'}
saranicole

1
ลิงก์สำหรับคนขี้เกียจ - มีผู้ให้บริการเพิ่มเติมมากกว่าเพียงแค่ผู้แทนที่ ( :-)
Justin Wrobel

10
@JustinWrobel - น่าเสียดายที่ไม่มีไวยากรณ์เหมือน${VAR:-yes:-no}กัน
Ken Williams

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

การcond && op1 || op2แสดงออกที่แนะนำในคำตอบอื่น ๆ มีข้อบกพร่องโดยธรรมชาติ: หากop1มีสถานะออกที่ไม่ใช่ศูนย์op2จะกลายเป็นผลเงียบ; ข้อผิดพลาดจะไม่ถูกจับใน-eโหมด ดังนั้นการแสดงออกที่เป็นเพียงความปลอดภัยที่จะใช้ถ้าop1ไม่สามารถล้มเหลว (เช่น:, trueถ้า builtin หรือการกำหนดตัวแปรโดยไม่ต้องดำเนินการใด ๆ ที่สามารถล้มเหลว (เช่นส่วนและระบบปฏิบัติการสาย))

หมายเหตุ""คำพูด คู่แรกจะป้องกันข้อผิดพลาดทางไวยากรณ์ถ้า$bว่างเปล่าหรือมีช่องว่าง อื่น ๆ จะป้องกันการแปลของช่องว่างทั้งหมดในช่องว่างเดียว


1
ฉันเคยเห็นคำนำหน้าฉันเชื่อว่า[ "x$b" -eq "x" ]เคยทดสอบสตริงว่างโดยเฉพาะ ฉันชอบใช้ไวยากรณ์ที่ zsh ใช้ ('พารามิเตอร์ส่วนขยาย' ในหน้า zshexpn) แต่ฉันไม่รู้เกี่ยวกับการพกพามากนัก
John P


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

31
นี่เป็นสิ่งที่ดีสำหรับการเปรียบเทียบเชิงตัวเลขและการมอบหมาย แต่มันจะให้ผลลัพธ์ที่คาดเดาไม่ได้ถ้าคุณใช้มันสำหรับการเปรียบเทียบและการมอบหมายสตริง .... (( ))ถือว่าสตริงใด ๆ / ทั้งหมดเป็น0
Peter.O

3
สิ่งนี้สามารถเขียนได้:a=$(( b==5 ? c : d ))
joeytwiddle

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

สิ่งนี้จะหลีกเลี่ยงการดำเนินการส่วนหลัง | | โดยไม่ได้ตั้งใจเมื่อรหัสระหว่าง && และ || ล้มเหลว


สิ่งนี้จะยังไม่พบข้อผิดพลาดใน-eโหมด: (set -o errexit; [ 5 == 5 ] && { false; true; echo "success"; } || echo "failure"; echo $?; echo "further on";)->success 0 further on
ivan_pozdeev

2
ใช้:หัวกระสุนแทนtrueการบันทึกexecโปรแกรมภายนอก
Tom Hale

@ivan_pozdeev มีวิธีการใช้ใด&&.. ||และยังคงจับคำสั่งที่ล้มเหลวในระหว่างพวกเขา?
Tom Hale

2
@TomHale No. &&และ||โดยนิยามจะใช้กับคำสั่งทั้งหมดก่อนหน้าพวกเขา ดังนั้นหากคุณมีสองคำสั่งก่อนหน้านี้ (ไม่ว่าจะรวมกันอย่างไร) คุณจะไม่สามารถใช้กับหนึ่งในคำสั่งเหล่านั้นเท่านั้น คุณสามารถเลียนแบบif/ then/ elseลอจิกด้วยตัวแปรแฟล็ก แต่ทำไมต้องรำคาญถ้ามีif/ then/ elseเหมาะสม?
ivan_pozdeev

14

ขอคำสั่งสนับสนุนมากที่สุดของผู้ประกอบการพื้นฐานที่หนึ่งจะต้อง:

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

โดยปกติจะใช้งานได้เฉพาะกับการกำหนดตัวแปรเท่านั้น มันไม่สามารถดำเนินการคำสั่งอื่น ๆ


24
มันเทียบเท่ากับ ((... )) ดังนั้นจึงใช้ได้กับนิพจน์ทางคณิตศาสตร์เท่านั้น
drAlberT

13

นี่คือตัวเลือกอื่นที่คุณต้องระบุตัวแปรที่คุณกำหนดหนึ่งครั้งและไม่สำคัญว่าการกำหนดของคุณจะเป็นสตริงหรือตัวเลข:

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

แค่ความคิด :)


ข้อเสียที่สำคัญมันยังจะจับ stdout [ test ]ของ ดังนั้นโครงสร้างจะปลอดภัยต่อการใช้เฉพาะถ้าคุณ "รู้" ว่าคำสั่งจะไม่แสดงผลใด ๆ ไปยัง stdout
ivan_pozdeev

นอกจากนี้ยังเป็นแบบเดียวกันกับstackoverflow.com/questions/3953645/ternary-operator-in-bash/ …รวมถึงความจำเป็นที่จะต้องอ้างและหลีกเลี่ยงการเสนอราคาภายใน VARIABLE="`[ test ] && echo \"VALUE_A\" || echo \"VALUE_B\"`"รุ่นพื้นที่ใจกว้างจะมีลักษณะเช่น
ivan_pozdeev

8

ดูเหมือนว่าต่อไปนี้จะทำงานสำหรับกรณีการใช้งานของฉัน:

ตัวอย่าง

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

7
คำอธิบายมาก สมบูรณ์มาก verbose มาก สามสิ่งที่ฉันชอบ ;-)
Jesse Chisholm

2
เป็นternส่วนหนึ่งของ Bash หรือไม่? Mac ดูเหมือนจะไม่มี
nonopolarity

4
เฮ้ @ 太極者無極而生 - นั่นคือชื่อของสคริปต์ทุบตี - บันทึกส่วน "เทิน" ด้านบนลงในไฟล์ชื่อ "เทิน" จากนั้นเรียกใช้chmod 700 ternในโฟลเดอร์เดียวกัน ตอนนี้คุณจะมีternคำสั่งในเทอร์มินัลของคุณ
Brad Parks

8

เราสามารถใช้สามวิธีต่อไปนี้ใน Shell Scripting สำหรับโอเปอร์เรเตอร์ที่ประกอบไปด้วย:

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

Or

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

Or

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

อันที่จริงแล้วนี่เป็นคำตอบที่เหมาะสมที่สุดสำหรับคำถามนี้ IMHO
netpoetica

6

นอกจากนี้ยังมีไวยากรณ์ที่คล้ายกันมากสำหรับเงื่อนไขสามประการใน bash:

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

น่าเสียดายที่มันใช้ได้กับตัวเลขเท่านั้น - เท่าที่ฉันรู้



4

คุณสามารถใช้สิ่งนี้หากคุณต้องการไวยากรณ์ที่คล้ายกัน

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

สิ่งนี้ใช้ได้กับจำนวนเต็มเท่านั้น คุณสามารถเขียนได้ง่ายขึ้นตามa=$(((b==5) ? : c : d))- $((...))จำเป็นเฉพาะเมื่อเราต้องการกำหนดผลลัพธ์ของเลขคณิตให้กับตัวแปรอื่น
codeforester

2

นี่คือตัวเลือกบางส่วน:

1- ใช้ถ้าหากเป็นอย่างอื่นในบรรทัดเดียวก็เป็นไปได้

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

2

นี่เป็นวิธีแก้ปัญหาทั่วไปนั่น

  • ทำงานร่วมกับการทดสอบสตริงเช่นกัน
  • รู้สึกเหมือนแสดงออก
  • หลีกเลี่ยงผลข้างเคียงที่บอบบางเมื่อเงื่อนไขล้มเหลว

ทดสอบด้วยการเปรียบเทียบเชิงตัวเลข

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

ทดสอบกับการเปรียบเทียบสตริง

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


1

นี้เป็นเหมือนคำตอบที่ดีของวลาดิเมีย หาก "ternary" ของคุณเป็นกรณีของ "if true, string, ถ้า false, empty" คุณก็สามารถทำได้:

$ 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

เพื่อตอบคำถาม: 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" เทียบเท่ากับ $ ((แสดงออก))


0

ทางเลือกที่มุ่งเน้นสตริงที่ใช้อาร์เรย์:

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

ผลลัพธ์ใด:

13: IGNORE
14: REPLACE
15: IGNORE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.