Чтение двоичных данных 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: Вот шестнадцатеричный файл из двух файлов. Первый, 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  |.,......#*[email protected]|
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"[email protected]|
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) байтов. Возврат каретки?? Это звонит какие-то колокольчики? Смешивается ли он в несколько пустых строк?

Ответ 1

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

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

То, что ^M является char, я получил нажатием ctrl + v → ctrl + m, только заметил, что он не работает при копировании.

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

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

Ответ 3

Как уже отмечалось, "adb shell" выполняет преобразование линии (0x0a) в преобразование return-return + linefeed (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, которая может отправлять необработанное видео из захвата в реальном времени через соединение оболочки АБР.

Ответ 4

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

Ответ 5

С Android версии 6.0 вы можете использовать toybox base64 applet:

 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: 0A

Ответ 6

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

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

2. Уплотнить файл "stty" на андроид и установить правильное разрешение.

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.

Ответ 7

Другой способ:

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

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

Ответ 8

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

base64 foo3.png>foo3.png.base64

а затем в окнах, используя некоторую утилиту base64 или, возможно, notepad ++ для дешифрования файла.

Или в linux/cygwin:

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

Ответ 9

Вот решение, которое работает везде (включая 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. Опускание >/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

Ответ 10

попробуйте этих парней:

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

Ответ 11

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

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

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

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

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

dos2unix -f screenshot.png

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

Ответ 12

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

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

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

Ответ 13

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

Ответ 14

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

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