从adb shell读取二进制stdout数据?

时间:2012-11-27 06:16:53

标签: android adb

是否可以从adb shell命令读取二进制stdout?例如,如何使用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等的内容)并且大小相同,但文件已损坏,因为图像读者关注。

我还试图在java中使用ddmlib来做到这一点,结果是一样的。我很乐意使用任何必要的图书馆。我的目标是减少获取捕获的总时间。在我的设备上,使用双命令解决方案,获取图像大约需要3秒钟。使用ddmlib并捕获stdout需要不到900毫秒,但它不起作用!

是否可以这样做?

编辑:这是两个文件的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  |.............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)字节。回车?这会敲响任何铃声吗?是否在一些空白行中混合?

16 个答案:

答案 0 :(得分:81)

adb exec-out不同,pty命令不会使用adb exec-out screencap -p > test.png 来破坏二进制输出。所以你可以做到

/dev/null

documentation of momentJS

请注意,如果您将此技术用于在STDERR上生成输出的命令,则应将其重定向到adb,否则adb exec-out "tar -zcf - /system 2>/dev/null" > system.tar.gz 将在其STDOUT中包含STDERR,从而破坏您的输出。例如,如果您尝试备份和压缩目录:

{{1}}

答案 1 :(得分:45)

很抱歉发布一个旧问题的答案,但我自己遇到了这个问题,并且只想通过shell来实现。这对我很有用:

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

^M是按ctrl + v得到的字符 - > ctrl + m,只是注意到它在复制粘贴时不起作用。

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

也为我做了伎俩。

答案 2 :(得分:12)

如上所述,“adb shell”正在执行换行(0x0a)到回车+换行(0x0d 0x0a)转换。这是由伪tty线路规则执行的。由于shell没有“stty”命令,因此没有简单的方法来处理终端设置。

可能用ddmlib做你想做的事。您需要编写在设备上执行命令的代码,捕获输出并通过线路发送。这或多或少是DDMS对某些功能的作用。这可能比它的价值更麻烦。

repair()解决方案 - 将所有CRLF转换为LF - 感觉不稳定但实际上是可靠的,因为“腐败”的LF到CRLF转换是确定性的。我曾经做同样的事情来修复无意的ASCII模式FTP传输。

值得注意的是PNG文件格式是明确设计用于捕获这个(和相关的)问题。神奇的数字从0x89开始,以捕获任何剥离高位的内容,然后是“PNG”,这样您就可以轻松地告诉文件中的内容,然后使用CR LF来捕获各种ASCII行转换器,然后使用0x1a来捕获旧的MS-DOS程序使用Ctrl-Z作为特殊的文件结束标记,然后使用单独的LF。通过查看文件的前几个字节,您可以确切地知道对它做了什么。

...这意味着您的repair()函数可以同时接受“损坏”和“纯”输入,并可靠地确定它是否需要执行任何操作。

编辑:另外一个注意事项:设备端二进制文件可以使用cfmakeraw()配置tty以避免转换。请参阅Android 5.0中screenrecord命令中的prepareRawOutput()函数,该函数可以通过ADB shell连接从实时屏幕捕获中发送原始视频。

答案 3 :(得分:9)

最佳解决方案是使用adb exec-out建议的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 输出之间的区别:

hexdump

它适用于Windows(我在@AjeetKhadke使用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命令,设备和主机PC都必须支持platform-tools V2协议。

照顾PC端是相当简单的 - 只需将adb包(包含adbd二进制文件)更新到最新版本。设备上的adb shell守护程序版本链接到Android版本。 Android 5.0中引入了adb V2协议以及完整的c大修(从C++adb exec-out代码)。但是有一些回归(又称错误),因此Android 5.x中的{{1}}有用性仍然有限。最后,不支持Android 4.x和旧设备。幸运的是,那些仍在用于开发的旧设备的份额正在快速下降。

答案 4 :(得分:8)

深入挖掘十六进制转储后,很明显每次发出字符0x0A时,shell都会发出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();      
}
编辑:我明白为什么要这样做。它将LF转换为CR / LF,就像老式DOS一样。我想知道是否有某个地方可以关闭它?

答案 5 :(得分:5)

是的,在Unix / Linux / Mac OS X上,您可以通过前缀“stty -onlcr;”接收adb shell的二进制输出。你的命令( ~~需要是一个有根的机器人)。

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. Prepend“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操作系统,默认情况下,android中的LF将转换为CR CR LF 即使你做了上一步,你仍然得到CR LF 这似乎“似乎”因为本地adb.exe使用fwrite导致CR被添加 除了在Windows操作系统上手动将CR LF转换为LF之外,我没办法解决这个问题。

答案 6 :(得分:2)

以下是无处不在的解决方案(包括Linux和Windows)。

您需要netcat实用程序,通常名为nc
如果您的设备上ncbusybox nc都失败,则需要新的busybox。您可以使用Play Market中的busybox安装程序(需要root),或使用solution by osexp2003(从official site下载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

答案 7 :(得分:1)

也可以使用base64,所以只需使用:

对其进行编码
base64 foo3.png>foo3.png.base64

然后在Windows上使用一些base64 utility或者记事本++来解密文件。

或者在linux / cygwin中:

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

答案 8 :(得分:1)

试试这个家伙:

<?xml version="1.0" encoding="utf-8"?>

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/spinner_item_textview"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:maxLines="1"
    android:ellipsize="end"
    android:paddingStart="10dp"
    android:paddingEnd="10dp"
    android:textSize="18sp"
    android:textColor="@color/primaryTextColor" />

答案 9 :(得分:0)

如果可用,您也可以使用标准dos2unix命令。

apt-get install dos2unix如果您使用的是Debian / Ubuntu。如果您使用google,可能会在Windows,OS X等地方进行构建。

dos2unix将CRLF转换为LF,与Eric Lange的repair()函数相同。

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

或修复损坏的文件(就地):

dos2unix -f screenshot.png

您需要-f强制它处理二进制文件。

答案 10 :(得分:0)

我把这个方法用于使用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)

答案 11 :(得分:0)

这是个老问题,但这个解决方案可能对某些人有用。

只需从 adb 下载任何文件,您可以使用:

adb run-as your.package.name base64 -w 0 /path/to/your/file.db

然后在linux中读取和解码base64字符串(例如):

cat saved.base64.str|base64 -d

当然,您可以先压缩源数据,然后再将其编码为 base64。

答案 12 :(得分:-1)

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创建一个有希望的正确备份

答案 13 :(得分:-1)

这是在OS中使用Shell的最佳方式

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

答案 14 :(得分:-1)

我的bash脚本(默认为exec-out,因为它在两种设备上都能正常运行):

https://gist.github.com/mj41/14767642e02dda7ce32ed26ce012fe12

答案 15 :(得分:-2)

此命令适用于 Windows操作系统

Toast.makeText(getApplicationContext(), item.text + " is clicked", Toast.LENGTH_SHORT).show(); 

但你想用这个: https://sourceforge.net/projects/dos2unix/