UNIX shell脚本中的浮点运算

14

如何在 shell 脚本中使用浮点数(例如1.503923)进行算术运算?浮点数作为字符串从文件中获取。文件的格式如下:

1.5493482,3.49384,33.284732,23.043852,2.2384...
3.384,3.282342,23.043852,2.23284,8.39283...
.
.
.

这里是一些我需要使其正常工作的简化样例代码。在算术操作之前,一切都很顺利。我从文件中获取一行,然后从该行中获取多个值。我认为这将减少搜索处理时间,因为这些文件非常巨大。

# set vars, loops etc.

while [ $line_no -gt 0 ]
do
    line_string=`sed -n $line_no'p' $file_path`  # Pull Line (str) from a file
    string1=${line_string:9:6}                   # Pull value from the Line
    string2=${line_string:16:6}
    string3=...
    .
    .
    .
    calc1= `expr $string2 - $string7` |bc -l     # I tried these and various
    calc2= ` "$string3" * "$string2" ` |bc -l    # other combinations
    calc3= `expr $string2 - $string1`
    calc4= "$string2 + $string8" |bc
    .
    .
    .
    generic_function_call                        # Use the variables in functions
    line_no=`expr $line_no - 1`                  # Counter--
done

我一直得到的输出:

expr: non-numeric argument
command not found

使用shell调试/跟踪选项(set -vx)查看正在处理的值,然后您可以调整字符串解析以仅检索数字部分。这只是为了练习而已,对吧?在awk中进行此类处理更有效和更易于理解。祝好运。 - shellter
在Unix.SE上的同一问题:在shell脚本变量定义中执行浮点运算 - Palec
1
相关问题:在Unix.SE上如何进行整数和浮点数计算,使用bash或其他编程语言/框架? - kenorb
4个回答

15
我认为你应该使用:bc 例如:
echo "scale = 10; 123.456789/345.345345" | bc

(这就是Unix的方式:每个工具都专门用于做好自己应该做的事情,它们共同协作完成伟大的事情。不要通过另一个伟大的工具来模拟一个伟大的工具,而是让它们一起工作。)
.3574879198

或者使用比例尺为1而不是10
echo "scale = 1; 123.456789/345.345345" | bc

输出:

.3

请注意,这不会进行舍入操作。
如果您需要执行更复杂的操作,我强烈建议您转用awk,或者对于最复杂的操作使用perl。
示例:使用awk完成您的操作:
# create the test file:
printf '1.5493482,3.49384,33.284732,23.043852,2.2384,12.1,13.4,...\n' > somefile
printf '3.384,3.282342,23.043852,2.23284,8.39283,14.1,15.2,...\n'    >> somefile

# do OP's calculations (and DEBUG print them out!)

awk -F',' '
   # put no single quote in here... even in comments! you can instead print a: \047 
   # the -F tell awk to use "," as a separator. Thus awk will automatically split lines for us using it. 
   # $1=before first ","  $2=between 1st and 2nd ","  ... etc.
    function some_awk_function_here_if_you_want() {  # optionnal function definition
         # some actions here. you can even have arguments to the function, etc.
         print "DEBUG: no action defined in some_awk_function_here_if_you_want yet ..."
    }
    
    BEGIN      {  rem="Optionnal START section. here you can put initialisations, that happens before the FIRST file-s FIRST line is read"
    }
    
    (NF>=8)    {  rem="for each line with at least 8 values separated by commas (and only for lines meeting that condition)"
                  calc1=($2 - $7)
                  calc2=($3 * $2)
                  calc3=($2 - $1)
                  calc4=($2 + $8)
                  # uncomment to call this function :(ex1): #  some_awk_function_here_if_you_want
                  # uncomment to call this script:(ex2): # cmd="/path/to/some/script.sh \"" calc1 "\" \"" calc2 "\" ..." ; rem="continued next line"
                  # uncomment to call this script:(ex2): # system(cmd); close(cmd) 
                  line_no=(FNR-1) # ? why -1? .  FNR=line number in the CURRENT file.   NR=line number since the beginning (NR>FNR after the first file ...)
                  print "DEBUG: calc1=" calc1 " , calc2=" calc2 " , calc3=" calc3 " , calc4=" calc4 " , line_no=" line_no
                  print "DEBUG fancier_exemples: see man printf for lots of info on formatting (%...f for floats, %...d for integer, %...s for strings, etc)"
                  printf("DEBUG: calc1=%d , calc2=%10.2f , calc3=%s , calc4=%d , line_no=%d\n",calc1, calc2, calc3, calc4, line_no)
    }

    END        {  rem="Optionnal END section. here you can put things that need to happen AFTER the LAST file-s LAST line is read"
    }
      
'  somefile # end of the awk script, and the list of file(s) to be read by it.

请问您能否详细解释一下“scale”是什么?我已经做了一些搜索并查看了相关手册,但似乎无法理解它的强大之处以及如何在代码中使用。如果有任何有用的链接也可以提供。谢谢。 - Dave Engineer
这里使用的工具是bc,从stdin接收命令。Scale是在bc内部使用的命令。请参见gnu的bc页面上的更多信息 - Olivier Dulac
@DaveEngineer 我在结尾处添加了一个 awk 使用示例,它比 bash 计算更强大且更易于使用(awk 可以自动分割、可以进行复杂的函数调用、可以调用 shell 脚本或命令、具有合理的默认值等)。 - Olivier Dulac

11

如果你没有"bc",你可以使用'awk':

calc=$(echo 2.3 4.6 | awk '{ printf "%f", $1 + $2 }')

10

这个怎么样?

calc=$(echo "$String2 + $String8"|bc)

这将使bc将$String2和$String8的值相加,并将结果保存在变量calc中。


2

在BC中,scale是精度参数。如果您使用4的比例尺,则输入bc <<< 'scale=4;22.0/7',您将得到3.1428作为答案。如果您使用8个刻度,则会得到3.14285714,这是浮点数后8个数字。 因此,比例尺是一个精度因子。


bc = 基准计算器?来自http://en.wikipedia.org/wiki/Bc_(programming_language) - user2985029

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接