Xargs not generating correct command

xargszsh

I want to delete a number of apps on an Android device that all begin with the same package. I get these with the following command:

$ 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

Now I want to execute adb uninstall for each of these package names, I thought of using 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

It looks like it is going to work, so I remove echo:

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

However running each command independently yields Success:

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

What am I doing wrong?

Best Answer

Though the problem ended up being caused by CR characters in the adb shell output (inserted by the tty line discipline of the the pty created on the target Android system (see here for more details)), another possible explanation (and I'll leave it there for future readers as that's a common problem with xargs) could have been that in:

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

depending on the xargs implementation, adb's stdin will be either /dev/null or the pipe from grep. In any case, it won't be the tty and that may be why adb fails if it expects to be able to interact with the user.

With GNU xargs and a shell with support for process substitution (like zsh), you could change it to:

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

In that case xargs reads the list from the file given as argument to -a which lets you leave stdin alone.

Or since you're mentioning zsh, you could use:

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

(using -L instead of -n as zargs's -n limits the total number of arguments to adb (including the uninstall one) which means we would need -n 2).

Or simply use a loop, which would be even shorter and more legible in this case:

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