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

Можно ли читать двоичный файл stdout из команды adb shell? Например, все примеры использования 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 shell screencap -p > foo3.png 

И избегайте промежуточной записи на SD-карту. Это генерирует что-то похожее на PNG-файл (запуск strings foo3.png генерирует что-то с помощью IHDR, IEND и т. Д.) И примерно такого же размера, но файл поврежден в отношении читателей изображений.

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

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

EDIT: Вот hexdump из двух файлов. Первый, screen.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 |.............ne| 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.~gq....| 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) байтов. Возврат каретки?? Это кольцо звонит? Смешивается ли он в несколько пустых строк?

Solutions Collecting From Web of "Чтение двоичных данных stdout из оболочки adb?"

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

 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 команда adb exec-out не использует pty которая управляет двоичным выходом. Таким образом, вы можете сделать

 adb exec-out screencap -p > test.png 

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

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

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

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

Стоит отметить, что формат PNG-файла явно разработан для того, чтобы точно распознавать эти (и связанные) проблемы. Магическое число начинается с 0x89, чтобы поймать все, что разделяет высокие биты, а затем «PNG», чтобы вы могли легко узнать, что находится в файле, а затем CR LF, чтобы поймать различные ASCII-линейные преобразователи, а затем 0x1a, чтобы уловить старые программы MS-DOS, которые Использовал Ctrl-Z как специальный маркер конца файла, а затем одиночный LF. Рассматривая первые несколько байтов файла, вы можете точно сказать, что с ним сделано.

… что означает, что функция repair() может принимать как «поврежденный», так и «чистый» вход, и надежно определять, нужно ли что-либо делать.

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

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

 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 старой школы. Интересно, есть ли где-нибудь где-то, чтобы отключить это?

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

1. Загрузите исполняемый файл «stty».
http://www.busybox.net/downloads/binaries/latest/
Для старого андроида используйте 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. Подготовить «stty -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.
Это «кажется», потому что локальный adb.exe использует fwrite, который заставляет CR быть добавленным.
У меня нет никакого способа об этом, кроме как преобразовать CR LF в LF вручную в ОС Windows.

Начиная с версии 6.0 для Android вы можете использовать апплет toybox base64 :

  adb shell "screencap -p | toybox base64" | base64 -di >screencap.png 

Но лучшим решением, если доступно, является использование команды adb exec-out например, @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: 0D 

Другой путь:

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

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

Для этого также возможно использовать base64, поэтому просто зашифруйте его, используя:

 base64 foo3.png>foo3.png.base64 

А затем в окнах с помощью некоторой утилиты base64 или, возможно, notepad ++, чтобы расшифровать файл.

Или в linux / cygwin:

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

Вот решение, которое работает повсюду (включая Linux и Windows).

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

Идея заключается в использовании 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 

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

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

dos2unix преобразует CRLF в LF так же, как функция repair() Эрика Ланге.

 adb shell screencap -p | dos2unix -f > screenshot.png 

Или, исправить поврежденный файл (на месте):

 dos2unix -f screenshot.png 

Вам нужно -f заставить его обрабатывать двоичные файлы.

Эта команда работала для меня в ОС 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 для создания надежной резервной копии для / data / media