LINUX.ORG.RU
решено ФорумAdmin

Invalid arithmetic operator

 , ,


0

1

Добрый день. Решил тут набросать скриптик, да чет не работает. В Баше впервые, прошу сильно не пинать. Суть скрипта - перебрать все файлы в каталоге, грепнуть необходимые данные, засунуть по в отдельный файл в том же каталоге. Данные представляют из себя количество скачанной информации в байтах, необходимо представить в читаемом виде. Второй цикл «ломается» с ошибкой «неправильный арифметический оператор», не могу понять почему. Код:

#!/bin/bash

counter=(B KB MB GB TB)
START=0
for i in *.ini
do
VAR1=«$(basename /samba/gene6/$i)»
VAR2=«$(cat $i | grep StatsDownloaded | awk -F= '{print $2}')»
for ((n=$START; $(($VAR2<=1000)); n++))
do
VAR2=$(($VAR2/1024))
done
echo $VAR1".«$VAR2${counter[n]}>>/samba/gene6/gene6.log
done

ну так вытащи перед арифметикой переменную на посмотреть, а то вдруг дробное?

echo VAR2

ну и если там дробь, то используем костыль

VAR2=$(echo "scale=3; $VAR2/1024" | bc)
Morin ★★★★
()
Последнее исправление: Morin (всего исправлений: 2)
#!/bin/bash

counter=(B KB MB GB TB)
for file_name in *.ini; do
    downloaded=$(grep StatsDownloaded "$file_name" | awk -F= '{print $2}' | head -n1)
    for ((n = 0; downloaded >= 1024; n++)); do
        (( downloaded /= 1024))
        suffix=${counter[n]}
    done
    echo "${file_name}.$downloaded$suffix" >> /samba/gene6/gene6.log
done
spirit ★★★★★
()
Ответ на: комментарий от Morin

Да, вы правы, в переменной были не только числа, но еще и скрытый символ переноса каретки.

ELatestark
() автор топика

#!/bin/bash

counter=(B KB MB GB TB)
for i in *.ini
do
VAR1=«$(basename /samba/gene6/$i | sed -e «s/.ini//»)»
VAR2=«$(cat $i | grep StatsDownloaded | awk -F= ‘{print $2}’ | sed -e «s/\r//»)»
for ((n=0; $(($VAR2>=1000)); $n+1))
do
((VAR2/=1024))
done
echo $VAR1".«$VAR2$suffix${counter[n]}>>/samba/gene6/gene6.log
done

Ошибка неправильного оператора исправлена, будьте добры подсказать еще один момент:
в выводе видно, что не работает массив, т.е. все числа поделились, но приставка у всех «B», возможно потому что счётчик не объявлен глобально?

ELatestark
() автор топика
Ответ на: комментарий от ELatestark

возможно потому что счётчик не объявлен глобально?

Не глобально, а потому что написано чер-те что. Надо:

for ((n=0; VAR2>=1024; n++))

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

каким-то интуитивным методом я заставил работать эту конструкцию подобным образом:

for ((n; $(($VAR2>=1000)); n=$((n+1))))

ELatestark
() автор топика
Ответ на: комментарий от vodz

Но Ваш способ, признаться, выглядит красивее и понятнее

ELatestark
() автор топика

#!/bin/bash

counter=(B KB MB GB TB)
for i in *.ini
do
n=0
VAR1=«$(basename /samba/gene6/$i | sed -e „s/.ini//“)»
VAR2=«$(cat $i | grep StatsDownloaded | awk -F= '{print $2}' | sed -e „s/\r//“)»
for ((n; VAR2>=1024; n++))
do
((VAR2/=1024))
done
echo $VAR1".«$VAR2${counter[n]}>>/samba/gene6/gene6.log
done

И последний вопрос, указанный выше код работает, но! Но только для целочисленных значений) Помогите понять, как добавить пару знаков после запятой в вычислениях? Пробовал и с помощью awk и calc, переприсваивал значение VAR2=»$(calc $VAR2/1024)", но после первой итерации цикла и присваивания значения n=1 цикл брейкается с ошибкой
./script.sh: line 11: ((: 18688810.2734375: syntax error: invalid arithmetic operator (error token is ".2734375")

ELatestark
() автор топика
Ответ на: комментарий от Morin

Да, ваш пример я видел и пробовал его. Если писать
VAR2=$(echo «scale=3; $VAR2/1024» | bc)
,то счетчик останаливается на n=1, дальше не считает. Пример вывода: «304114555.689KB»

ELatestark
() автор топика
Ответ на: комментарий от ELatestark

Причем с той же ошибкой -
18688810.273: syntax error: invalid arithmetic operator (error token is ".273")

ELatestark
() автор топика
Ответ на: комментарий от ELatestark

При создании сообщения под ним есть строчка «Внимание: прочитайте описание разметки Markdown или LORCODE.», обрати на неё внимание.

gremlin_the_red ★★★★★
()
Ответ на: комментарий от gremlin_the_red

Извиняюсь, вот, привёл к «удобоваримому» виду:

#!/bin/bash

counter=(B KB MB GB TB)
for i in *.ini
do
n=0
VAR1=«$(basename /samba/gene6/$i | sed -e „s/.ini//“)»
VAR2=«$(cat $i | grep StatsDownloaded | awk -F= '{print $2}' | sed -e „s/\r//“)»
for ((n; VAR2>=1024; n++))
do
((VAR2/=1024))
done
echo $VAR1".«$VAR2${counter[n]}>>/samba/gene6/gene6.log
done
ELatestark
() автор топика
Ответ на: комментарий от ELatestark

Конвейер штука мощная и полезная, но не злоупотребляй им, заботься о читаемости

VAR2=$(grep StatsDownloaded "$i" | awk -F= '{printf "%u", $2}')

gremlin_the_red ★★★★★
()
Ответ на: комментарий от Morin

Уважаемый Morin, дело совсем не в том, что я не знаю что есть округление и как его производить, мой вопрос звучит как:

"По какой причине команда

VAR2=$(echo «scale=3; $VAR2/1024» | bc)

будучи запущенной в цикле, работает только при первой итерации этого цикла? Почему при n=2 цикл вылетает? Я пробовал прогонять этот цикл «ручками» и в терминале все работает:

[root@filedrop gene6]# VAR2=$(echo "scale=3; 311413305026/1024" | bc)
[root@filedrop gene6]# echo $VAR2
304114555.689
[root@filedrop gene6]# VAR2=$(echo "scale=3; $VAR2/1024" | bc)
[root@filedrop gene6]# echo $VAR2
296986.870
[root@filedrop gene6]# VAR2=$(echo "scale=3; $VAR2/1024" | bc)
[root@filedrop gene6]# echo $VAR2
290.026
[root@filedrop gene6]#

Тогда по какой такой причине я получаю:


./script.sh: line 11: ((: 304114555.689453125: syntax error: invalid arithmetic operator (error token is ".689453125")

Я понимаю, что я могу не замечать чего-то, возможно, слишком явного для вас, ну будьте же снисходительны)

ELatestark
() автор топика
Ответ на: комментарий от ELatestark

а сравнение в цикле ты с чем делаешь? :)

давай проще, не надо вывод bc без подготовки применять для логики дальше

Morin ★★★★
()
Последнее исправление: Morin (всего исправлений: 1)
Ответ на: комментарий от ELatestark
#!/bin/bash
n=0
#var2=1099511627776
var2=1099511627780
for ((n; var2>=1024; n++))
do
var1=$(echo "scale=3; $var2/1024" | bc -l)
echo $var1
var2=$(echo "scale=0; $var1/1" | bc -l)
done

для самых ленивых

Morin ★★★★
()
Ответ на: комментарий от Morin

Взял ваш код, всё работает, замечательно, премного благодарен.

Пытаюсь понять для чего вторая переменная, в чём сакральный смысл команды деления на единицу?

ELatestark
() автор топика
Ответ на: комментарий от Morin

Меня, возможно, постигло озарение, подскажите один момент:

Правильно ли я понимаю, что деление должно выполняться только с целочисленными значениями? Мы можем получать в качестве ответа float, но использовать его впоследствии нельзя. Поэтому, приходится этот float округлять и переприсваивать.

ELatestark
() автор топика
Ответ на: комментарий от ELatestark

Ну озарение почти верное, еще раз внимательно - bash не умеет арифметику и логику с float, поэтому мы используем внешнюю утилиту bc. Если нам надо обрабатывать башем результат полученный от внешней утилитой, то надо привести его к целому. Обрати внимание на параметр scale, который мы передаем bc, это кол-во знаков после запятой. Обращаю внимание на костыль в виде деления на единицу, к сожалению bc применяет scale только при делении, например если умножить, то получим тыкву, результат обрезан не будет

Morin ★★★★
()
Ответ на: комментарий от Morin

Спасибо Вам большое, мне было важно узнать причину!

ELatestark
() автор топика
Ответ на: комментарий от ELatestark
#!/bin/bash
n=0
var1=1099511627780
var2=$var1
for ((n; var2>=1024; n++))
do
var1=$(echo "scale=9; $var1/1024" | bc -l)
echo $var1
var2=$(echo "scale=0; $var2/1024" | bc -l)
done

так математически верней, если брать больше знаков после запятой, с другой стороны 9 знаков слишком избыточно для деления на 1024, тут уж смотри сам, не я тебе постановку задачи делаю

и обрати внимание, это не совсем округление, мы просто отбрасываем значение меньше чем кол-во знаков. по идее надо написать функцию округления для bc. но думаю этим займешся сам, если не будет лень

Morin ★★★★
()
Последнее исправление: Morin (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.