Grep медленно выходить после нахождения совпадения?

20

Я пытаюсь написать bash-скрипт, который опрашивает btmon на предмет подключения устройств. У меня есть рабочее решение, но оно абсурдно медленное, и кажется, что проблема в том, что grep очень медленно выходит после нахождения совпадения (около 25 секунд). Что я могу сделать, чтобы ускорить grepили вообще избежать его использования?

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

редактировать: чтобы уточнить, btmonэто инструмент мониторинга bluetooth, входящий в комплект Bluez, а cec-client - это утилита, которая поставляется вместе с libCEC для выдачи команд через последовательную шину HDMI-CEC (среди прочего).

обкрадывать
источник
2
Сколько "материала" btmonвыводит? Вы уверены, что это не просто вопрос буферизации?
SteelDriver
@steeldriver Seconded. Вы пробовали отключить буферизацию в трубе?
l0b0
btmon выводит около 250 символов в секунду.
Роб
@ l0b0 Я попытался отключить буферизацию с помощью команды unbuffer, но, похоже, это вообще мешает завершению работы grep? Я также попытался перевести grep в режим --line-buffer, но это не помогло.
Роб
Это может быть то, что btmonреализует саму буферизацию, и в этом случае вам не повезло.
l0b0

Ответы:

28

В:

cmd1 | cmd2

Большинство оболочек (оболочка Bourne, (t) csh, а также yash и некоторые версии AT & T ksh при некоторых условиях, являющихся заметными исключениями) ждут обоих cmd1и cmd2.

В bash, вы заметите, что

sleep 1 | uname

возвращается через одну секунду.

В:

btmon | grep -m 1 '@ Device Disconnected'

grepвыйдет, как только он обнаружит одно вхождение шаблона, но bashвсе равно будет ждать btmon.

btmonобычно умирает от SIGPIPE при следующей записи в канал после grepвозвращения, но если он никогда ничего не записывает, он никогда не получит этот сигнал.

Вы могли бы заменить #! /bin/bashс , #! /bin/ksh93как это оболочка совместима с bashи один , который ждет только для последнего компонента трубопровода. Затем в

btmon | grep -m 1 '@ Device Disconnected'

после grepвозврата btmonбудет работать в фоновом режиме, и оболочка будет продолжать работу с остальной частью сценария.

Если вы хотите убить, btmonкак только grepвернетесь, POSIXly, вы можете сделать что-то вроде:

sh -c 'echo "$$"; exec btmon' | (
   read pid
   grep -m1 '@ Device Disconnected' || exit
   kill "$pid" 2> /dev/null
   true)
Стефан Шазелас
источник
3
Спасибо, что объяснили, почему это так. Мне не пришло в голову, что bash может ждать выхода btmon. Перестановка на ksh93 работает прекрасно!
Роб