Aquí está mi renovado script, que ahora se llama la NEMRIS, que responde a la pregunta. Como de costumbre, es libremente editable por cualquier persona, y sh-compatible como bien, lo que significa que se puede ejecutar desde el shell de Android.
En realidad, la secuencia de comandos depende de la aapt utilidad, puede discernir si la utilidad está instalado, es capaz de determinar si el backupDir
y appsToBackup
variables están vacíos o no y, si lo son, se establece los valores predeterminados en lugar de abortar.
Aún así, la fecha binario no puede determinar su verdadera zona horaria, por lo que todo el mundo debería editar manualmente su TZ
variable.
Una vez que ha sido corrió por primera vez, la herramienta es capaz de discernir si un APK ya ha sido respaldado o no, haciendo uso de una matriz llena de sumas de comprobación MD5. Espero que puede resultar muy útil.
#!/system/bin/sh
#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 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 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" utility is installed
#///Beginning of function "dependencyChecker"///
function dependencyChecker {
echo "[ INFO ] Checking if aapt is installed..."
whence -v aapt &> /dev/null
isAaptAbsent="$(echo $?)"
if [ "$isAaptAbsent" == "0" ]; then
echo "[ INFO ] aapt has been found."
unset isAaptAbsent
else
echo "[ FATAL ] aapt cannot be found. Aborting."
exit
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 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
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="$(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 founds 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
alreadyBackedUp=0
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."
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
}
#///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 ""
if [ "$isDirectoryEmpty" == "0" ]; then
checksumsGenerator
echo ""
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!"