Как я могу добавить числа в скрипте Bash?

567

У меня есть этот сценарий 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".
Скотт Чу

Ответы:

977

Для целых чисел :

  • Используйте арифметическое расширение :$((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
Да, это проблема :) Примечание: $((..))арифметическая оценка выполняется в bash. exprвыполняется как отдельный процесс, поэтому он будет намного медленнее. используйте последний в системах, где арифметическая оценка не поддерживается (sh! = bash)
Karoly Horvath
4
Так $((…))как POSIX стандартизирован, это становится все более редким, что exprнеобходимо.
Chepner
115

Используйте $(( ))арифметическое расширение.

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

См. Главу 13. Арифметическое Расширение для получения дополнительной информации.

Стив Прентис
источник
2
Странный. Это не работает здесь, когда я пытаюсь вычислить 191.003 + 190.
Сигур
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.

sorpigal
источник
1
@Sorpigal. Спасибо. Могу я спросить вас кое о чем еще ... как я могу напечатать в конце не число, а число / 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

Я всегда забываю синтаксис, поэтому захожу в гугл, но потом никогда не нахожу тот, с которым мне знакомо: P. Это самое чистое для меня и более верное тому, что я ожидал на других языках.

i=0
((i++))

echo $i;
ssshake
источник
18
 #!/bin/bash
read X
read Y
echo "$(($X+$Y))"
Амарджит Сингх
источник
1
Хорошо работает в macOS.
Tmx
15

Мне тоже очень нравится этот метод, меньше беспорядка:

count=$[count+1]
unixball
источник
1
Кстати, почему это работает? Как мы это называем? Я не могу найти документы об этом.
skyline75489
4
Менее беспорядок, но не рекомендуется.
Камиль Гудесюн
7

Другой переносимый POSIXсовместимый способ сделать это bash, который может быть определен как функция .bashrcдля всех арифметических операторов удобства.

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

и просто вызвать его в командной строке, как,

addNumbers 1 2 3 4 5 100
115

Идея состоит в том, чтобы использовать Input-Field-Separator (IFS) , специальную переменную, bashиспользуемую для разделения слов после раскрытия и разделения строк на слова. Функция изменяет значение локально, чтобы использовать символ разделения слов в качестве оператора суммы +.

Помните, что IFSон изменяется локально и НЕ влияет на IFSповедение по умолчанию вне области действия функции. Отрывок со man bashстраницы,

Оболочка обрабатывает каждый символ IFS как разделитель и разбивает результаты других расширений на слова на этих символах. Если IFS не установлен или его значение в точности равно значению по умолчанию, то последовательности, и в начале и в конце результатов предыдущих расширений игнорируются, и любая последовательность символов IFS, отсутствующая в начале или конце, служит для разделения слова.

"$(( $* ))"Представляет список аргументов , переданных расщепляться , +а затем значение суммы выводится с помощью printfфункции. Функция может быть расширена, чтобы добавить область для других арифметических операций также.

Inian
источник
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
Милен Джон Томас
источник