Чтение двоичных данных stdout из оболочки adb?


можно ли прочитать двоичный stdout из команды оболочки adb? Например, все примеры использования screencap включают в себя два шага:

adb shell screencap -p /sdcard/foo.png
adb pull /sdcard/foo.png

тем не менее, служба поддерживает запись в stdout. Вы можете, например, сделать следующее:

adb shell "screencap -p > /sdcard/foo2.png"
adb pull /sdcard/foo2.png

и это работает одинаково хорошо. Но как насчет чтения выходных данных через ADB? Что я хочу сделать, это следующее:

adb shell screencap -p > foo3.png

и избегайте промежуточной записи на SD-карту. Это создает что-то такое выглядит как файл PNG (работает strings foo3.png генерирует что-то с IHDR, IEND и т. д.) и примерно такого же размера, но файл поврежден что касается читателей изображения.

Я также попытался сделать это с помощью ddmlib в java, и результаты одинаковы. Я был бы рад использовать любую библиотеку надо. Моя цель состоит в том, чтобы сократить общее время, чтобы получить захват. На моем устройстве, используя решение с двумя командами, требуется около 3 секунд, чтобы получить изображение. Использование ddmlib и захват stdout занимает менее 900 МС, но это не работает!

можно ли это сделать?

EDIT: вот hexdump из двух файлов. Первый-экран.png пришел из stdout и поврежден. Второй, xscreen - это решение с двумя командами и работает. Изображения должны быть визуально одинаковыми.

$ hexdump -C screen.png | head
00000000  89 50 4e 47 0d 0d 0a 1a  0d 0a 00 00 00 0d 49 48  |.PNG..........IH|
00000010  44 52 00 00 02 d0 00 00  05 00 08 06 00 00 00 6e  |DR.............n|
00000020  ce 65 3d 00 00 00 04 73  42 49 54 08 08 08 08 7c  |.e=....sBIT....||
00000030  08 64 88 00 00 20 00 49  44 41 54 78 9c ec bd 79  |.d... .IDATx...y|
00000040  9c 1d 55 9d f7 ff 3e 55  75 f7 de b7 74 77 d2 d9  |..U...>Uu...tw..|
00000050  bb b3 27 10 48 42 16 c0  20 01 86 5d 14 04 11 dc  |..'.HB.. ..]....|
00000060  78 44 9d c7 d1 d1 11 78  70 7e 23 33 8e 1b 38 33  |xD.....xp~#3..83|
00000070  ea 2c 8c 8e 0d 0a 08 a8  23 2a 0e 10 82 ac c1 40  |.,......#*.....@|
00000080  12 02 81 24 64 ef ec 5b  ef fb 5d 6b 3b bf 3f ea  |...$d..[..]k;.?.|
00000090  de db dd 49 27 e9 ee 74  77 3a e3 79 bf 5e 37 e7  |...I'..tw:.y.^7.|

$ hexdump -C xscreen.png | head
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 02 d0 00 00 05 00  08 06 00 00 00 6e ce 65  |.............n.e|
00000020  3d 00 00 00 04 73 42 49  54 08 08 08 08 7c 08 64  |=....sBIT....|.d|
00000030  88 00 00 20 00 49 44 41  54 78 9c ec 9d 77 98 1c  |... .IDATx...w..|
00000040  c5 99 ff 3f d5 dd 93 37  27 69 57 5a e5 55 4e 08  |...?...7'iWZ.UN.|
00000050  24 a1 00 58 18 04 26 08  8c 01 83 31 38 c0 19 9f  |$..X..&....18...|
00000060  ef 7c c6 3e 1f 70 f8 7e  67 ee 71 e2 b0 ef ce f6  |.|.>.p.~g.q.....|
00000070  f9 ec 73 04 1b 1c 31 60  23 84 30 22 88 a0 40 10  |..s...1`#.0"..@.|
00000080  08 65 69 95 d3 4a 9b c3  c4 4e f5 fb a3 67 66 77  |.ei..J...N...gfw|
00000090  a5 95 b4 bb da a4 73 7d  9e 67 55 f3 ed 50 5d dd  |......s}.gU..P].|

просто на быстрый взгляд кажется, что несколько дополнительных 0x0D (13) байт добавляются. Возврат каретки?? Делает это что-нибудь напоминает? Это смешивание в некоторых пустых строках?

15 53

15 ответов:

Извините, что публикую ответ на старый вопрос, но я просто столкнулся с этой проблемой сам и хотел сделать это только через оболочку. Это хорошо работало для меня:

adb shell screencap -p | sed 's/^M$//' > screenshot.png

Это ^M это символ, который я получил, нажав ctrl+v - > ctrl+m, просто заметил, что он не работает при копировании.

adb shell screencap -p | sed 's/\r$//' > screenshot.png

сделал трюк для меня.

в отличие от adb shell the adb exec-out команда не использует pty который искажает двоичный выход. Так что вы можете сделать

adb exec-out screencap -p > test.png

https://android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f

обратите внимание, что если вы используете этот метод для команды, которая производит вывод на STDERR, вы должны перенаправить его на /dev/null, иначе adb будет включать STDERR в его STDOUT развращает ваш выход. Например, если вы попытка резервного копирования и сжатия каталога:

adb exec-out "tar -zcf - /system 2>/dev/null" > system.tar.gz

как уже отмечалось, "adb shell" выполняет преобразование linefeed (0x0a) в carriage-return + linefeed (0x0d 0x0a). Это выполняется псевдо-линейной дисциплиной. Поскольку в оболочке нет команды" stty", нет простого способа возиться с настройками терминала.

это возможно делать то, что вы хотите с ddmlib. Вам нужно будет написать код, который выполнял команды на устройстве, захватил вывод и отправил его по проводу. Это больше или меньше того, что DDMS делает для определенных функций. Это может быть больше проблем, чем стоит.

The repair() решение-преобразование всех CRLF в LF-чувствует себя шатким, но на самом деле надежно, так как "разрушающее" преобразование LF в CRLF является детерминированным. Я использовал то же самое, чтобы восстановить непреднамеренные передачи FTP в ASCII-режиме.

стоит отметить, что формат файла PNG явно разработан, чтобы поймать именно эти (и связанные с ними) проблемы. Магическое число начинается с 0x89 в поймать все, что обнажает высокую битов, а затем "ПНГ", так что вы можете легко сказать, что в файл, с последующим CR LF в разнообразных символов ASCII строки преобразователи, затем 0x1a в ловушку старых версий MS-DOS программ, которые используют сочетания клавиш Ctrl-Z, как особый конец файла маркер, а потом одинокий ЛФ. Посмотрев на первые несколько байт файла, вы можете точно сказать, что с ним было сделано.

...что означает, что ваш repair() функция может принимать как" поврежденный", так и" чистый " вход и надежно определять, нужно ли это сделать ничего.

Edit: еще одно примечание: двоичный файл на стороне устройства можно настроить tty, чтобы избежать преобразования, используя cfmakeraw(). Смотрите на screenrecord команда в Android 5.0, которая может отправлять необработанное видео с живого захвата экрана через соединение оболочки ADB.

после углубления в шестнадцатеричные дампы стало ясно, что каждый раз, когда символ 0x0A был испущен, оболочка будет испускать 0x0D 0x0A. я восстановил поток с помощью следующего кода, и теперь двоичные данные верны. Теперь, конечно, вопрос в том, почему adb shell делает это? Но в любом случае, это решает проблему.

static byte[] repair(byte[] encoded) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for (int i=0; i<encoded.length; i++) {
        if (encoded.length > i+1 && encoded[i] == 0x0d && encoded[i+1] == 0x0a) {
            baos.write(0x0a);
            i++;
        } else {
            baos.write(encoded[i]);
        }
    }
    try {
        baos.close();
    } catch (IOException ioe) {

    }

    return baos.toByteArray();      
}

EDIT: меня осенило, почему он это делает. Он преобразует LF в CR / LF, как в старой школе DOS. Интересно, есть ли где-нибудь настройка чтобы отключить это?

лучшее решение-использовать как @AjeetKhadke предложил.

позвольте мне проиллюстрировать разницу между adb shell и adb exec-out выход:

~$ adb shell "echo -n '\x0a'" | xxd -g1
00000000: 0d 0a

~$ adb exec-out "echo -n '\x0a'" | xxd -g1
00000000: 0a

он работает в Windows (я использую hexdump С GNUWin32 Hextools для демо), а также:

C:\>adb shell "echo -n '\x0a'" | hexdump
00000000: 0D 0A

C:\>adb exec-out "echo -n '\x0a'" | hexdump
00000000: 0A

недостатком является то, что для того, чтобы иметь возможность извлечь выгоду из использования adb exec-out команда как устройство, так и хост-ПК должны поддерживать adb shell V2 протокол.

это довольно тривиально, чтобы заботиться о компьютере - просто обновите platform-tools пакет (который содержит adb binary) до последней версии. Версия adbd демон на устройстве связан с версией Android. Элемент adb shell протокол V2 был введен в Android 5.0 вместе с complete adb капитальный ремонт (идя от c до C++ код). Но были некоторые регрессии (ака ошибки) так adb exec-out полезность в Android 5.х был еще ограниченный. И, наконец, нет поддержки для Android 4.x и более старые устройства. К счастью, доля тех старых устройств, которые все еще используются для разработки, быстро падает.

да, в Unix / Linux / Mac OS X вы можете получить двоичный вывод оболочки adb, добавив "stty-onlcr;" к вашей команде (нет ~~ нужно быть корневым android).

1.скачать исполняемый файл " stty.
http://www.busybox.net/downloads/binaries/latest/
Для старых android, используйте busybox-armv5l, другие используют busybox-armv7l.
переименуйте файл в "stty"

2.Uploda файл "stty" для android и установить соответствующие разрешения.

adb push somelocaldir/stty /data/local/tmp/   
adb shell chmod 777 /data/local/tmp/stty 

3.вставить "будут действовать до его закрытия -onlcr;" на вашу команду, как это:

adb shell /data/local/tmp/stty -onlcr\; screencap -p  > somelocaldir/output.png
or:
adb shell "/data/local/tmp/stty -onlcr; screencap -p"  > somelocaldir/output.png
or (Only for Windows):
adb shell /data/local/tmp/stty -onlcr; screencap -p  > somelocaldir/output.png

готово!

но для ОС Windows, по умолчанию, LF из android будет преобразован в CR CR LF.
Даже если вы сделали выше шаг, вы все равно получите CR LF.
Это "кажется", потому что местный АБР.exe использовать fwrite, которые вызывают CR быть добавлены.
У меня нет никакого способа об этом, кроме преобразования CR LF в LF вручную на ОС Windows.

иначе:

adb shell "busybox stty raw; screencap -p "> foo3.png 

но, как сказал @osexp2003,это не работает для ОС Windows.

вот решение, которое работает везде (Linux и Windows включены).

вам понадобится netcat утилиты, часто называют nc.
Если оба nc и busybox nc сбой на вашем устройстве, вам нужен свежий busybox. Вы можете либо использовать установщик busybox из Play Market (Требуется root), либо использовать решение от osexp2003 (скачать busybox из официальный сайт, положил его в /data/local/tmp/ на устройстве и добавить выполнить разрешение.)

идея в том, чтобы использовать netcat как примитивный HTTP-сервер.
Ну, даже не правильный сервер на самом деле. Он просто отправит свой вход в качестве ответа на любой TCP-соединение (будь то HTTP-запрос из браузера, соединение telnet или просто netcat) и прекратить.

выполнить команду, которую вы хотите получить на выходе следующим образом:

adb shell 'screencap -p | busybox nc -p 8080 -l >/dev/null'

в приведенном выше примере screencap -p делает снимок экрана (PNG изображение) и трубы его в netcat.
-l говорит netcat выступать в качестве сервера (прослушивать соединение), и -p 8080 говорит ему использовать TCP-порт 8080. Omiting >/dev/null будет просто печатать, например, входящий запрос HTTP GET на ваш терминал.
Приведенный выше пример будет ждать, пока кто-то подключится, отправит скриншот и только затем завершит работу.
Конечно, вы можете запустить его без adb shell, например, от эмулятора терминала на вашем устройстве.

после выполнения команды выше, вы можете скачать его выхода из ваш телефон, открыв http://ip.of.your.phone:8080 в браузере или любым другим способом, например с помощью netcat:

busybox nc ip.of.your.phone:8080 >screenshot.png

если вы хотите использовать USB-кабель для загрузки, вам нужно перенаправить соединение с помощью ADB следующим образом:

adb forward tcp:7080 tcp:8080

после этого вы можете использовать localhost:7080 вместо ip.of.your.phone:8080.
Вы можете удалить эту переадресацию с помощью следующей команды:

adb forward --remove tcp:7080

то что доктор прописал:

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

для этого также можно использовать base64, поэтому просто Закодируйте его с помощью:

base64 foo3.png>foo3.png.base64

а затем на windows, используя некоторые утилита base64 или, может быть, Notepad++ для расшифровки файла.

или в linux / cygwin:

base64 -d foo3.png.base64>foo3.png

вы также можете использовать стандартный dos2unix команда, если она доступна.

(apt-get install dos2unix Если вы находитесь на Debian / Ubuntu. Вероятно, есть сборки для Windows, OS X и т. д. там где-то, если вы google).

dos2unix преобразует CRLF в LF так же, как Эрик Ланге

Я поставил метод для использования python get image bytes с помощью adb здесь, возможно, это будет полезно для тех, кто столкнулся с этой проблемой. Код выглядит следующим образом:

 pipe = subprocess.Popen("adb shell screencap -p",
                      stdin=subprocess.PIPE,
                      stdout=subprocess.PIPE, shell=True)
 image_bytes = pipe.stdout.read().replace(b'\r\n', b'\n')
 gray_image = cv2.imdecode(np.fromstring(image_bytes, np.uint8), cv2.IMREAD_GRAYSCALE)

эта команда работала для меня ОС Windows:

adb exec-out screencap -p > test.png && dos2unix.exe -f test.png

но вы хотите использовать этот: https://sourceforge.net/projects/dos2unix/

nc Это был единственный способ, которым он работал для меня. Используется:

adb forward tcp:7080 tcp:8080 &&\    
adb shell 'tar -zcvf - /data/media | nc -p 8080 -l 1>/dev/null' &\    
sleep 1;\    
nc localhost 7080 > media.tar.gz &&\    
adb forward --remove tcp:7080

root-правами чтобы создать, надеюсь, правильное резервное копирование /данные/средства массовой информации

Это лучший способ использования оболочки в ОС

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png