【问题标题】:Read binary stdout data like screencap data from adb shell?从 adb shell 读取二进制标准输出数据?
【发布时间】:2012-11-14 17:32:09
【问题描述】:

是否可以从 adb shell 命令读取二进制标准输出?例如,如何使用屏幕截图的所有示例都包括两个步骤:

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

但是,该服务支持写入标准输出。例如,您可以执行以下操作:

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 等的内容)并且大小大致相同,但就图像阅读器而言,文件已损坏担心。

我也尝试在 java 中使用 ddmlib 来执行此操作,结果是相同的。我很乐意使用任何必要的库。我的目标是减少获得捕获的总时间。在我的设备上,使用双命令解决方案,获取图像大约需要 3 秒。使用 ddmlib 并捕获 stdout 耗时不到 900 毫秒,但它不起作用!

可以这样做吗?

编辑:这是两个文件的十六进制转储。第一个,screen.png 来自标准输出并且已损坏。第二个, 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) 字节。回车??这有什么铃声吗?是否混入了一些空白行?

【问题讨论】:

  • 我猜你正在将程序的标准输出与命令的标准输出结合起来。我没有准备好自己尝试这个,但你有没有详细看过它创建的文件?如果您在文本编辑器中打开它,您可能会看到一个可以删除的错误字符串。至少你可以将它与正确的版本进行比较,看看有什么区别。
  • 更新了我的问题以添加两个文件的 hexdumps。我没有看到任何奇怪的字符串混入其中,但随机字节似乎确实如此。
  • 如果你在那里得到了几个额外的cr,我一点也不感到惊讶。您可能只运行“adb shell screencap -p”并查看输出是什么。请记住,您不是将 screencap -p 的输出传递给 adb,而是传递 adb shell 的输出。
  • 我用十六进制编辑器刮掉了两个明显的任性crs,并试图再次查看图像。这一次,它被预览识别为PNG(之前不是),但图像是空白的。 Imagemagick 仍然声称它已损坏,因此那里可能有更多垃圾。叹息。
  • 像这样:adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png

标签: android adb


【解决方案1】:

最好的解决方案是使用adb exec-out 命令,如建议的@AjeetKhadke

让我说明adb shelladb exec-out输出的区别:

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

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

它也适用于 Windows(我使用来自 GNUWin32 Hextoolshexdump 进行演示):

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

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

缺点是为了能够从使用adb exec-out 命令中受益,设备和主机PC 都必须支持adb shell V2 协议。

PC 端的维护相当简单——只需将platform-tools 包(其中包含adb 二进制文件)更新到最新版本即可。设备上@9​​87654333@ daemon 的版本与Android 版本相关联。 adb shell V2 协议已在 Android 5.0 中引入,同时对 adb 进行了完整的大修(从 cC++ 代码)。但是有一些回归(也就是错误),所以adb exec-out 在 Android 5.x 中的用处仍然有限。最后,不支持 Android 4.x 和更早的设备。幸运的是,仍在用于开发的旧设备的份额正在迅速下降。

【讨论】:

  • 谢谢。但我应该补充一点,保持电话无根是一项要求,所以我不能使用busybox。但是,它确实给了我一些想法来尝试不同的实用程序。
  • 大多数 Android 版本都有一个非官方的地方,可以放置一个小的二进制文件,例如编码工具,但它可以放在哪里取决于版本。
  • 我的 Android 4.3 设备没有busybox,也没有uudecode。
  • 这些测试对我不起作用。我得到的是 '\x0a' 字符而不是二进制 LF。为了获得正确的 LF,我需要在 echo 命令中添加 -e: adb shell "echo -e -n '\x0a' etc...
  • 这在 Windows 10 上没有用。我都得到 0d 0a !哦,好吧,答案是从 2012 年开始的……
【解决方案2】:

深入挖掘十六进制转储后,很明显每次发出字符 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();      
}

编辑:我明白为什么要这样做。它像老式 DOS 一样将 LF 转换为 CR/LF。我想知道是否有某个地方可以关闭它?

【讨论】:

  • 可以在运行Glenn Willen 所述的shell 命令之前使用stty raw 禁用它。
  • 我发现我的流正在从 0x0a => 0x0d 0x0d 0x0a 转换。这是对我有用的代码:if ((i + 2) &lt; encoded.length &amp;&amp; encoded[i] == 0x0d &amp;&amp; encoded[i+1] == 0x0d &amp;&amp; encoded[i+2] == 0x0a) { ...
【解决方案3】:

如前所述,“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 连接从实时屏幕截图发送原始视频。

【讨论】:

  • 是的,repair() 解决方案是可靠的,因为它具有确定性。我对其进行了一些优化,以减少对输出流的写入调用次数,现在处理时间没有明显增加。效果很好。谢谢!
  • Re:“‘破坏’的 LF 到 CRLF 的转换是确定性的”:但它是可逆的吗?如果 CRLF 和 LF 都转换为 CRLF,那么将 CRLF 转换回 LF 不是 100% 可靠,因为它会混淆原始 CRLF 正确的情况。
  • 在这种情况下,CRLF 变为 CRCRLF。如果 CRLF 在未经修改的情况下通过,那么它将是不可逆的。
  • 很高兴看到这里解释了该 sed 命令的原因。惊讶地发现人们并没有吵着要知道为什么首先需要 sed。
【解决方案4】:

很抱歉发布一个老问题的答案,但我自己也遇到了这个问题,只想通过 shell 来解决。这对我来说效果很好:

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

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

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

我也成功了。

【讨论】:

  • 虽然上述方法在 Ubuntu 上效果很好,但在 Mac OS X 上却不适合我。这种使用 Perl 代替 sed 的变体:adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' &gt; screen.png
  • 任何人都知道 windows 的正确 perl 表达式应该是什么 - 我有一个团队成员不幸在 windows 平台上。
  • 在 Windows 上,我不得不使用 sed 's/\r$//' &gt; screenshot.png 两次。知道这是为什么吗?
  • @kanna 可能是因为 ^ 字符必须在 windows shell 中转义。
  • 对于非常非常大的文件(例如转储闪存),这将不起作用(sed: regex input buffer length larger than INT_MAX)。
【解决方案5】:

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

1.下载“stty”可执行文件。
http://www.busybox.net/downloads/binaries/latest/
对于旧的 android,使用busybox-armv5l,其他使用busybox-armv7l。
将文件重命名为“stty”

2.上传文件“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 操作系统,默认情况下,来自 android 的 LF 会被转换为 CR CR LF。
即使你做了上述步骤,你仍然会得到 CR LF。
这“似乎”是因为本地 adb.exe 使用 fwrite 导致 CR 被前置。
除了在 Windows 操作系统上手动将 CR LF 转换为 LF 之外,我对此没有任何办法。

【讨论】:

【解决方案6】:

adb shell 不同,adb exec-out 命令不使用会破坏二进制输出的pty。所以你可以做

adb exec-out screencap -p > test.png

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

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

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

【讨论】:

  • 运行此程序时为什么会出现“错误:关闭”的任何想法?我有 ADB 1.0.32 版
  • @Kristopher,1.0.32 是古老的。请使用更新的版本
  • 如果现在有人遇到同样的问题,这似乎是一个非常好的主意。当然,这是一年零六天前,当时 1.0.32 是最前沿的伙伴。真正的答案是我在 Windows 上做我的开发,即使 exec-out 应该忽略行尾问题,我仍然得到它。不过,答案现在对我来说很明显:只需使用 frickin' Linux 开发 Android 并停止尝试在 Windows 上伪造它。
  • @Ralph, shell v2 需要同时支持 PC 主机端的 adb 和设备端的 adbd。你的设备太旧了。
  • 为了让这更有趣,在 Windows 上,这在从 cmd 运行 adb 时完美运行,但会破坏 powershell 上的 PNG。 CRLF 到 LF 在 powershell 上也不起作用(使用 adb shell)。我没有试图弄清楚它来自哪里的额外噪音,因为你可以切换到 cmd。
【解决方案7】:

也可以为此使用 base64,因此只需使用以下代码对其进行编码:

base64 foo3.png>foo3.png.base64

然后在 Windows 上使用一些 base64 utility 或者可能是 notepad++ 来解密文件。

或者在linux/cygwin中:

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

【讨论】:

  • 我不需要screencap,但我确实需要tar。以下是我将所有图片复制到笔记本电脑的操作:time adb shell 'tar -C /sdcard/DCIM -cf - Camera | toybox base64' | base64 -D | tar -xf - 感谢 base64 提示!
【解决方案8】:

另一种方式:

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

但是,正如@osexp2003 所说,这不适用于 Windows 操作系统。

【讨论】:

    【解决方案9】:

    这里是适用于任何地方(包括 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 充当服务器(监听连接),@987654336 @ 告诉它使用 TCP 端口 8080。 省略 &gt;/dev/null 将简单地打印例如向您的终端传入 HTTP GET 请求。
    上面的示例将等待某人连接,发送屏幕截图,然后才终止。
    当然你可以不使用adb shell 运行它,例如来自您设备上的终端模拟器。

    如上运行命令后,您可以从手机下载其输出,方法是在浏览器中打开 http://ip.of.your.phone:8080 或通过任何其他方式,例如使用 @ 987654340@:

    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
    

    【讨论】:

    • 我在我的nexus 5x 上没有找到任何原版nc,但是有一个toybox 二进制文件编译了nc,所以我可以将它用作toybox nc ...。非常感谢这个提示。
    【解决方案10】:

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

    apt-get install dos2unix 如果你使用的是 Debian/Ubuntu。如果你用谷歌搜索的话,可能有适用于 Windows、OS X 等的构建版本。

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

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

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

    dos2unix -f screenshot.png
    

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

    【讨论】:

    • 这适用于默认情况下既没有sed 也没有perl 的Windows
    【解决方案11】:

    这个命令在 Windows 操作系统上对我有用:

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

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

    【讨论】:

    • adb exec-out 在 Windows 中工作得很好,不需要dos2unix。所以你的答案是多余的
    • 是的,但奇怪的是 exec-out 在我的电脑上不起作用。
    • 从最新的platform-tools 版本中尝试adb。如果它不起作用 - 这意味着您的手机不正确支持该命令。
    • 它对我有用,但只有当我使用 dos2unix 两次时,因为我没有 exec-out。 adb shell screencap -p | dos2unix -f | dos2unix -f &gt; Screencap.png 谢谢dos2unix@
    【解决方案12】:

    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】:

      试试这个家伙:

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

      【讨论】:

        【解决方案14】:

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

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

        【讨论】:

          【解决方案15】:

          我把使用python get image bytes using 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)
          

          【讨论】:

            【解决方案16】:

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

            对于简单地从 adb 下载任何文件,您可以使用:

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

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

            cat saved.base64.str|base64 -d

            当然,您可以在将源数据编码为 base64 之前对其进行压缩。

            【讨论】:

              【解决方案17】:

              我想为这些应用添加另一个解决方案,防止 adb 截屏

              您可以为此使用scrcpy。它适用于 Linux 和 Windows!运行:

              scrcpy
              

              将打开一个带有您设备屏幕的窗口。您现在可以使用您的本地截图工具(例如 windows 上的截图工具和 linux 上的截图)来截取当前屏幕的截图!

              【讨论】:

              【解决方案18】:

              一般的做法是使用adb exec-out,如Ajeet47Alex P.pointedout

              但是,如果中间有任何东西会创建一个伪 tty,事情就会变得一团糟。

              我想知道为什么我的 tar 文件会损坏,即使我使用的是 adb exec-out。就我而言,问题是使用su 二进制文件生成tar 进程。 su 似乎产生了一个伪 tty,你无法将其关闭,这给了你两全其美的感觉。

              例子:

              % adb exec-out 'su -c '"'"'printf "\n"'"'" | xxd
              00000000: 0d0a                                     ..
              

              在这种情况下,您仍然需要通过stty raw 之类的方式告诉外壳程序不要使用额外的回车符来破坏换行符:

              % adb exec-out 'su -c '"'"'stty raw; printf "\n"'"'" | xxd                                                                  
              00000000: 0a                                       .
              

              但是,正如其他人指出的那样,这可能很脆弱,尤其是对于二进制数据。在将exec-outstty raw 配对时,我在接收tar 输出超过adb 时没有发现任何问题,但肯定可能存在问题。

              因此,如果您真的想要逐位相等,请检查生成的数据是否与您希望捕捉到的令人惊讶的情况相匹配。这不会修复它们,但至少会提醒您注意它们。

              执行此操作的一种方法是在将原始数据写出时对原始数据进行校验和,并对主机上接收到的数据进行校验和。使用 POSIX 兼容的系统和 shell 执行此操作的一种相当复杂但必要的方法如下:

              % adb exec-out 'su -c '"'"'stty raw && printf "\n" 2>/data/local/tmp/printf.stderr | { tee /dev/fd/3 | sha512sum -b - > /data/local/tmp/printf.sha512sum; } 3>&1'"'" | sha512sum -b - 
              be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09 *-
              % adb exec-out 'cat /data/local/tmp/printf.sha512sum'
              be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09
              

              不要尝试使用bash 的命令行替换功能(例如cmd | tee &gt;(sha512sum -b - &gt; ...)),因为这样会遗憾地删除尾随换行符,从而修改原始数据。

              【讨论】:

                猜你喜欢
                • 2011-02-20
                • 2010-12-08
                • 1970-01-01
                • 2010-12-01
                • 1970-01-01
                • 2015-11-23
                • 1970-01-01
                • 1970-01-01
                • 2015-10-07
                相关资源
                最近更新 更多