シェルスクリプトから値を計算する方法は?

シェルスクリプトから値を計算する方法は?

端末で次のコマンドを実行します。

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 $#)これはこれを行う信頼できる方法です(少なくとも1つの一致するファイルがあると仮定し、その仮定が維持されない可能性がある場合はそれを使用してください。$(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"

または、2つの中間値の計算をインラインで実行できます。

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

これで、算術演算を実行するためにシェルの組み込み関数を使用できます。算術拡張ただし、整数演算に制限されるため、/演算子は最も近い整数に丸められます。

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

ksh93、zsh、yash(他のシェルでは除く)の式に浮動小数点を強制する項目(浮動小数点定数など)がある場合、浮動小数点算術が得られます。この機能は、Bourne Shell、ksh88、pdksh、bash、ashには存在しません。

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

これbcユーティリティは、任意の精度で10進数の操作を実行します。

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携帯できませんので参考にしてください。整数演算を使用しても問題ない場合は、代わりにbcシェルを使用して計算して返すことができます$((

答え3

このような?

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

関連情報