Математические операции - сценарий Bash

В:

cat test | xargs -I% bash -c 'echo % $1 $2;'

$1и $2передаются как литеральные строки в аргументе bash -c, то есть встроенному сценарию -. И %также будет расширен в этом скрипте, поэтому будет рассматриваться как шелл-код. Так, например, если testсодержит ;reboot;, это вызовет команду reboot.

Вы не передаете аргументы этому встроенному скрипту, поэтому $1, $2будут пустыми.

Вы могли бы сделать:

< test xargs -I% bash -c 'echo "$1" "$2" "$3"' bash % "$1" "$2"

Чтобы перенаправить аргумент вашего скрипта в ваш встроенный скрипт, но если $1или $2также содержат символы %, они также будут расширены на xargs.

Таким образом, вместо этого вы можете использовать среду для передачи этих значений:

< test PARAM1=$1 PARAM2=$2 xargs -I% bash -c '
  echo "$1" "$PARAM1" "$PARAM2"' bash %

Также обратите внимание, что xargsвыполняет некоторую обработку кавычек на входе, если вы хотите, чтобы необработанное содержимое каждой строки передавалось дословно, это не то, что вы бы использовали. С GNU xargsвы могли бы сделать:

< test xargs -d '\n' -n 1 bash -c 'echo "$3" "$1" "$2"' bash "$1" "$2"

Кроме того, не забудьте указать свои переменные в кавычках. printfболее надежен, чем echo, но помните, что первый аргумент — это формат , поэтому не должен быть переменной .

0
23.11.2019, 21:16
3 ответа

Выполнение математических операций, результатом которых является 25 цифр (3706907995955475988644381), безусловно, выходит за рамки возможностей большинства реализаций оболочки. На самом деле арифметика оболочки очень похожа на арифметику языка C. В языке C целое число со знаком меняет знак при переполнении. Обычно самое длинное системное целое для 64-битной системы имеет значение, ограниченное 63 двоичными цифрами (, другой бит определяет знак целого числа ), поэтому 63 единицы в двоичном или 0efff ffff ffff ffff в шестнадцатеричном представлении число $(((1<<63)-1 ))или 9223372036854775807 (19 цифр ). Отрицательный предел равен -9223372036854775808.

25-значное число не может поместиться внутри 19-значного целого, поэтому оно переполняется, так как Целые числа со знаком C переполняются:при смене знака (на большинстве машин):

$ echo "$(( 9223372036854775807 + 1 ))"
-9223372036854775808

до н.э.

Язык самого низкого уровня, который (автономная )утилита может обеспечить для чисел "произвольной точности" (без предустановленного ограничения длины ), — это bc. В bc нетрудно выполнить все требование:

x=12345
y=2
scale=0;
s=0;
while (x>0) {
              b=x%10;
          x/=10;
          s+=b^y
        };
s
quit

Запишите это в файл (предположим, что он называетсяdigpower.bc)и выполните с этим:

$ bc -q digpower.bc
55

Преобразование всего файла, чтобы он содержал только функцию, которая заботится о временных переменных и возвращает масштаб к исходному значению:

define d(x,y){
    auto s,b,u; s=scale;
    while (x>0) { b=x%10; x/=10; u+=b^y }
    scale=s; return(u) };

В этом случае вызовите bc следующим образом:

$ bc -q digpower.bc <<<"d(12345,2)"
55

$ echo "d(3706907995955475988644381,25)" | bc digpower.bc
3706907995955475988644381

питон

Следующим языком более высокого уровня (, пропускающим Common lisp ), который имеет неограниченный (только компьютерной памяти )целые числа, является Python:

$ python3 -c 'x=12345;y=2;s=0;
while (x>0): b=x%10;x//=10;s+=b**y                        
print(s)'
55

В большинстве других языков используются те или иные формы чисел с плавающей запятой, в том числе awk. Некоторые языки позволяют устанавливать размер числа с плавающей запятой (подробнее об этом в последней версии awk ).

авк

Все числа внутри awkхранятся как числа с плавающей запятой. По умолчанию awk использует 53-битную мантиссу. 53-битная мантисса имеет общее ограничение в 16 цифр.В некоторых конкретных поплавках может быть точно 17 или даже 18 цифр.

$ awk -vx=12345 -vy=2 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
55

Но (тот же код с другими значениями):

$ awk -vx=3706907995955475988644381 -vy=25 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
2989038141653481752100864

Что, очевидно, неверно, потому что внутреннее представление в виде числа с плавающей запятой становится совершенно неверным после 16-значного числа. Просто чтобы показать это:

$ awk -vx=3706907995955475988644381 'BEGIN{print(x);print(x+0)}'
3706907995955475988644381
3706907995955475754516480

Эти ошибки приводят к большим ошибкам.

В GNU awk существует альтернатива увеличения числа битов мантиссы с плавающей запятой, если awk имеет bignum, скомпилированный в (вывод версии awk --содержит «GNU MPFR»):

$ awk -M -v PREC=100 -vx=3706907995955475988644381 -vy=25 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b**y}; print(s)}'
3706907995955475988644381

25-значная цифра требует около 25*log(2)/log(10)двоичных цифр или 83-битной мантиссы (точный ответ довольно долго объяснять ). Использование 100 дает достаточный запас.

2
28.01.2020, 02:13

Арифметика оболочки в bashиспользует самый широкий целочисленный тип, поддерживаемый вашим компилятором C. В большинстве современных систем/компиляторов C это 64-битные целые числа, поэтому «только» охватывают диапазон -9223372036854775808 до 9223372036854775807 и обертывают числа вне этого. Для этого вам понадобится другой инструмент, например bc :

.
#!/bin/bash

num1=$1
num2=$2
sum=0

for (( i=0; i<${#num1}; i++ )); do
    n=${num1:$i:1}
    sum=$( bc <<<"$sum + $(bc <<<"${n}^$num2")" )
done

echo "$sum"
5
28.01.2020, 02:13

С короткимawkшрифтом:

sum_powered.awkскрипт:

#!/bin/awk -f
BEGIN{
    split(ARGV[1], a, "");
    pow = ARGV[2];
    for (i in a) sum += a[i] ** pow;
    print sum;
}

Использование:

$./sum_powered.awk 12345 2
55

$./sum_powered.awk 3706907995955475988644381 25
3706907995955475217645568

Возможно, вам потребуется добавить разрешение на выполнение для нового awk скрипта перед запуском:

$ chmod +x sum_powered.awk 
2
28.01.2020, 02:13

Теги

Похожие вопросы