Como calcular valores em um script de shell?

3

Eu corro este comando no terminal:

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

E recebo esta saída:

Blah: 44 / 89 * 100

O que eu espero ver:

49.4

Existe uma maneira de obter a saída desejada usando apenas os comandos bash? Eu não prefiro scripts que eu estou planejando para enviar a saída.

    
por jokerdino 06.11.2011 / 11:21

3 respostas

4

Seu código diz para imprimir uma string. Não diz em lugar nenhum que essa string é, de fato, uma expressão aritmética que você deseja avaliar. Então você não pode razoavelmente esperar que sua expressão seja avaliada.

Seu código está abaixo do ideal. $(wc -l) contará o número de correspondências retornadas por grep , mas há uma maneira mais simples: execute grep -c . $(ls | wc -l) é uma maneira não confiável de contar os arquivos não pontuais no diretório atual, porque a saída de ls não é confiável ; $(set -- *; echo $#) é uma maneira confiável de fazer isso (supondo que haja pelo menos um arquivo correspondente; se essa suposição não for válida, use $(set -- *; if [ -e "$1" ]; then echo $#; else echo 0; fi , mas observe que isso resultará em uma divisão por zero abaixo da qual você deve tratar como um condição de erro de uma forma ou outra). Então você pode escrever seu código desta maneira:

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

ou você pode alinhar o cálculo dos dois valores intermediários:

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

Agora, para realizar a aritmética, você pode usar a expansão aritmética integrada do shell , mas é limitado a operações inteiras, portanto, o operador / será arredondado para o número inteiro mais próximo.

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

Em ksh93, zsh e yash, mas não em outros shells, você obtém aritmética de ponto flutuante se houver algo na expressão para forçar um ponto flutuante, como uma constante de ponto flutuante. Este recurso não está presente no shell Bourne, ksh88, pdksh, bash, ash.

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

O utilitário bc executa operações no número decimal com precisão arbitrária.

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

Outro utilitário padrão que pode executar computação de ponto flutuante (com menos funções matemáticas disponíveis) é awk .

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

Primeiro de tudo, você não especificou seu shell. Presumo que você esteja usando bash , mas, por favor, diga isso no futuro.

Também é muito importante que você não analise a saída de ls . Há uma boa documentação sobre por que não fazer isso aqui .

Além disso, o que você está tentando obter a saída percentual? Você não parece estar tentando calcular uma porcentagem no final. Por enquanto eu apenas fiz o cálculo exato que você listou.

Aqui está um pequeno script que deve ser capaz de fazer isso sem os problemas mencionados:

#!/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[@]}")"

Tenha em mente que bc não é portátil. Se você não se importar em usar a aritmética inteira, você pode usar o shell para calcular e retornar em vez de passá-lo para bc usando $(( .

    
por 06.11.2011 / 13:08
0

Algo parecido com isto?

echo "scale=2;100*'grep 'bla bla bla' bla* | wc -l'/'cat bla* | wc -l'" | bc
    
por 06.11.2011 / 13:35