xargs 没有生成正确的命令

m0s*_*it0 7 zsh xargs

我想删除 Android 设备上的许多应用程序,它们都以相同的程序包开头。我使用以下命令获得这些:

$ adb shell ls /data/data | grep -i com.company
com.company.android.app.adwidget
com.company.android.app.attendancereports
com.company.android.app.atteventmanagement
com.company.android.app.buttonwidget
com.company.android.app.clockwidget
Run Code Online (Sandbox Code Playgroud)

现在我想对adb uninstall这些包名中的每一个执行,我想到了使用xargs

$ adb shell ls /data/data | grep -i com.company | xargs -n1 echo adb uninstall
adb uninstall com.company.android.app.adwidget
adb uninstall com.company.android.app.attendancereports
adb uninstall com.company.android.app.atteventmanagement
adb uninstall com.company.android.app.buttonwidget
adb uninstall com.company.android.app.clockwidget
Run Code Online (Sandbox Code Playgroud)

看起来它会起作用,所以我删除了echo

$ adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall
Failure
Failure
Failure
Failure
Failure
Run Code Online (Sandbox Code Playgroud)

但是,独立运行每个命令会产生Success

$ adb uninstall com.company.android.app.adwidget
Success
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Sté*_*las 8

虽然问题最终是adb shell输出中的 CR 字符引起的(由在目标 Android 系统上创建的 pty 的 tty 行规则插入(有关更多详细信息,请参见此处)),另一种可能的解释(我将其留在那里)对于未来的读者,因为这是 ) 的一个常见问题,xargs可能是:

adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall
Run Code Online (Sandbox Code Playgroud)

根据xargs实现,adb的标准输入将是/dev/null或来自 的管道grep。在任何情况下,它都不会是 tty,adb如果它希望能够与用户交互,这可能是失败的原因。

使用 GNUxargs和支持进程替换的 shell(如zsh),您可以将其更改为:

xargs -n1 -ra <(adb shell ls /data/data | grep -i com.company) adb uninstall
Run Code Online (Sandbox Code Playgroud)

在这种情况下xargs,从作为参数给出的文件中读取列表-a,让您无需理会 stdin。

或者既然你提到了zsh,你可以使用:

autoload zargs # best in ~/.zshrc
zargs -L1 $(adb shell ls /data/data | grep -i com.company) -- adb uninstall
Run Code Online (Sandbox Code Playgroud)

(使用-L而不是-n作为zargs-n限制数目的参数到adb(包括uninstall一个),我们将需要该装置-n 2)。

或者简单地使用一个循环,在这种情况下它会更短更清晰:

for x ($(adb shell ls /data/data | grep -i com.company)) adb uninstall $x
Run Code Online (Sandbox Code Playgroud)


m0s*_*it0 4

将输出重定向adb shell ls /data/data | grep -i com.company到文件并使用十六进制编辑器检查它,我发现它们附加了 Windows 风格的回车符\\r\\n (0x0D 0x0A)。所以摆脱\\rwithtr -d \'\\r\'解决了问题。

\n\n

整个命令使用for(来自St\xc3\xa9phane Chazelas\' 的回答):

\n\n
for x in $(adb shell ls /data/data | grep -i com.company | tr -d \'\\r\'); do adb uninstall $x;  done\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者类似地使用xargs

\n\n
adb shell ls /data/data | grep -i com.company | tr -d \'\\r\' | xargs -r -n1 adb uninstall\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

另一种选择(正如 St\xc3\xa9phane Chazelas 在下面的评论中所友好解释的那样)是\\r完全禁用stty -opost,尽管这很可能需要busybox(或类似的替代方案toybox在 Android 设备上安装

\n\n
$ adb shell echo test | sed -n l               \ntest\\r$\n$ adb shell \'busybox stty -opost; echo test\' | sed -n l\ntest$\n
Run Code Online (Sandbox Code Playgroud)\n

  • `adb shell 'stty -opost; ls /data/data'` 也可能有效(如果引入这些 `CR` 是因为 `adb` 与 Android 端的伪终端交互)。 (2认同)