Grep powoli wychodzi po znalezieniu meczu?

20

Próbuję napisać skrypt bash, który odpytuje btmon w poszukiwaniu połączeń urządzeń. Mam działające rozwiązanie, ale jest absurdalnie wolne i wydaje się, że problem polega na tym, że grep bardzo powoli wychodzi z gry po znalezieniu dopasowania (około 25 sekund). Co mogę zrobić, aby przyspieszyć greplub całkowicie go nie używać?

#!/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

edycja: Aby to wyjaśnić, btmonjest narzędziem do monitorowania Bluetooth, które jest częścią pakietu Bluez, a cec-client jest narzędziem, które jest spakowane z libCEC do wydawania poleceń przez magistralę szeregową HDMI-CEC (między innymi).

Obrabować
źródło
2
Ile „rzeczy” wydaje btmon? czy jesteś pewien, że nie chodzi tylko o buforowanie?
steeldriver
@steeldriver Seconded. Czy próbowałeś wyłączyć buforowanie w potoku?
l0b0
btmon generuje około 250 znaków na sekundę.
Rob
@ l0b0 Próbowałem wyłączyć buforowanie za pomocą polecenia unbuffer, ale wydaje się, że w ogóle uniemożliwia to wyjście grepowi? Próbowałem też wymusić grep na tryb --line-buffer, ale to nie pomogło.
Rob
Może się zdarzyć, że btmonsamo buforuje, w którym to przypadku nie masz szczęścia.
l0b0

Odpowiedzi:

28

W:

cmd1 | cmd2

Większość pocisków (powłoka Bourne'a, (t) csh, a także yash i niektóre wersje AT&T ksh, pod pewnymi warunkami będącymi godnymi uwagi wyjątkami) czekają na oba cmd1i cmd2.

W bash, można zauważyć, że

sleep 1 | uname

wraca po jednej sekundzie.

W:

btmon | grep -m 1 '@ Device Disconnected'

grepwyjdzie, gdy tylko znajdzie jedno wystąpienie wzorca, ale bashnadal będzie czekać btmon.

btmonzazwyczaj umiera z SIGPIPE następnym razem, gdy pisze do potoku po greppowrocie, ale jeśli już nigdy więcej nie napisze, nigdy nie otrzyma tego sygnału.

Można wymienić #! /bin/bashz #! /bin/ksh93jak to jest kompatybilne z muszli bashi jeden, który czeka tylko na ostatni składnik rurociągu. Następnie w

btmon | grep -m 1 '@ Device Disconnected'

po greppowrocie btmonpozostawałby uruchomiony w tle, a powłoka kontynuowałaby resztę skryptu.

Jeśli chcesz zabić, btmongdy tylko grepwróci, POSIXly, możesz zrobić coś takiego:

sh -c 'echo "$$"; exec btmon' | (
   read pid
   grep -m1 '@ Device Disconnected' || exit
   kill "$pid" 2> /dev/null
   true)
Stéphane Chazelas
źródło
3
Dziękujemy za wyjaśnienie, dlaczego tak się dzieje. Nie przyszło mi do głowy, że bash może czekać na wyjście btmon. Zamiana na ksh93 działa pięknie!
Rob