Чтение двоичных данных 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
сделал трюк и для меня.
Ответ 2
В отличие от adb shell
команда adb exec-out
не использует pty
, которая управляет двоичным выходом. Таким образом, вы можете сделать
adb exec-out screencap -p > test.png
https://android.googlesource.com/platform/system/core/+/5d9d434efadf1c535c7fea634d5306e18c68ef1f
Ответ 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