Команда не дает вывода при чтении?

У меня довольно странная проблема. Я не могу понять, почему это происходит и как это исправить.

Сценарий:

#!system/bin/sh
#set -x

reader() {
    t2=-1
    grep -v -E "add device|name:" | while IFS=' ' read -r t1 a b c d _; do
        t1=${t1%%-*}
        t=`expr $t1 - $t2`
        if let 't > 0 && t2 > -1'; then
            echo "sleep $t"
        fi
        printf 'sendevent %s' "${a%?}"
        printf '%5d %5d %5d\n' "0x$b" "0x$c" "0x$d"
        t2=$t1
    done
}

let() {
    IFS=, command eval [ '$(($*))' -ne 0 ]
}

countDown() {
    echo 'Starting in...'
    i=4
    while [[ $i -gt 1 ]]; do
        i=$(($i-1))
        echo "$i"
        sleep 1
    done
    printf '%s\n\n\n' 'Go!'
#   echo "$*"
    "$@" | reader
}

clear
printf '%s >' 'Catch by [n]umber of events or [t]imeout?'
read type

case $type in
    n)
        printf '%s >' 'Events to catch?' 
        read arg
        echo "Gonna catch $arg events!"
        countDown getevent -t -c "$arg"
        ;;
    t)
        printf '%s >' 'Timeout (in seconds)?' 
        read arg
        echo "Gonna catch events for $arg seconds!"
        countDown timeout -t $arg getevent -t
esac

Цель скрипта:

Перехватывайте действия пользователя (например, нажатия клавиш, касания экрана и т. д.) с помощью команды getevent и выводит сценарий на стандартный вывод, который можно использовать для репликации этих событий.

Дополнительная информация:

вывод getevent в шестнадцатеричном формате sendevent принимает десятичный формат

Ожидаемый результат:

Catch by [n]umber or by [t]imeout? n
Events to catch? > 4
Gonna catch 4 events...
Starting in...
3
2
1
Go!
sendevent /dev/input/event5 1 102 1
sendevent /dev/input/event5 0 0 0
sleep 3
sendevent /dev/input/event5 1 102 0
sendevent /dev/input/event5 0 0 0

Проблема:

Код работает, как и ожидалось, когда выбрано «n». При выборе «t» сценарий завершает работу через указанное количество секунд. Однако он ничего не выводит — первая строка цикла while даже не запускается.

С set -x вот что показано (последние несколько строк):

+ printf %s\n\n\n Go!
Go!
+ reader
+ t2=-1
+ grep -v -E add device|name:
+ timeout -t 5 getevent -t
+ IFS =  read -r t1 a b c d _

Запуск только этого показывает вывод на стандартный вывод (вывод, который должен быть прочитан и изменен внутри цикла while):

timeout -t 5 getevent -t

Любые идеи?

Спасибо.

ИЗМЕНИТЬ:

Хорошо, я думаю, что это проблема с буферизацией. По сути, это приводит к аналогичной проблеме: getevent > output Файл обновляется при каждом наборе событий, но не мгновенно (и может никогда не обновляться, если создается недостаточное количество событий). Я не знаю никаких обходных путей на Android.


person GermainZ    schedule 20.02.2013    source источник
comment
Не по теме для SO. Попробуйте разработчиков XDA   -  person Simon    schedule 21.02.2013
comment
@Simon Как это не по теме? Он может использовать специальные команды для Android, но это вопрос программирования, учитывая проблему, с которой я сталкиваюсь.   -  person GermainZ    schedule 21.02.2013
comment
извините, я не могу помочь в этом конкретном кусочке арканы оболочки, но +1 за хорошо сформулированный вопрос, показывающий значительную работу. Продолжайте публиковать. Удачи!   -  person shellter    schedule 21.02.2013
comment
@shellter ormaaj переписал мой первый скрипт (позже мне пришлось изменить его версию из-за явно глючной оболочки), так что ему следует отдать должное за скрипт :)   -  person GermainZ    schedule 21.02.2013


Ответы (3)


Я думаю, что нет возможности включить линейную буферизацию в busybox grep, но вы можете сделать что-то вроде этого:

$ adb shell timeout -t 10 getevent -t | \
    grep --line-buffered -v -E "add device|name:" | \
    while read line; do echo "READ: $line"; done
person Diego Torres Milano    schedule 21.02.2013
comment
Это работает даже без --line-buffered с использованием adb (и Ubuntu), но та же проблема возникает, как только я пытаюсь использовать оболочку Android без --line-buffered (как вы сказали, она не поддерживается). - person GermainZ; 21.02.2013
comment
Если вы пытаетесь сохранить список событий для последующего воспроизведения, возможно, лучшим решением будет использование хост-компьютера. - person Diego Torres Milano; 22.02.2013
comment
Вся идея состоит в том, чтобы использовать его на самом устройстве (например, для прямого использования с другими приложениями, такими как: LMT Launcher, Tasker и т. д.) и поделиться им с аудиторией, у которой может не быть дистрибутива Linux (или готового к использованию). Цигвин). - person GermainZ; 22.02.2013

Это был в основном тот материал, который я перевел из вашего кода, прежде чем его искалечили обходные пути. Заменив одну команду, которой у меня нет, на cat в ветке тайм-аута, она работает правильно с вашим образцом ввода в Busybox sh, dash, mksh, bash и ksh93.

Поскольку так много основных вещей в оболочке и приложениях сломаны, неудивительно, что это не работает. Я бы удостоверился, что Busybox обновлен, и посмотрел, воспроизводятся ли арифметические и другие ошибки, которые вы постоянно сталкиваетесь, в других системах, и сообщил бы об ошибках, если этот код не работает.

#!/bin/sh

reader() {
    t2=-1
    grep -vE '^(add device|[[:space:]]+name:)' | 
    while IFS=' ' read -r t1 a b c d _; do
        let "(t = (t1 = ${t1%%-*}) - t2) > 0 && t2 > -1" && echo "sleep $t"
        printf 'sendevent %s' "${a%[[:digit:]]:}"
        printf '%5d %5d %5d\n' "0x$b" "0x$c" "0x$d"
        let t2=t1
    done
}

let() {
    IFS=, command eval test '$(($*))' -ne 0
}

countDown() {
    echo 'Starting in...'
    i=4
    while let 'i-=1 > 0'; do
        echo "$i"
        sleep 1
    done
    printf '%s\n\n\n' 'Go!'
    echo "$*"
    "$@" <&3 | reader
}

isDigit() {
    while ! ${1+false}; do
        case $1 in
            *[^[:digit:]]*|'') return 1
        esac
        command shift
    done 2>/dev/null
}

main() {
    printf '%s >' 'Catch by [n]umber of events or [t]imeout?'
    read type

    case $type in
        n)
            printf '%s >' 'Events to catch?' 
            read arg
            isDigit "$arg" || return 1
            echo "Gonna catch $arg events!"
            countDown getevent -t -c "$arg"
            ;;
        t)
            printf '%s >' 'Timeout (in seconds)?' 
            read arg
            isDigit "$arg" || return 1
            echo "Gonna catch events for $arg seconds!"
            countDown busybox timeout -t "$arg" cat -
            ;;
        *)
            return 1
    esac
}

main "$@" 4<&0 <<\EOF 3<&0 <&4 4<&-
add device 1: /dev/input/event8
  name:     "bcm_headset"
add device 2: /dev/input/event7
  name:     "max8986_ponkey"
add device 3: /dev/input/event6
  name:     "sec_touchscreen"
add device 4: /dev/input/event5
  name:     "sec_keypad"
add device 5: /dev/input/event4
  name:     "orientation"
add device 6: /dev/input/event3
  name:     "accelerometer"
add device 7: /dev/input/event0
  name:     "proximity_sensor"
add device 8: /dev/input/event2
  name:     "geomagnetic_raw"
add device 9: /dev/input/event1
  name:     "geomagnetic"
45534-48646 /dev/input/event6: 0001 008b 00000001
45534-48646 /dev/input/event6: 0000 0000 00000000
45534-48646 /dev/input/event6: 0001 008b 00000000
45534-48646 /dev/input/event6: 0000 0000 00000000
EOF

# vim: set fenc=utf-8 ff=unix ft=sh :

Выход:

 $ bb --help
BusyBox v1.21.0 (2013-02-20 20:39:21 CST) multi-call binary.

Usage: bb [-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]

Unix shell interpreter
 $ bb answers/countdown
Catch by [n]umber of events or [t]imeout? >t
Timeout (in seconds)? >5
Gonna catch events for 5 seconds!
Starting in...
3
2
1
Go!


busybox timeout -t 5 cat -
sendevent /dev/input/event6:    1   139     1
sendevent /dev/input/event6:    0     0     0
sendevent /dev/input/event6:    1   139     0
sendevent /dev/input/event6:    0     0     0
person ormaaj    schedule 21.02.2013
comment
Присваивания выдают ошибку: eval: arith: syntax error: "t2=t1" eval: arith: syntax error: "(t = (t1 = 45534) -t2) > 0 && t2 > -1" При этом преобразование действительно работает с cat, но воспроизводится та же проблема (вообще не выводится - даже ошибок) при замене cat на getevent или getevent -t. - person GermainZ; 21.02.2013

Была та же проблема, и я вспомнил трюк, который я сначала использовал в MS-DOS 3.30, а затем в Cygwin (не для тайм-аута, но команда more - волшебная пуля для многих проблем с перенаправлением):

timeout -t 8 getevent | more > getevent.txt

хорошо работает в оболочке Total Commander с su (не sh)

Пожалуйста :-)

person Zwaa KwaZuluNatal    schedule 16.12.2013