68

是否可以从 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 毫秒,但它不起作用!

是否有可能做到这一点?

编辑:这是两个文件的 hexdump。第一个,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) 字节。回车??这有什么铃声吗?它是否混合了一些空白行?

4

18 回答 18

96

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

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
于 2015-07-14T08:22:48.183 回答
50

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

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

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

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

也为我做了伎俩。

于 2013-01-16T07:30:57.347 回答
15

最好的解决方案是使用@AjeetKhadke建议的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

它也适用于 Windows(我使用hexdumpGNUWin32 Hextools进行演示):

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 shellV2 协议。

处理 PC 端相当简单——只需将platform-tools包(包含adb二进制文件)更新到最新版本。设备上的adbddaemon 版本与 Android 版本相关联。adb shellV2 协议已在 Android 5.0 中引入,并进行了全面检修adb(从代码cC++代码)。但是有一些回归(也就是错误),所以adb exec-out在 Android 5.x 中的用处仍然有限。最后,不支持 Android 4.x 和更早的设备。幸运的是,仍在用于开发的旧设备的份额正在迅速下降。

于 2012-11-27T15:13:00.867 回答
15

如前所述,“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()函数可以接受“损坏”和“纯”输入,并可靠地确定它是否需要做任何事情。

编辑:一个附加说明:设备端二进制文件可以配置 tty 以避免转换,使用cfmakeraw(). 请参阅Android 5.0 中screenrecordprepareRawOutput()命令中的功能,该功能可以通过 ADB shell 连接从实时屏幕截图发送原始视频。

于 2012-11-27T22:00:10.073 回答
9

在深入挖掘十六进制转储后,很明显每次发出字符 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。我想知道是否有某个地方可以关闭它?

于 2012-11-27T21:24:00.270 回答
7

是的,在 Unix/Linux/Mac OS X 上,您可以通过添加“stty -onlcr;”来接收 adb shell 的二进制输出。到你的命令( ~~需要是一个有根的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 之外,我对此没有任何办法。

于 2013-11-22T09:37:06.253 回答
4

这是适用于任何地方的解决方案(包括 Linux 和 Windows)。

您将需要netcat实用程序,通常命名为nc.
如果两者都在您的设备ncbusybox nc失败,您需要新busybox的 . 您可以使用 Play Market 的busybox安装程序(需要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
于 2015-12-11T04:24:25.447 回答
3

其他方式:

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

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

于 2015-09-11T07:19:33.037 回答
1

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

base64 foo3.png>foo3.png.base64

然后在windows上使用一些base64实用程序或者notepad++来解密文件。

或者在 linux/cygwin 中:

base64 -d foo3.png.base64>foo3.png
于 2015-08-30T15:00:49.433 回答
1

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

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

dos2unixrepair()以与 Eric Lange 的函数相同的方式将 CRLF 转换为 LF 。

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

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

dos2unix -f screenshot.png

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

于 2016-11-15T07:13:56.867 回答
1

试试这个家伙:

adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > screen.png
于 2017-10-24T08:16:56.153 回答
1

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

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

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

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

cat saved.base64.str|base64 -d

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

于 2021-05-02T16:49:40.247 回答
0

我把使用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)
于 2018-01-15T06:20:09.137 回答
0

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

您可以为此使用scrcpy。它适用于 Linux 和 Windows!赶紧跑:

scrcpy

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

于 2021-10-04T17:32:34.930 回答
0

adb exec-out正如Ajeet47Alex P. 指出的 那样,一般要走的路是使用。

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

我想知道为什么即使我使用的是 .tar 文件,我的 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                                       .

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

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

一种方法是在写出原始数据时对原始数据进行校验和,并对主机上接收到的数据进行校验和。使用与 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's 的命令行替换功能(例如cmd | tee >(sha512sum -b - > ...)),因为这会遗憾地删除尾随换行符并因此修改原始数据。

于 2021-11-15T18:30:59.477 回答
-1

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

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

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

于 2017-07-08T14:17:52.487 回答
-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 创建一个有希望的正确备份

于 2017-07-20T18:51:16.863 回答
-1

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

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

于 2017-12-27T08:31:28.340 回答