xargs 未生成正确的命令

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

现在我想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

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

$ adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall
Failure
Failure
Failure
Failure
Failure

然而,独立运行每个命令会产生Success

$ adb uninstall com.company.android.app.adwidget
Success

我究竟做错了什么?

答案1

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

adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall

根据xargs实现,adb的标准输入将是/dev/null或来自 的管道grep。无论如何,它都不会是 tty,这可能就是为什么adb它希望能够与用户交互时会失败的原因。

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

xargs -n1 -ra <(adb shell ls /data/data | grep -i com.company) adb uninstall

在这种情况下,xargs从作为参数给出的文件中读取列表,这样-a您就可以不用管 stdin 了。

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

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

(使用-L而不是-naszargs-n限制全部的参数的数量adb(包括uninstall一个),这意味着我们需要-n 2)。

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

for x ($(adb shell ls /data/data | grep -i com.company)) adb uninstall $x

答案2

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

整个命令使用for(来自斯特凡·查泽拉斯的回答):

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

或者类似地使用xargs

adb shell ls /data/data | grep -i com.company | tr -d '\r' | xargs -r -n1 adb uninstall

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

$ adb shell echo test | sed -n l               
test\r$
$ adb shell 'busybox stty -opost; echo test' | sed -n l
test$

相关内容