Grep tard pour sortir après avoir trouvé le match?

20

J'essaie d'écrire un script bash qui interroge btmon pour les connexions de périphériques. J'ai une solution qui fonctionne, mais c'est absurdement lent, et il semble que le problème soit que grep soit très lent à quitter après avoir trouvé une correspondance (environ 25 secondes). Que puis-je faire pour accélérer grepou éviter complètement de l'utiliser?

#!/bin/bash
COUNTER=0
while :
  do
    until btmon | grep -m 1 '@ Device Connected'
      do :
    done
    let COUNTER=COUNTER+1
    echo on 0 | cec-client RPI -s -d 1
    sleep 5
    echo as | cec-client RPI -s -d 1
    until btmon | grep -m 1 '@ Device Disconnected'
      do :
    done
    let COUNTER=COUNTER-1
    if [ $COUNTER -eq 0 ];
      then echo standby 0 | cec-client RPI -s -d 1;
    fi
done

edit: Pour clarifier, btmonet est un outil de surveillance Bluetooth qui fait partie de la suite Bluez, et cec-client est un utilitaire fourni avec libCEC pour émettre des commandes sur le bus série HDMI-CEC (entre autres).

Rob
la source
2
Combien de "trucs" btmonsortent? êtes-vous sûr que ce n'est pas seulement une question de mise en mémoire tampon?
steeldriver
@steeldriver Appuyé. Avez-vous essayé de désactiver la mise en mémoire tampon dans le tuyau?
l0b0
btmon produit environ 250 caractères par seconde.
Rob
@ l0b0 J'ai essayé de désactiver la mise en mémoire tampon avec la commande unbuffer, mais cela semble empêcher la sortie de grep? J'ai également essayé de forcer grep en mode --line-buffer, mais cela n'a pas semblé aider.
Rob
Il se peut que cela btmonimplémente le tampon lui-même, auquel cas vous n'avez pas de chance.
l0b0

Réponses:

28

Dans:

cmd1 | cmd2

La plupart des shells (le shell Bourne, (t) csh, ainsi que le yash et certaines versions d'AT & T ksh sous certaines conditions étant les exceptions notables) attendent les deux cmd1et cmd2.

Dans bash, vous remarquerez que

sleep 1 | uname

revient après une seconde.

Dans:

btmon | grep -m 1 '@ Device Disconnected'

grepquittera dès qu'il aura trouvé une occurrence du modèle, mais bashattendra toujours btmon.

btmonmeurt généralement d'un SIGPIPE la prochaine fois qu'il écrit dans le tuyau après grepson retour, mais s'il n'écrit plus jamais, il ne recevra jamais ce signal.

Vous pouvez remplacer #! /bin/bashpar #! /bin/ksh93car c'est un shell compatible avec bashet un qui n'attend que le dernier composant d'un pipeline. Puis dans

btmon | grep -m 1 '@ Device Disconnected'

après les grepretours, btmonserait laissé tourner en arrière-plan et le shell continuerait avec le reste du script.

Si vous vouliez tuer btmondès le grepretour, POSIXly, vous pourriez faire quelque chose comme:

sh -c 'echo "$$"; exec btmon' | (
   read pid
   grep -m1 '@ Device Disconnected' || exit
   kill "$pid" 2> /dev/null
   true)
Stéphane Chazelas
la source
3
Merci d'avoir expliqué pourquoi cela se comporte comme ça. Il ne m'est pas venu à l'esprit que bash pourrait attendre la sortie de btmon. Le passage à ksh93 fonctionne à merveille!
Rob