1

You can do adb shell pm list packages, and after reading through the list, do an adb shell pm path "com.yourpackage" to get the full path and do a simply adb pull.

Fact is: I want to automate this. I thought about a really simple bash script:

for apk in $(adb shell pm list packages | sed 's/package://g' | sort); do
    # If you just do an echo, IT WORKS
    # echo $apk
    # But here it doesn't work
    adb shell pm path $apk
done

But that simply doesn't work. It seems that "$apk" becomes lost or something and nothing is shown in that snippet. I don't know if it's a subshell problem, or some peculiarity when using variables that got through an adb shell.

Should I create a script apks.sh just using pm list, for and such, push it to my device, run adb shell only once and execute apks.sh?

3
  • Try enclosing the parameters in quotes: adb shell "pm path $apk" and see if that works. Quoting it such makes clear the entire thing should be passed to adb shell. Without the quotes, it's not clear whether the $apk is 2nd param to pm or 5th param to adb ;)
    – Izzy
    Commented Mar 8, 2016 at 6:49
  • The culprit is that pm list packages, just as pm path [PACKAGE], results in the output being preceded by the "package:" string. A way to circumvent this would be to iterate through the pm list packages with something like the following: for i in $(pm list packages); do. Then, inside the loop, you'll define a variable var with the content of i, such as in: var=$i. Next, you'll trim the "package:" with var="${var#p*:}", which will return the package name. After that, pass var through pm path, as in: pm path $var, and trim "package:" once more.
    – Grimoire
    Commented Mar 8, 2016 at 10:06
  • Regarding your question, I guess that the variable apk belongs to the Linux terminal only. At line 4, you're trying to pass apk to the pm as if it were an Android shell variable.
    – Grimoire
    Commented Mar 8, 2016 at 10:09

2 Answers 2

4

It doesn't work because adb adds carriage return in the output. You can read Gilles' answer for more info on that.

All of you've to do is remove the carriage return. Here's the tweaked script of yours:

#!/bin/bash

for package in $(adb shell pm list packages | tr -d '\r' | sed 's/package://g'); do
    apk=$(adb shell pm path $package | tr -d '\r' | sed 's/package://g');
    echo "Pulling $apk";
    adb pull -p "$apk";
done

You can use -p with adb pull if only you've version 1.0.32 or above of adb. sort is not needed, as I see it.

0
3

I derived a one liner bash command from Firelord's awesome answer:

for package in $(adb shell pm list packages -3 | tr -d '\r' | sed 's/package://g'); do apk=$(adb shell pm path $package | tr -d '\r' | sed 's/package://g'); echo "Pulling $apk"; adb pull -p $apk "$package".apk; done

Changes/Improvements from original version:

  • Backups only 3rd party apps, coz I don't see the point of backing up built-in apps. However, if you wanna backup system apps too, just skip the -3 flag.
  • Renames all apks to their package names instead of overwriting the same base.apk package in Android 6.0 "Marshmallow" and above.
1
  • 1
    This solution was failing to account for apk bundles where multiple APKs were returned per package, and I wireless debugging kept disconnecting for me (Galaxy Watch6) so I wanted to skip files that were already downloaded. ChatGPT to the rescue. chat.openai.com/share/cdf380e0-cd73-4630-8b3e-7233cec7c2cb. Pretty incredible, it did the job perfectly. Commented Sep 21, 2023 at 0:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .