2

I have a rather weird issue. I can't figure out why it's happening or how to fix it.

The script:

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

The goal of the script:

Catch the user's interactions (e.g., key presses, screen taps, etc.) using the getevent command and outputs a script to standard output, that can be used to replicate these events.

Additional information:

getevent's output is in hexadecimal format, and sendevent accepts decimal format.

Expected output:

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

The problem:

The code runs as expected when "n" is picked. When "t" is picked, the script exits after the specified amount of seconds. However, it does not output anything - the first line of the while loop doesn't even run.

With set -x, here's what's shown (last few lines):

+ 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 _

Running this alone shows output to standard output (output that's supposed to be read and modified inside the while loop):

timeout -t 5 getevent -t

Any ideas?

All right, so I think it's a buffering issue. Basically this gives a similar issue:

 `getevent > output`

The file gets updated every bunch of events, but not instantly (and might never update if not enough events are made). I'm not aware of any work around on Android.

4
  • Off topic for SO. Try XDA Devs Commented Feb 20, 2013 at 22:43
  • @Simon How is this off topic? It might use android specific commands but it's a programming question, considering the problem I'm encountering. Commented Feb 20, 2013 at 22:47
  • sorry I can't help on this particular bit of shell arcana, but +1 for well framed question showing considerable work. Keep posting. Good luck! Commented Feb 21, 2013 at 1:51
  • @shellter ormaaj rewrote my first script (I later had to modify his version because of my apparently buggy shell), so credits for the script should be given for him :) Commented Feb 21, 2013 at 12:44

3 Answers 3

2

I think there isn't any way of enabling line-buffering in BusyBox's grep, but you can do something like this:

adb shell timeout -t 10 getevent -t | \
    grep --line-buffered -v -E "add device|name:" | \
    while read line; do echo "READ: $line"; done
Sign up to request clarification or add additional context in comments.

3 Comments

That works even without --line-buffered using adb (and Ubuntu), but the same issue appears as soon as I try it using Android's shell without --line-buffered (it isn't supported as you said).
If you are trying to save a list of events to reproduce later, using the host computer is probably the better solution.
The whole idea is to use it on the device itself (e.g. for direct use with other apps, for example: LMT Launcher, Tasker, etc), and share it with an audience that might not be running a Linux distro (or ready to use Cygwin).
2

Here was basically that stuff I translated from your code before being mangled by bug workarounds. Replacing the one command I don't have with cat on the timeout branch, it runs correctly with your sample input in BusyBox sh, Dash, mksh, Bash, and KornShell 93.

Since so many basic things about the shell and applications are broken, it isn't surprising this doesn't work. I would make sure BusyBox is up-to-date and see whether the arithmetic and other bugs you keep hitting are reproducible on other systems, and report bugs if this code doesn't work.

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

Command:

bb --help

Output:

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

And:

bb answers/countdown

Output:

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!

And:

busybox timeout -t 5 cat -

Output:

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

1 Comment

Assignments are giving an error: eval: arith: syntax error: "t2=t1" eval: arith: syntax error: "(t = (t1 = 45534) -t2) > 0 && t2 > -1" That being said, the conversion actually works with cat, but the same problem is reproduced (not output at all - not even errors) when I replace cat by getevent or getevent -t.
0

I had the same problem, and remembered a trick that I first used in MS-DOS 3.30, and later in Cygwin (not for timeout, but the more command is a magical bullet for lots of redirect problems):

timeout -t 8 getevent | more > getevent.txt

It works well in Total Commander's shell with su (not sh).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.