Script Bash: faça algo uma vez dentro de um loop e então pare, mas continue em loop

6

Eu tenho um script Bash que verifica a bateria do meu laptop e me envia uma notificação do Android (via shuttle, um script Bash que eu escrevi que serve como interface cli para a API do Pushbullet). Funciona muito bem, mas repetidamente me notificará para desconectar meu laptop quando a bateria atingir 100%. Em vez disso, eu prefiro que apenas me notifique 100% da carga uma vez, e é isso (ou seja, continue verificando, mas não notifique). No entanto, quero que ele me notifique repetidamente quando a bateria estiver fraca, para que eu lembre de conectar o laptop (que é atualmente o que ele faz).

Eu estava pensando que isso é possível usando comandos continue e break dentro do loop, mas não tenho certeza se é isso que eu quero (não estou muito familiarizado com eles).

De qualquer forma, gostaria de receber sugestões sobre como melhor implementar isso usando o Bash. Eu suspeito que isso é muito simples, mas eu não estou entendendo por algum motivo.

Aqui está o meu script:

#! /bin/bash

while true;
do

percent=$(acpi | awk '{ print $4}' | sed -e 's/%//g' |  sed -e 's/,//g')

 if [ "$percent" -le "20" ];
then
    shuttle push note Chrome "Aurora: Plug in now" "Battery is at $percent percent"
fi  
if [ "$percent" -eq "100" ];
then
    shuttle push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
fi

sleep 7m
done
    
por Andy Forceno 14.07.2015 / 03:38

3 respostas

3

Que tal:

if [ "$percent" -eq 100 ] && [ "$full_flag" -eq 0 ];
then
    shuttle push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
    full_flag=1
fi
if [ "$percent" -lt 100 ];
then
    full_flag=0
fi
    
por 14.07.2015 / 03:46
6
push(){
    shuttle push note Chrome \
        "Aurora: $1" \
        "Battery is at $percent percent"
}

full=0
while    percent=$(acpi | awk '{ print $4}' | sed 's/[,%]//g')
do       case $percent:$full in 
         (100:1) ;; (100:0)
              full=1
              push 'Battery charged';;
         (?:*|1?:*|20:*)
              full=0
              push 'Plug in Now';;
         (*1) full=0
         esac
done

A instrução case do shell permite que você execute um bloco de código arbitrário baseado em se um padrão de shell pode ou não ser comparado com o valor de uma expansão do shell. Desta forma, você pode simplesmente lidar com múltiplos resultados possíveis do mesmo teste.

Acima eu concateno os valores de $percent e $full em um delimitador de : dois pontos, que é uma técnica que eu peguei originalmente de Stephane Chazelas, mesmo que eu tenha me tornado bastante bom desde então. Como o código executado precisa depender dos dois valores, testá-los simultaneamente é a solução mais simples.

Os blocos de padrões

case são avaliados em ordem do primeiro ao último. Assim que um padrão é correspondido, o código associado é executado e case é retornado. O primeiro padrão correspondente também é o último padrão avaliado.

E se os valores de $percent e $full forem respectivamente:

  1. 100 e 1

    • O primeiro padrão é correspondido, que está associado a um bloco de códigos vazio. Nenhuma ação é tomada e case retorna para while .
  2. 100 e 0

    • O segundo padrão é correspondido, o que define $full para 1 e chama a função de shell push w / o argumento Battery Charged
    • push() só é definido aqui para organizar o código de acordo com a finalidade.
  3. < = 20 e qualquer coisa

    • O terceiro padrão é correspondido, $full é definido como 0 e push é chamado com o arg Plug in Now .
  4. qualquer coisa e 1

    • O último padrão é correspondido e $full está definido como 0.
  5. Qualquer outra coisa

    • Nenhum padrão é correspondido e nenhuma ação é tomada.

Em resumo, isso significa que $full é definido apenas quando necessário, push() só é chamado quando $full é 0 e $percent é 100 ou $full é nada e $percent é < = 20.

Tudo isso seria uma melhoria em relação ao que você já tem. Mas o problema real é:

percent=$(acpi | awk '{ print $4}' | sed 's/[,%]//g')

Bifurcar várias vezes como essa em while true é incrivelmente um desperdício. Você tem que pensar em outra maneira de fazer isso.

    
por 14.07.2015 / 04:29
1

continue pula para a próxima iteração do loop. break sai do loop completamente. Nenhum deles é útil se você quiser ter algum efeito em futuras iterações. Para isso, você precisa se lembrar da informação de que algo tem que se comportar de maneira diferente. A maneira de lembrar algumas informações é armazená-las em uma variável.

Uma maneira de fazer o que você deseja é armazenar o valor anterior de percent em uma variável previous . Se percent e previous forem ambos 100, não imprima uma mensagem.

Dado que você está executando isso em segundo plano para monitorar a bateria, você deve se esforçar para não usar muita energia. Minimize o número de processos.

Em vez de executar o loop em true , execute o loop no comando sleep . Dessa forma, você pode matar o script matando o comando sleep , quando ele estiver em execução, o que acontece na maior parte do tempo.

Recue seu código. Qualquer coisa dentro de um bloco ( do , then , etc.) deve ter mais espaço em branco à esquerda, por um número consistente.

previous=
while sleep 7m
do
  percent=$(acpi | awk '{ gsub(/[^ 0-9]/, ""); print $4 }')
  if [ "$percent" -le "20" ]; then
    shuttle push note Chrome "Aurora: Plug in now" "Battery is at $percent percent"
  elif [ "$percent" -eq "100" ] && [ "$previous" -ne "100" ]; then
    shuttle push note Chrome "Aurora: Battery charged" "Battery is at $percent percent"
  fi
done
    
por 15.07.2015 / 01:11