是否可以从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)字节。回车?这会敲响任何铃声吗?是否在一些空白行中混合?
答案 0 :(得分:81)
与adb exec-out
不同,pty
命令不会使用adb exec-out screencap -p > test.png
来破坏二进制输出。所以你可以做到
/dev/null
请注意,如果您将此技术用于在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
。
如果您的设备上nc
和busybox 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();