Як я можу додати числа в сценарії 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
Я думаю, ви надрукуєте нульове значення, навіть використовуючи наступну відповідь. Існує хитрість, наприклад, підрозділ процесу, щоб довести остаточне значення "внутрішнього числа" до "зовнішнього числа".
Скотт Чу

Відповіді:


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існували та мали цілі значення?
сорпігаль

1
Все ж вони існували, але одна змінна подвійна .. це проблема ??
Нік

2
Так, це проблема :) Примітка: $((..))арифметичне оцінювання виконується в баші. exprвиконується як окремий процес, тому він буде набагато повільніше. використовуйте останнє в системах, де арифметична оцінка не підтримується (sh! = bash)
Karoly Horvath

4
Оскільки $((…))це стандартизовано POSIX, воно повинно ставати все рідше, що exprнеобхідно.
чепнер

115

Використовуйте $(( ))арифметичне розширення.

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

Подивитися отримання додаткової інформації Розділ 13. Арифметичне розширення .


2
Дивно. Тут не виходить, коли я намагаюся обчислити 191.003 + 190.
Сигур

2
Не працює для чисел із десятковою комою.
п'ятьдогіт

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

Як описано в FAQ B2 022 , Bash не підтримує номери з плаваючою комою. Якщо вам потрібно підсумовувати числа з плаваючою комою, використовуйте зовнішній інструмент (наприклад, bcабоdc ).

У цьому випадку рішення було б

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

Для додавання накопичувальних номерів, можливо, з плаваючою комою num.


1
@Sorpigal Thanksssss багато .. Чи можу я попросити вас щось інше ... як я можу надрукувати в кінці не число, а число / 10 ??
Нік

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
Менше захаращення, але застаріле.
Каміль Ґудсейне


7

Ще один портативний POSIXсумісний спосіб зробити це bash, який можна визначити як функцію .bashrcдля всіх арифметичних операторів зручності.

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

і просто викликати його в командному рядку як,

addNumbers 1 2 3 4 5 100
115

Ідея полягає у використанні сепаратора введення-поля (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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.