Ce script est obsolète et n'est plus pris en charge. Pour la version la plus récente, qui nécessite Android 5+ et Python 3.5+, et qui prend en charge les variantes du thème Arcus et les superpositions Substratum, voir mon site web Dépôt GitHub .
Voici mon scénario remanié, désormais doublé NEMRIS ce qui répond à la question. Comme d'habitude, il est librement modifiable par n'importe qui, et il est également compatible avec sh, ce qui signifie qu'il peut être exécuté à partir de l'interpréteur de commandes Android.
En fait, le script dépend de l'option aapt peut discerner si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé. backupDir
et le appsToBackup
sont vides ou non et, si c'est le cas, il définit des valeurs par défaut au lieu d'abandonner.
Cependant, la date binaire ne peut pas déterminer votre véritable fuseau horaire, c'est pourquoi tout le monde doit modifier manuellement son TZ
variable.
Une fois lancé pour la première fois, l'outil est capable de déterminer si un APK a déjà été sauvegardé ou non, en utilisant un tableau rempli de sommes de contrôle MD5.
Il stocke également ce tableau dans un dictionnaire, appelé md5sums.txt qui est un fichier en clair situé dans le répertoire de sauvegarde de l'APK. Cette fonctionnalité permet de réduire de moitié le temps écoulé, mais elle dépend d'une statut binaire capable de mesurer la taille exacte d'un fichier en octets. Si le statut n'est pas conforme, les sommes de contrôle seront calculées à chaque fois, comme dans la version précédente du script. Cela permet de s'assurer que NEMRIS ne dépendra que de aapt . N'oubliez pas d'effacer manuellement le md5sums.txt si vous faites un peu de nettoyage dans le dossier de sauvegarde.
En outre, le script dispose désormais d'un maxDictSize
qui accepte une valeur entière. La valeur saisie sera interprétée comme des octets. Cette valeur peut être personnalisée et sert à recalculer le dictionnaire des sommes de contrôle si sa taille dépasse le maximum autorisé.
Enfin, je l'ai patché pour qu'il prenne en charge les applications tierces dont les noms contiennent des barres obliques. Plus d'erreurs bizarres, je pense.
J'espère qu'il sera utile.
Licence : Licence publique générale GNU, version 3 ou plus tard.
#!/system/bin/sh
# nemris - an Android app backup tool
# Copyright (C) 2016 Death Mask Salesman
# <http://android.stackexchange.com/users/152843/death-mask-salesman>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License at <http://www.gnu.org/licenses/> for
# more details.
#This variable is used to calculate the total elapsed time
#DO NOT alter it, or the script won't be able to calculate it
SECONDS=0
#This variable is used to output the time when the script is launched
#Edit the plus/minus and the value after "UTC", to force date into displaying your correct time zone
timeAtStart="$(TZ=UTC-2 date +%H:%M:%S)"
#This variable stores the path where to backup your apps' APK files
#Edit it freely, but do not forget the quotes
backupDir=""
#This variable stores the path where the script is located
scriptDir="$(dirname "$(readlink -f "$0")")"
#This variable tells the script which apps to backup
#Supported values are:
# - "User": tells the script to backup only third-party apps
# - "System": tells the script to backup only system apps
# - "All": tells the script to backup both 3rd party and system apps
appsToBackup=""
#This variable contains the maximum size of the checksums dictionary, in bytes
#It is used to decide whether to flush the dictionary or not
typeset -i maxDictSize=20000
#This function outputs the time when the operations start. Purely cosmetic: can be disabled safely
#///Beginning of function "timeInfo"///
function timeInfo {
echo "[ INFO ] Operations started at "$timeAtStart"."
}
#///End of function "timeInfo"///
#This function checks if the "aapt" and "stat" utilities are installed
#///Beginning of function "dependencyChecker"///
function dependencyChecker {
echo "[ INFO ] Checking if aapt and stat are installed..."
whence -v aapt &> /dev/null
isAaptAbsent="$(echo $?)"
whence -v stat &> /dev/null
isStatAbsent="$(echo $?)"
if [ "$isAaptAbsent" == "0" ]; then
echo "[ INFO ] aapt has been found."
unset isAaptAbsent
else
echo "[ FATAL ] aapt cannot be found. Aborting."
exit
fi
if [ "$isStatAbsent" == "0" ]; then
echo "[ INFO ] stat has been found."
echo ""
echo "[ INFO ] Checking if stat can measure exact file sizes..."
stat -c %s "$scriptDir/nemris.sh" &> /dev/null
if [ "$(echo $?)" == "0" ]; then
echo "[ INFO ] Measurement successful."
isStatUsable="1"
unset isStatAbsent
else
echo "[ WARNING ] Measurement failed. stat will be ignored."
isStatUsable="0"
unset isStatAbsent
fi
else
echo "[ WARNING ] stat cannot be found."
isStatUsable="0"
unset isStatAbsent
fi
}
#///End of function "dependencyChecker"///
#This function verifies whether the values into "backupDir" and "appsToBackup" have anything inside or not
#In the case that the variables are empty, default values will be used, instead
#///Beginning of the function "variablesChecker"///
function variablesChecker {
echo "[ INFO ] Checking if a backup path has already been specified..."
if [ "$backupDir" == "$(cat /dev/null)" ]; then
echo "[ WARNING ] A backup path has not been specified."
echo "[ WARNING ] Setting the path to default (/sdcard/AppsBackup)."
backupDir="/sdcard/AppsBackup"
else
echo "[ INFO ] Backup path has already been specified."
fi
echo ""
echo "[ INFO ] Checking if the typology of apps to backup has already been chosen..."
if [ "$appsToBackup" == "$(cat /dev/null)" ]; then
echo "[ WARNING ] A typology of apps to backup has not been specified."
echo "[ WARNING ] Setting the default typology (User)."
appsToBackup="User"
else
echo "[ INFO ] A typology of apps has already been specified."
fi
}
#///End of the function "variablesChecker"///
#This function emulates the case insensitivity for "appsToBackup", usually provided by brace expansion, but lacked by sh
#In the case that an unsupported value is found, a default value will be used instead
#///Beginning of the function "caseInsensitiveWorkaround"///
function caseInsensitiveWorkaround {
systemArray="$(echo -n {s,S}{y,Y}{s,S}{t,T}{e,E}{m,M})"
userArray="$(echo -n {u,U}{s,S}{e,E}{r,R})"
allArray="$(echo -n {a,A}{l,L}{l,L})"
case ${appsToBackup:0:1} in
s|S)
for i in $systemArray; do
case "$appsToBackup" in
$i)
appsToBackup="System"
;;
esac
done
;;
u|U)
for i in $userArray; do
case "$appsToBackup" in
$i)
appsToBackup="User"
;;
esac
done
;;
a|A)
for i in $allArray; do
case "$appsToBackup" in
$i)
appsToBackup="All"
;;
esac
done
;;
*)
echo "[ WARNING ] \""$appsToBackup"\": invalid typology."
echo "[ WARNING ] Setting the default typology (User)."
appsToBackup="User"
;;
esac
case "$appsToBackup" in
System|User|All)
;;
*)
echo "[ WARNING ] \""$appsToBackup"\": invalid typology."
echo "[ WARNING ] Setting the default typology (User)."
appsToBackup="User"
;;
esac
}
#///End of the function "caseInsensitiveWorkaround"///
#This function checks if the backup directory already exists
#///Beginning of the function "backupDirCheck"///
function backupDirCheck {
echo "[ INFO ] Checking if the backup directory already exists..."
if [ -d "$backupDir" ]; then
echo -n "[ INFO ] Backup directory already exists, "
cd "$backupDir"
#echo "[ INFO ] Checking if the directory is empty..."
if [ "$(ls | grep "\.apk$" | head -n 1)" == "$(cat /dev/null)" ]; then
echo "and does not have any APK file inside."
else
echo "and has at least an APK file inside."
isDirectoryEmpty="0"
fi
else
echo "[ INFO ] Backup directory does not exist. Creating it now."
mkdir -p "$backupDir"
cd "$backupDir"
fi
}
#///End of the function "backupDirCheck"///
#This function compares the size of the checksums dictionary and flushes it if too big
#///Beginning of function "sizeComparer"///
function sizeComparer {
echo "[ INFO ] Comparing the dictionary size to the max value..."
typeset -i dictSize="$(stat -c %s "$backupDir/md5sums.txt")"
if [ $dictSize -ge maxDictSize ]; then
echo "[ INFO ] The dictionary size exceeds the maximum value. The checksums will be recomputed."
dictTooBig="1"
unset dictSize
else
echo "[ INFO ] The dictionary size does not exceed the maximum value."
fi
}
#///End of function "sizeComparer"///
#This function generates a MD5 array, filled with the checksum of each APK file
#///Beginning of the function "checksumsGenerator"///
function checksumsGenerator {
echo "[ INFO ] Generating file checksums..."
apkList="$(printf "%s\n" * | grep "\.apk$")"
for i in $apkList; do
tempMd5=($(md5sum $i))
md5Array="$tempMd5 $md5Array"
done
cat << EOF > "$backupDir/md5sums.txt"
md5Array="$md5Array"
EOF
echo "[ INFO ] MD5 array generated."
}
#///End of the function "checksumsGenerator"///
#This function creates a list of the apps to backup, based on the content in "appsToBackup"
#///Beginning of the function "appListGenerator"///
function appListGenerator {
case "$appsToBackup" in
System)
echo "[ INFO ] Retrieving system apps list..."
appList="$(pm list packages -s)"
;;
User)
echo "[ INFO ] Retrieving third-party apps list..."
appList="$(pm list packages -3)"
;;
All)
echo "[ INFO ] Retrieving apps list..."
appList="$(pm list packages)"
;;
esac
echo "[ INFO ] Applications list retrieved."
}
#///End of the function "appListGenerator"///
#This function creates an array, filled with each app's full path
#///Beginning of the function "apkPathRetriever"///
function apkPathRetriever {
echo "[ INFO ] Collecting the path of each app's APK..."
for i in $appList; do
apkPath="${i#p*:}"
apkPath="$(pm path $apkPath)"
apkPath="${apkPath#p*:}"
pathArray="$apkPath $pathArray"
done
echo "[ INFO ] Paths collected."
}
#///End of the function "apkPathRetriever"///
#This function extracts the label or the package name for the APK supplied to it
#It extracts the app label for any third-party app, the package name for any system app
#///Beginning of the function "appNameRetriever"///
function appNameRetriever {
case "$1" in
/system/*)
appName="${aaptOutput#*name=\'}"
appName="${appName//\'*/}"
displayedName="$appName"
;;
/data/*|/mnt/asec/*)
appName="${aaptOutput#*application-label:\'}"
appName="${appName//app*/}"
appName="${appName%\'*}"
displayedName=$appName
appName="${appName//\//}"
appName="$(printf "%s" $appName)"
;;
esac
}
#///End of the function "appNameRetriever"///
#This function retrieves the version number of the APK
#///Beginning of the function "appVersionRetriever"///
function appVersionRetriever {
appVersion="${aaptOutput#*versionName=\'}"
appVersion="${appVersion//platformBuildVersion*/}"
appVersion="${appVersion%\'*}"
appVersion="$(printf "%s" $appVersion)"
}
#///End of the function "appVersionRetriever"///
#This function compares the MD5 of the APK in question against any MD5 in the array
#If it finds a match, then the function exits and the APK isn't backed up
#///Beginning of the function "md5Compare"///
function md5Compare {
alreadyBackedUp=0
appMd5=($(md5sum "$1"))
for c in $md5Array; do
if [ "$alreadyBackedUp" == "0" ]; then
if [ "$appMd5" == "$c" ]; then
alreadyBackedUp=1;
fi
else
break;
fi
done
}
#///End of the function "md5Compare"///
#This function decides whether to backup an app or not
#It calls both "appNameRetriever" and "appVersionRetriever" for renaming any copied "base.apk"
#It also decides whether to call "md5Compare" or not, in order to see if an app has already been backed up
#///Beginning of the function "apkBackup"///
function apkBackup {
for i in $pathArray; do
aaptOutput="$(aapt d badging "$i")"
appNameRetriever "$i"
appVersionRetriever
if [ "$isDirectoryEmpty" == "0" ]; then
md5Compare "$i"
if [ "$alreadyBackedUp" == "0" ]; then
echo -n "[ INFO ] Backing up "$displayedName"... "
cp "$i" "$backupDir"/$appName"_"$appVersion.apk
echo "done."
md5Array="$appMd5 $md5Array"
else
echo "[ INFO ] "$displayedName" has already been backed up."
fi
else
echo -n "[ INFO ] Backing up "$displayedName"... "
cp "$i" "$backupDir"/$appName"_"$appVersion.apk
echo "done."
fi
done
if [ "$md5Array" != "$(cat /dev/null)" ]; then
cat << EOF > "$backupDir/md5sums.txt"
md5Array="$md5Array"
EOF
fi
}
#///End of the function "apkBackup"///
#This is the core of the tool
echo "**************************"
echo " NEMRIS - App backup tool "
echo " by Death Mask Salesman "
echo "**************************"
timeInfo
echo ""
dependencyChecker
echo ""
variablesChecker
echo ""
caseInsensitiveWorkaround
echo ""
backupDirCheck
echo ""
#This section checks whether to analyze the dictionary size
#It also decides whether to compute the checksums
if [ "$isDirectoryEmpty" == "0" ]; then
if [ -e "$backupDir/md5sums.txt" ]; then
if [ "$isStatUsable" == "1" ]; then
sizeComparer
echo ""
else
dictTooBig="1"
fi
if [ "$dictTooBig" == "1" ]; then
checksumsGenerator
echo ""
else
source "$backupDir/md5sums.txt"
fi
else
echo "[ INFO ] Checksums not calculated yet."
checksumsGenerator
echo ""
fi
fi
appListGenerator
echo ""
apkPathRetriever
echo ""
apkBackup
echo ""
echo "[ INFO ] Operations took "$(((SECONDS/60)/60))" hours, "$(((SECONDS/60)%60))" minutes and "$((SECONDS%60))" seconds."
echo "[ INFO ] All done!"