bc erro aritmético

0

Estou seguindo este guia para tentar configurar uma configuração da barra de limão: link

Agora, quase tudo funciona perfeitamente, exceto por esta linha:

bc <<< "scale=2; 100 - $f / $t * 100" | cut -d. -f1

Nesta função:

memused() {
    read t f <<< 'grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}''
    bc <<< "scale=2; 100 - $f / $t * 100" | cut -d. -f1
}

Eu recebo o seguinte erro (standard_in) 1: syntax error

Agora, pelo que posso dizer, eu deveria estar adicionando colchetes duplos ao redor da equação (()) , mas eu tentei vários arranjos diferentes e nada corrigiu o erro.

    
por Otis Wright 29.10.2018 / 11:43

3 respostas

3

awk pode fazer todos os trabalhos de read , cut , grep e bc aqui:

 awk -F': *' '
   $1 == "MemTotal" {t = $2}
   $1 == "MemFree" {f = $2}
   END {printf "%.1f%%\n", (t - f) * 100 / t}' /proc/meminfo

Tenha em atenção que nem todas as implementações awk produzirão 40,5% em vez de 40,5 nas localidades onde a vírgula é usada como base decimal (GNU awk apenas o faz no modo POSIX como quando $POSIXLY_CORRECT está no ambiente ). Use LC_ALL=C awk... para forçar a raiz decimal a ponto / período.

Altere %.1f para %.0f se você não quiser uma parte decimal. Isso arredondará para o inteiro mais próximo . Use %d se você quiser truncar a parte decimal (obtenha 99% ao invés de 100% para 99.999).

Seu read t f <<< 'grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}'' teria funcionado em versões mais antigas de bash , assumindo uma $IFS não modificada.

Em cmd <<< 'code' , bash usado para dividir a saída de code nos caracteres de $IFS (que, por padrão, inclui nova linha) e uni-los ao primeiro caractere de $IFS (por padrão espaço) antes alimentando como stdin ao comando. Portanto, no seu caso, essas duas linhas de saída de awk acabariam sendo alimentadas como duas palavras em uma linha conforme esperado por read t f . Esse comportamento surpreendente (e também diferente da implementação original em zsh ) foi corrigido no bash-4.4.

Em bash-4.4 e acima, você precisaria de { read t; read f; } <<< "$(awk...)" (aqui, adicionando aspas em torno de $(...) , para que também funcione no bash-4.3 e antes, ainda assumindo o valor padrão de $IFS ).

    
por 29.10.2018 / 11:58
1

Resumo:

Portátil, em uma linha e usando printf:

awk '/MemTotal/{t=$2}; /MemFree/{f=$2}; END{printf("%d\n",(1-f/t)*100)}'

Apenas Shell (bash):

memused() { : # Print the (integer) value of % of free memory
            local ret ref a t f 
            ret='MemTotal:[ \t]*([0-9]+)';              # Regex for Total memory.
            ref='MemFree:[ \t]*([0-9]+)';               # Regex for Free  memory.
            a=$(</proc/meminfo)                         # Get meminfo in var (a).
            [[ $a =~ $ret ]] && t="${BASH_REMATCH[1]}"  # Get Total memory.
            [[ $a =~ $ref ]] && f="${BASH_REMATCH[1]}"  # Get Free  memory.
            printf '%s\n' "$(( 100 - 100*f/t )) %"      # Print integer % value.
          }

Descrição

O motivo da reclamação de bc é que ela está recebendo apenas uma var.

Para reproduzir:

$ t=150 ; f='' ; bc <<< "scale=2; 100 - $f / $t * 100"
(standard_in) 1: syntax error

Uma maneira de evitar esse erro é definir um valor padrão:

$ t=150; f=''; bc <<< "scale=2; 100 - ${f:-0} / ${t:-0} * 100"
100

A razão para apenas um valor é que a leitura está obtendo os valores em linhas separadas (no bash 4.4). Uma solução que funciona bem para todas as versões bash é usar -d '' :

$ read -d '' t f <<< "'grep -E 'Mem(Total|Free)' /proc/meminfo |awk '{print $2}''"
$ echo "total=<$t>     free=<$f>"
total=<1922764>     free=<424360>

E isso também funciona se a expansão do comando estiver entre aspas (como deveria ser) "'grep … ''" ou não.

Mas não há motivo para chamar grep , awk , bc e finalmente cut . Uma chamada para o awk poderia fazer tudo:

</proc/meminfo awk '/MemTotal/{t=$2}
                    /MemFree/ {f=$2}
                    END{
                        print( (1-f/t)*100 )
                    }
                   '

Em uma linha e usando printf:

awk '/MemTotal/{t=$2};/MemFree/{f=$2};END{printf("%d\n",(1-f/t)*100)}' </proc/meminfo

Se você quiser algo que funcione mais rápido e use apenas o shell (embora bash):

memused() {  : # Print the (integer) value of % of free memory
             local ret ref a t f 
             ret='MemTotal:[ \t]*([0-9]+)';              # Regex for Total memory.
             ref='MemFree:[ \t]*([0-9]+)';               # Regex for Free  memory.
             a=$(</proc/meminfo)                         # Get meminfo in var (a).
             [[ $a =~ $ret ]] && t="${BASH_REMATCH[1]}"  # Get Total memory.
             [[ $a =~ $ref ]] && f="${BASH_REMATCH[1]}"  # Get Free  memory.
             printf '%s\n' "$(( 100 - 100*f/t )) %"      # Print integer % value.
   }
    
por 30.10.2018 / 06:44
0

A string lida pelo comando read contém apenas 1 campo, onde 2 é esperado para preencher as variáveis t e f .

Você pode resolvê-lo adicionando | tr '\n' ' ' após o comando awk .

Mas seria melhor simplificá-lo para apenas:

read t f <<< $(awk '/MemTotal/{t=$2}/MemFree/{f=$2}END{print t,f}' /proc/meminfo)
    
por 29.10.2018 / 11:58