วิธีการทำให้อาร์กิวเมนต์เป็นตัวเลือกในทุบตี?


13

ในฟังก์ชั่นด้านล่างมี 9 ข้อโต้แย้ง:

SUM() { 
    echo "The sum is $(($1+$2+$3+$4+$5+$6+$7+$8+$9))"
}

ฉันต้องการที่จะทำให้ข้อโต้แย้งที่สองเพื่อถัดไป (3..9) กลายเป็นข้อโต้แย้งที่ไม่จำเป็น

เมื่อฉันเรียกใช้ฟังก์ชันที่มี 2 อาร์กิวเมนต์ฉันได้รับข้อผิดพลาด:

SUM 3 8
bash: 3+8+++++++: syntax error: operand expected (error token is "+")

หมายเหตุ BOLD : อาร์กิวเมนต์แรกและอาร์กิวเมนต์ที่สองคืออาร์กิวเมนต์แรงและไม่บังคับสำหรับฟังก์ชัน ฉันต้องการอาร์กิวเมนต์ที่สองต่อไปเป็นตัวเลือกและเมื่อฉันเรียกใช้ฟังก์ชันน้อยกว่า 2 args ฟังก์ชันจะต้องส่งคืนผลลัพธ์

คำตอบ:


22

หากคุณจะไม่ผ่านการขัดแย้งกับช่องว่าง:

sum() {  
[[ -n $2 ]] && echo $(( $(tr ' ' '+' <<<"$@") ))
}

ผลกระทบ:

$ sum 1 2 3
6

คำอธิบาย:

  1. <<<"some string"ฟีด"some string"เป็นอินพุตเท่านั้น echo "some string" |คิดว่ามันเป็นชวเลข มันจะเรียกว่าที่นี่ String
  2. "$@"ขยายเป็นพารามิเตอร์ตำแหน่งทั้งหมดคั่นด้วยช่องว่าง "$1 $2 ..."มันจะเทียบเท่ากับ
  3. ดังนั้นtr ' ' '+' <<<"$@"ผลที่ได้รับการประเมินจากภายนอก"$1+$2+$3..."$(( ))
  4. [[ -n $2 ]]ทดสอบว่าพารามิเตอร์ตัวที่สองไม่ว่างเปล่าหรือไม่ คุณสามารถแทนที่ด้วย[[ -n $2 ]] &&[[ -z $2 ]] ||

อีกวิธีหนึ่ง:

sum() {
[[ -n $2 ]] && (IFS=+; echo $(( $* )))
}

คำอธิบาย:

  1. $*เป็นเช่นเดียว$@ยกเว้นว่าพารามิเตอร์จะไม่แยกจากกันโดยช่องว่าง แต่โดยตัวอักษรตัวแรกของภายในสนาม Separator (IFS ) ด้วยIFS=+จะขยายเป็น "$ 1 + $ 2 + ... " ดูความแตกต่างระหว่าง $ * และ $ @ คืออะไร
  2. เราตั้งค่าIFSใน subshell (หมายเหตุวงเล็บล้อมรอบ) เพื่อให้เชลล์หลักไม่ได้รับผลกระทบ IFSตามค่าเริ่มต้นคือ: \t\n(เว้นวรรคแท็บบรรทัดใหม่) นี่เป็นทางเลือกแทนการใช้localตัวแปร

ตอนนี้เพื่อตอบคำถามของคุณ:

คุณสามารถใช้ค่าเริ่มต้นสำหรับตัวแปรหรือพารามิเตอร์ใด ๆ ทั้ง:

SUM() { 
 echo "The sum is $(($1+$2+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))" || false
}

หรือ:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}

6
Nifty! ฉันรู้ว่าความคิดเห็นไม่ได้มีไว้สำหรับการชมเชยและขอบคุณ แต่วิธีนี้เป็นเพียง ... ชั่วร้าย! :-)
zwets

17

ดูที่shiftโอเปอเรเตอร์ มันจะเลื่อนข้อโต้แย้งที่ 2 ขึ้นไปสู่ตำแหน่งที่ 1 และต่อไปโดยทิ้งข้อโต้แย้งที่ 1

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}

4

คุณสามารถใช้คำจำกัดความซ้ำซึ่งจะสิ้นสุดลงเมื่อsumมีการเรียกใช้โดยไม่มีข้อโต้แย้ง เราจะใช้ประโยชน์จากความจริงที่ว่าโดยไม่ต้องประเมินข้อโต้แย้งtestfalse

sum () {
    test $1 && echo $(( $1 + $(shift; sum $@) )) || echo 0
}

3

ลองสิ่งนี้:

SUM () {
 [ $# -lt "2" ] && return 1
 for par in $@; do
   local sum=`expr $sum + $par`
 done
 echo $sum
 return 0
}

SUM 3 4 5
SUM 3 4 5 1 1 1 1 2 3 4 5

สิ่งนี้จะออก 12 และ 30

$@หมายถึงพารามิเตอร์$#ส่งคืนจำนวนพารามิเตอร์ในกรณีนี้ 3 หรือ 11

ทดสอบบน linux redhat 4


2

คุณสามารถใช้การวนรอบเล็กน้อย:

sum(){
    t=0;
    for i in "$@"; do t=$((t + i )); done
    echo $t;
}

โดยส่วนตัวแล้วฉันจะใช้perlหรือawkแทน:

sum(){
 echo "$@" | perl -lane '$s+=$_ for @F; print $s'
}

หรือ

sum(){
 echo "$@" | awk '{for(i=1; i<=NF; i++){k+=$i} print k}'
}

2

ใช้ 0 เป็นค่าเริ่มต้นสำหรับ $ 1 ถึง $ 9:

SUM() { 
    echo "The sum is $((${1:-0}+${2:-0}+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))"
}

จากman bash:

${parameter:-word}
    Use Default Values. If parameter is unset or null, the expansion
    of word is substituted. Otherwise, the value of parameter is
    substituted.

ตัวอย่าง:

$ SUM

ผลรวมคือ 0

$ SUM 1 2 

ผลรวมคือ 3

$ SUM 1 1 1 1 1 1 1 1 1 

ผลรวมคือ 9


เอาต์พุตเดียวกันกับ awk:

SUM() {
  echo -e ${@/%/\\n} | awk '{s+=$1} END {print "The sum is " s}'
}

1

มันเป็นโซลูชันของฉันเองที่ฉันลองและพบ:

SUM() { 
    echo "The sum is $(($1+$2+$[$3]+$[$4]+$[$5]+$[$6]+$[$7]+$[$8]+$[$9]))"
 }

$ SUM 4 6 5
The sum is 15

แต่คำตอบของ @ muruนั้นดี


+1: การใช้เลขคณิตสองตัวที่น่าสนใจเพื่อประเมินพารามิเตอร์ที่ว่างเปล่าให้เป็นศูนย์
muru

1
@muru ขอบคุณ แต่ในกรณีนี้คำตอบของฉันเราไม่ได้ใช้มากกว่า 9 ข้อโต้แย้งและเราต้องใช้กลุ่มของการขัดแย้งที่จะผ่านมากกว่า 9 ขอบคุณสำหรับคำตอบของคุณที่สมบูรณ์แบบ
αғsнιη
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.