7

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?

  • 1
    Not a solution to your problem, but you don't need the grep, just use a glob: adb ls /data/data/*com.company* | xargs -n1 adb uninstall should do the same thing. Does it work if you do for i in adb ls /data/data/*com.company*; do adb uninstall "$i"; done ? – terdon May 8 '17 at 11:26
  • Ah, sorry, that should have been adb shell ls, not adb ls, does that make a difference? – terdon May 8 '17 at 11:41
  • 1
    @terdon, if you don't quote that glob, it will be expanded by the local shell, not the shell running on Android. The output will also be things like /data/data/something instead of something. – Stéphane Chazelas May 8 '17 at 11:42
  • @StéphaneChazelas Right, not quoting it still gives me no matches found. Quoting it behaves the same as in my question (Failure for all executions) – m0skit0 May 8 '17 at 11:43
8

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
  • Your xargs and for commands give the same error as my xargs version: Failure for all executions. zargs command gives me zargs: argument list too long (thanks for zargs btw) – m0skit0 May 8 '17 at 11:41
  • 1
    @m0skit0, wrap the for loop in (set -x; foo loop here) and see where that differs from running adb by hand. Possibly the output has some trailing carriage return (aka $'\C-M') characters. – Stéphane Chazelas May 8 '17 at 11:45
  • Cool feature. I see no difference, here's the first command output: +zsh:48> x=com.company.android.app.attendancereports +zsh:48> adb uninstall com.company.android.app.attendancereports Failure. I see no strange characters. – m0skit0 May 8 '17 at 11:47
  • @m0skit0, and adb uninstall com.company.android.app.attendancereports works? Possibly you need to insert some delay between adb commands? – Stéphane Chazelas May 8 '17 at 11:50
  • 1
    @m0skit0. Yes, I can reproduce with that version. With a newer version, you'd get the $'\C-M' as shown above. Mystery solved. – Stéphane Chazelas May 8 '17 at 14:07
4

Redirecting the output of adb shell ls /data/data | grep -i com.company to a file and examining it with a hexeditor I found out they're appended with Windows-style carriage return \r\n (0x0D 0x0A). So getting rid of the \r with tr -d '\r' solved the problem.

Whole command using for (from Stéphane Chazelas' answer):

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

Or similarly using xargs:

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

Another option (as kindly explained by Stéphane Chazelas on the comments below) is disabling \r altogether with stty -opost, although this most likely requires busybox (or an alternative like toybox) to be installed on the Android device.

$ adb shell echo test | sed -n l               
test\r$
$ adb shell 'busybox stty -opost; echo test' | sed -n l
test$
  • 2
    adb shell 'stty -opost; ls /data/data' might also work (if those CR are introduced because adb interacts with a pseudo-terminal on the Android side). – Stéphane Chazelas May 8 '17 at 14:12
  • @StéphaneChazelas I didn't have the time to test because all packages were uninstalled now. I have to install some again to test. – m0skit0 May 8 '17 at 15:59
  • Transferring binary data over ADB shell (ie fast file transfer using tar) suggests that's what's happening. stty raw includes stty -opost which includes stty -onlcr. Looking at a man page for that adb, it looks like you can also do adb shell -t to disable that pty allocation in the first place! – Stéphane Chazelas May 8 '17 at 16:07
  • For your first suggestion: /system/bin/sh: stty: not found\r$. About -t it doesn't work either: error: target doesn't support PTY args -Tt. Please note this is a quite old Android device (4.1). – m0skit0 May 8 '17 at 16:23
  • But using busybox does the trick indeed: adb shell 'busybox stty -opost; echo test' | sed -n l outputs test$ while adb shell echo test | sed -n l outputs test\r$. Thanks! – m0skit0 May 8 '17 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.