ฉันจะเพิ่มตัวเลขในสคริปต์ Bash ได้อย่างไร


566

ฉันมีสคริปต์ Bash นี้และฉันมีปัญหาในบรรทัดที่ 16 ฉันจะนำผลลัพธ์ก่อนหน้าของบรรทัดที่ 15 และเพิ่มไปยังตัวแปรในบรรทัดที่ 16 ได้อย่างไร

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        num= $num + $metab   (line16)
    done
    echo "$num"
 done

2
ฉันคิดว่าคุณจะพิมพ์ค่าศูนย์แม้ใช้คำตอบต่อไปนี้ มีเคล็ดลับเช่นกระบวนการย่อยเพื่อนำค่าสุดท้ายของ "Inside NUM" ไปยัง "Outside NUM"
Scott Chu

คำตอบ:


974

สำหรับจำนวนเต็ม :

  • ใช้การขยายเลขคณิต :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • การใช้exprยูทิลิตีภายนอก โปรดทราบว่านี่เป็นสิ่งจำเป็นสำหรับระบบเก่าจริงๆเท่านั้น

    num=`expr $num1 + $num2`     # Whitespace for expr is important

สำหรับจุดลอย :

Bash ไม่สนับสนุนสิ่งนี้โดยตรง แต่มีเครื่องมือภายนอกสองสามอย่างที่คุณสามารถใช้:

num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc)   # Whitespace for echo is important

คุณยังสามารถใช้สัญลักษณ์ทางวิทยาศาสตร์ (ตัวอย่างเช่น2.5e+2)


ข้อผิดพลาดทั่วไป :

  • เมื่อตั้งค่าตัวแปรคุณไม่สามารถมีช่องว่างด้านใดด้านหนึ่งได้=มิฉะนั้นจะบังคับให้เชลล์ตีความคำแรกเป็นชื่อของแอปพลิเคชันที่จะเรียกใช้ (ตัวอย่างเช่นnum=หรือnum)

    num= 1 num =2

  • bcและexprคาดว่าแต่ละหมายเลขและโอเปอเรเตอร์จะเป็นอาร์กิวเมนต์แยกต่างหากดังนั้นช่องว่างจึงมีความสำคัญ 3+ +4พวกเขาไม่สามารถดำเนินการขัดแย้งเช่น

    num=`expr $num1+ $num2`


1
ในคำตอบแรกก็พิมพ์ฉัน expr: อาร์กิวเมนต์ที่ไม่ใช่ตัวเลขที่สองไม่ทำงาน ..
นิค

1
@Nick: คุณลองสิ่งนี้ในขณะที่ตัวแปรตั้งชื่อnum1และnum2มีอยู่และมีค่าจำนวนเต็มหรือไม่
sorpigal

1
พวกมันมีอยู่แล้ว แต่ตัวแปรตัวเดียวคือสองเท่า .. นั่นเป็นปัญหาหรือไม่?
Nick

2
ใช่นั่นเป็นปัญหา :) หมายเหตุ: การ$((..))คำนวณทางคณิตศาสตร์จะถูกดำเนินการใน bash exprจะถูกดำเนินการเป็นกระบวนการแยกต่างหากดังนั้นมันจึงช้ากว่ามาก ใช้อันหลังบนระบบที่ไม่รองรับการประเมินทางคณิตศาสตร์ (sh! = bash)
Karoly Horvath

4
เนื่องจาก$((…))เป็นมาตรฐานโดย POSIX จึงควรกลายเป็นของหายากที่exprจำเป็นมากขึ้น
chepner

115

ใช้การ$(( ))ขยายเลขคณิต

num=$(( $num + $metab ))

ดูบทที่ 13 การขยายตัวทางคณิตศาสตร์สำหรับข้อมูลเพิ่มเติม


2
แปลก. 191.003 + 190มันไม่ได้ทำงานที่นี่เมื่อฉันพยายามที่จะคำนวณ
Sigur

2
ใช้ไม่ได้กับตัวเลขที่มีจุดทศนิยม
fivedogit

30

มีวิธีหนึ่งพันวิธี นี่คือหนึ่งในการใช้งานdc(เครื่องคิดเลขแบบตั้งโต๊ะขัดเงาซึ่งรองรับเลขคณิตความแม่นยำไม่ จำกัด ):

dc <<<"$num1 $num2 + p"

แต่ถ้ามันดีเกินไปสำหรับคุณ (หรือเรื่องการพกพา) คุณสามารถพูดได้

echo $num1 $num2 + p | dc

แต่บางทีคุณอาจเป็นหนึ่งในคนที่คิดว่า RPN น่ากลัวและแปลกประหลาด ไม่ต้องกังวล! bcอยู่ที่นี่สำหรับคุณ:

bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc

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

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in output-$i-* ; do # 'for' can glob directly, no need to ls
            echo "$j"

             # 'grep' can read files, no need to use 'cat'
            metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
            num=$(( $num + $metab ))
    done
    echo "$num"
done

ตามที่อธิบายไว้ในBash FAQ 022 Bash ไม่รองรับตัวเลขทศนิยม หากคุณต้องการรวมจำนวนจุดลอยตัวจำเป็นต้องใช้เครื่องมือภายนอก (เช่นbcหรือdc)

ในกรณีนี้การแก้ปัญหาจะเป็น

num=$(dc <<<"$num $metab + p")

ในการเพิ่มจำนวนจุดลอยตัวที่เป็นไปnumได้


1
@Sorpigal Thankssss มาก .. ฉันขอให้คุณอย่างอื่น .. ฉันจะพิมพ์ในตอนท้ายไม่ได้เป็น NUM แต่ NUM / 10 หรือไม่?
Nick

23

ในทุบตี

 num=5
 x=6
 (( num += x ))
 echo $num   # ==> 11

โปรดทราบว่า bash สามารถจัดการเลขคณิตจำนวนเต็มเท่านั้นดังนั้นหากคำสั่ง awk ของคุณคืนเศษส่วนคุณจะต้องออกแบบใหม่: นี่คือรหัสของคุณเขียนใหม่อีกเล็กน้อยเพื่อทำคณิตศาสตร์ทั้งหมดใน awk

num=0
for ((i=1; i<=2; i++)); do      
    for j in output-$i-*; do
        echo "$j"
        num=$(
           awk -v n="$num" '
               /EndBuffer/ {sum += $2}
               END {print n + (sum/120)}
           ' "$j"
        )
    done
    echo "$num"
done

20

ฉันมักจะลืมไวยากรณ์ดังนั้นฉันจึงไปที่ google แต่ฉันไม่เคยพบสิ่งที่ฉันคุ้นเคย: P นี่เป็นสิ่งที่สะอาดที่สุดสำหรับฉันและเป็นจริงกับสิ่งที่ฉันคาดหวังในภาษาอื่น ๆ

i=0
((i++))

echo $i;


15

ฉันชอบวิธีนี้เช่นกันถ่วงน้อยลง:

count=$[count+1]

1
ทำไมถึงใช้งานได้ เราจะเรียกสิ่งนี้ได้อย่างไร ฉันไม่พบเอกสารเกี่ยวกับสิ่งเหล่านี้
skyline75489

4
รกรุงรังน้อยลง แต่เลิกใช้แล้ว
Camille Goudeseune


7

อีกPOSIXวิธีที่สอดคล้องกับพกพาที่ต้องทำbashซึ่งสามารถกำหนดเป็นฟังก์ชัน.bashrcสำหรับผู้ดำเนินการทางคณิตศาสตร์ทั้งหมดเพื่อความสะดวก

addNumbers () {
    local IFS='+'
    printf "%s\n" "$(( $* ))"
}

และเรียกมันใน command-line เป็น

addNumbers 1 2 3 4 5 100
115

แนวคิดคือการใช้Input-Field-Separator (IFS)ซึ่งเป็นตัวแปรพิเศษที่bashใช้สำหรับการแยกคำหลังจากการขยายและเพื่อแยกบรรทัดเป็นคำ +ฟังก์ชั่นการเปลี่ยนแปลงค่าในประเทศที่จะใช้ตัวอักษรคำแยกเป็นผู้ประกอบการรวม

จำIFSมีการเปลี่ยนแปลงทั้งในประเทศและไม่ไม่ได้มีผลในการเริ่มต้นIFSพฤติกรรมนอกขอบเขตการทำงาน ข้อความที่ตัดตอนมาจากman bashหน้า

เชลล์ปฏิบัติต่ออักขระแต่ละตัวของ IFS เป็นตัวคั่นและแยกผลลัพธ์ของการขยายอื่น ๆ ออกเป็นคำในตัวอักษรเหล่านี้ หาก IFS ไม่ได้ตั้งค่าหรือค่าของมันคือค่าเริ่มต้นจากนั้นลำดับของและที่จุดเริ่มต้นและจุดสิ้นสุดของผลลัพธ์ของการขยายก่อนหน้านี้จะถูกละเว้นและลำดับของอักขระ IFS ใด ๆ ที่ไม่ได้อยู่ที่จุดเริ่มต้นหรือสิ้นสุดจะกำหนดขอบเขต คำ.

"$(( $* ))"หมายถึงรายการของอาร์กิวเมนต์ส่งผ่านไปจะแยกตาม+และต่อมามีมูลค่ารวมเป็นผลผลิตโดยใช้printfฟังก์ชั่น สามารถขยายฟังก์ชันเพื่อเพิ่มขอบเขตสำหรับการดำเนินการทางคณิตศาสตร์อื่น ๆ ได้เช่นกัน


2
#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do      
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        let num=num+metab (line 16)
    done
    echo "$num"
done

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