如何在shell脚本中计算值?

如何在shell脚本中计算值?

我在终端中运行此命令:

grep "bla bla blah" blah* | echo "Blah: $(wc -l) / $(ls | wc -l) * 100"

我得到这个输出:

Blah: 44 / 89 * 100

我期望看到什么:

49.4

有没有办法仅使用 bash 命令来获得所需的输出?我不喜欢我计划通过管道输出的脚本。

答案1

你的代码说要打印一个字符串。它没有在任何地方表明该字符串实际上是您想要计算的算术表达式。所以你不能合理地期望你的表达式被评估。

您的代码不是最优的。$(wc -l)将计算 所返回的匹配数grep,但有一种更简单的方法:grep -c改为运行。$(ls | wc -l)是计算当前目录中非点文件的不可靠方法,因为的输出ls不可靠;$(set -- *; echo $#)是执行此操作的一种可靠方法(假设至少有一个匹配文件;如果该假设可能不成立,请使用$(set -- *; if [ -e "$1" ]; then echo $#; else echo 0; fi,但请注意,这将导致除以零,低于该值您应该以某种方式将其视为错误条件)。所以你可以这样编写你的代码:

matches=$(grep -c "bla bla blah" blah*)
files=$(set -- *; echo $#)
echo "Blah: $matches / $files * 100"

或者您可以内联两个中间值的计算:

echo "Blah: $(grep -c "bla bla blah" blah*) / $(set -- *; echo $#) * 100"

现在,要执行算术,您可以使用 shell 的内置函数算术展开,但它仅限于整数运算,因此/运算符将向下舍入到最接近的整数。

echo "Blah: $(($matches * 100 / $files))"

在 ksh93、zsh 和 yash 中(但在其他 shell 中除外),如果表达式中存在强制浮点的内容(例如浮点常量),您将获得浮点算术。 Bourne shell、ksh88、pdksh、bash、ash 中不存在此功能。

echo "Blah: $(($matches * 100.0 / $files))"

bc实用程序以任意精度对十进制数执行运算。

echo "Blah: $(echo "scale=2; $matches * 100 / $files" | bc)"

另一个可以执行浮点计算的标准实用程序(可用的数学函数较少)是awk

echo "$matches" "$files" | awk '{print "Blah:", $1 * 100 / $2}'

答案2

首先,您还没有指定您的外壳。我假设您正在使用bash,但请在以后说明。

同样非常重要的是你解析 的输出ls。有很好的文档说明为什么不这样做这里

还,什么您是否正在尝试获取百分比输出?您似乎并没有试图在最后计算百分比。现在我只是做了你列出的精确计算。

这是一个小脚本,应该能够在没有提到的问题的情况下执行此操作:

#!/bin/bash

_die() {
    printf '%s\n' "${@:2}"
    exit "$1"
}

(( $# )) || _die 1 "Usage: ${0##*/} pattern <dir>"

[[ $2 ]] && _dir=$2 || _dir=.

[[ -d ${_dir} ]] || _die 2 "Directory does not exist: ${_dir}"

for _file in "${_dir}"/*; do
    [[ -f ${_file} ]] && _files+=( "${_file}" )
done

(( ${#_files[@]} )) || _die 3 'No files matched by glob, not attempting to divide by 0.'

# We pass the same files found to grep instead of reglobbing to avoid a race condition.
while IFS= read -r _number_of_matches; do
    (( _total_matches )) && (( _total_matches+=_number_of_matches )) || _total_matches=${_number_of_matches}
done < <(grep -hc "$1" "${_files[@]}")

(( _total_matches )) || _die 4 "Nothing matched by expression: $1"

printf 'Blah: %s\n' "$(bc <<< "${_total_matches}/${#_files[@]}")"

请记住,它bc不可移植。如果您不介意使用整数算术,则可以使用 shell 来计算并返回它,而不是bc使用$((.

答案3

像这样的东西吗?

echo "scale=2;100*`grep 'bla bla bla' bla* | wc -l`/`cat bla* | wc -l`" | bc

相关内容