Files
backup_script/tools/core/backup_core.sh
2026-04-29 20:08:41 +08:00

849 lines
41 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/system/bin/sh
# module: backup_core.sh
backup_path() {
if [[ $Output_path != "" ]]; then
[[ ${Output_path: -1} = / ]] && Output_path="${Output_path%?}"
if [[ ${Output_path:0:1} != / ]]; then
Directory_type="相對路徑"
Backup="$MODDIR/$Output_path/Backup_${Compression_method}_$user"
else
Directory_type="絕對路徑"
Backup="$Output_path/Backup_${Compression_method}_$user"
fi
outshow="使用自定義目錄($Directory_type)"
else
Backup="$MODDIR/Backup_${Compression_method}_$user"
if [[ ! -f ${0%/*}/app_details.json ]]; then
outshow="使用當前路徑作為備份目錄"
else
[[ -d $Backup ]] && outshow="使用上層路徑作為備份目錄" || echoRgb "$Backup目錄不存在" "0"
fi
fi
PU=$(mount | awk '$3 ~ "/mnt/media_rw/[^/]+$" {print $3, $5}' | egrep -v "$mount_point")
OTGPATH="$(echo "$PU" | cut -d' ' -f1)"
OTGFormat="$(echo "$PU" | cut -d' ' -f2)"
if [[ -d $OTGPATH ]]; then
if [[ $(echo "$MODDIR" | egrep -o "^${OTGPATH}") != "" ]]; then
hx="true"
Backup="$MODDIR/Backup_${Compression_method}_$user"
else
case $Lo in
0|1)
echoRgb "檢測到隨身碟 是否在隨身碟備份\n -音量上是,音量下不是" "2"
get_version "選擇了隨身碟備份" "選擇了本地備份" ;;
2)
Enter_options "檢測到隨身碟輸入1使用隨身碟備份 0本地備份" "選擇了隨身碟備份" "本地備份" && isBoolean "$parameter" "branch" && branch="$nsx" ;;
esac
[[ $branch = true ]] && hx="$branch"
[[ $hx = true ]] && Backup="$OTGPATH/Backup_${Compression_method}_$user"
fi
if [[ $hx = true ]]; then
if [[ $OTGFormat = vfat ]]; then
echoRgb "隨身碟檔案系統$OTGFormat不支持超過單檔4GB\n -請格式化為exfat" "0"
exit 1
fi
outshow="於隨身碟備份" && hx=usb
fi
fi
[[ ! -d $Backup ]] && mkdir -p "$Backup"
#分區詳細
if [[ $(echo "$Backup" | egrep -o "^/storage/emulated") != "" ]]; then
Backup_path="/data"
else
Backup_path="${Backup%/*}"
fi
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
echoRgb "$outshow" "2"
}
Calculate_size() {
#計算出備份大小跟差異性
filesizee="$(find "$1" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
if [[ $(echo "$filesizee > $filesize" | bc) -eq 1 ]]; then
NJL="本次備份增加 $(size "$(echo "scale=2; $filesizee - $filesize" | bc)")"
elif [[ $(echo "$filesizee < $filesize" | bc) -eq 1 ]]; then
NJL="本次備份減少 $(size "$(echo "scale=2; $filesize - $filesizee" | bc)")"
else
NJL="文件大小未改變"
fi
echoRgb "備份資料夾路徑↓↓↓\n -$1"
echoRgb "備份資料夾總體大小$(size "$filesizee")"
echoRgb "$NJL"
}
Backup_apk() {
#檢測apk狀態進行備份
#創建APP備份文件夾
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
apk_version2="$(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1)"
if [[ $apk_version = $apk_version2 ]]; then
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
unset xb
osj=$((osj + 1))
result=0
echoRgb "Apk版本無更新 跳過備份" "2"
else
if [[ $nobackup = false ]]; then
if [[ $apk_version != "" ]]; then
osn=$((osn + 1))
update_apk="$(echo "$name1 \"$name2\"")"
update_apk2="$(echo "$update_apk\n$update_apk2")"
echoRgb "版本:$apk_version>$apk_version2"
else
osk=$((osk + 1))
add_app="$(echo "$name1 \"$name2\"")"
add_app2="$(echo "$add_app\n$add_app2")"
echoRgb "版本:$apk_version2"
fi
unset Filesize
Filesize="$(find "$apk_path2" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
rm -rf "$Backup_folder/apk.tar"*
partition_info "$Backup" "$name1 apk"
if [[ $Skip != 1 ]]; then
#備份apk
echoRgb "$1"
echo "$apk_path" | sed -e '/^$/d' | while read; do
echoRgb "${REPLY##*/} $(size "$REPLY")"
done
(
cd "$apk_path2"
case $Compression_method in
tar | TAR | Tar) tar --checkpoint-action="ttyout=%T\r" -cf "$Backup_folder/apk.tar" *.apk ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" -cf - *.apk | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/apk.tar.zst" ;;
esac
)
echo_log "備份$apk_number個Apk"
if [[ $result = 0 ]]; then
Validation_file "$Backup_folder/apk.tar"*
if [[ $result = 0 ]]; then
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
[[ $apk_version != "" ]] && {
echoRgb "覆蓋app_details"
jq --arg apk_version "$apk_version2" --arg software "$name1" '.[$software].apk_version = $apk_version' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
} || {
echoRgb "新增app_details"
extra_content="{
\"$name1\": {
\"PackageName\": \"$name2\",
\"apk_version\": \"$apk_version2\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
}
else
rm -rf "$Backup_folder"
fi
if [[ $name2 = com.android.chrome ]]; then
#刪除所有舊apk ,保留一個最新apk進行備份
ReservedNum=1
FileNum="$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l)"
while [[ $FileNum -gt $ReservedNum ]]; do
OldFile="$(ls -rt /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | head -1)"
rm -rf "${OldFile%/*/*}" && echo "刪除文件:${OldFile%/*/*}"
FileNum=$((FileNum - 1))
done
[[ -f $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null) && $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l) = 1 ]] && cp -r "$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null)" "$Backup_folder/nmsl.apk"
fi
else
rm -rf "$Backup_folder"
fi
fi
else
osj=$((osj + 1))
rm -rf "$Backup_folder"
fi
fi
[[ $name2 = bin.mt.plus && ! -f $Backup/$name1.apk ]] && cp -r "$apk_path" "$Backup/$name1.apk"
}
Backup_ssaid() {
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
ssaid="$(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info")"
[[ $ssaid != null && $ssaid != "" ]] && echoRgb "SSAID:$ssaid"
if [[ $ssaid != null && $ssaid != $Ssaid ]]; then
echoRgb "備份ssaid"
echoRgb "$Ssaid>$ssaid"
SSAID_apk="$(echo "$name1 \"$name2\"")"
SSAID_apk2="$(echo "$SSAID_apk\n$SSAID_apk2")"
jq --arg entry "$name1" --arg new_value "$ssaid" '.[$entry].Ssaid |= $new_value' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份ssaid"
fi
[[ $ssaid = null ]] && ssaid=
}
Backup_Permissions() {
get_Permissions="$(jq -r '.[] | select(.permissions != null).permissions' "$app_details")"
Get_Permissions="$(get_Permissions "$name2" | jq -nR '[inputs | select(. != "null" and length>0) | split(" ") | {(.[0]): (.[1:] | join(" "))}] | if length > 0 then add else empty end')"
if [[ $Get_Permissions != "" && ($Get_Permissions = *true* || $Get_Permissions = *false*) ]]; then
if [[ $get_Permissions = "" ]]; then
echoRgb "備份權限"
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName].permissions |= $permissions' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份權限"
else
if [[ $get_Permissions != "" && ($get_Permissions = *true* || $get_Permissions = *false*) ]]; then
if [[ $get_Permissions != $Get_Permissions ]]; then
echoRgb "權限變更"
jq -n --argjson old "$get_Permissions" --argjson new "$Get_Permissions" '$new | to_entries | map(select(.key as $k | $old[$k] != null and $old[$k] != .value)) | .[].key' | sed 's/^/ /'
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName] |= . + {permissions: $permissions}' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echo_log "備份權限"
fi
fi
fi
else
[[ $Get_Permissions != "" ]] && echoRgb "備份權限失敗$(get_Permissions "$name2")" "0"
fi
}
#檢測數據位置進行備份
Backup_data() {
data_path="$path/$1/$name2"
MODDIR_NAME="${data_path%/*}"
MODDIR_NAME="${MODDIR_NAME##*/}"
[[ -f $app_details ]] && Size="$(jq -r --arg entry "$1" '.[$entry] | select(.Size != null).Size' "$app_details" 2>/dev/null)"
case $1 in
user) data_path="$path2/$name2" ;;
user_de) data_path="$path3/$name2" ;;
data|obb) ;;
*)
data_path="$2"
if [[ $1 != thanox ]]; then
Compression_method1="$Compression_method"
Compression_method=tar
fi
zsize=1
zmediapath=1
;;
esac
if [[ -d $data_path ]]; then
unset Filesize ssaid Get_Permissions result Permissions
Filesize="$(find "$data_path" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
[[ $Filesize != "" ]] && {
if [[ $Size != $Filesize ]]; then
case $1 in
user)
if [[ $(su "$(pm list packages -U --user "$user" </dev/null | awk -v pkg="$name2" -F'[ :]' '$2 == pkg {print $4}')" -c keystore_cli_v2 list | wc -l) -ge 2 ]]; then
echoRgb "$name1包含keystore 恢復可能閃退" "0"
jq --arg entry "$name1" '.[$entry].keystore |= "true"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
else
jq --arg entry "$name1" '.[$entry].keystore |= "false"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
fi
Backup_ssaid
Backup_Permissions ;;
esac
#停止應用
case $1 in
user|data|obb|user_de) kill_app ;;
esac
rm -rf "$Backup_folder/$1.tar"*
partition_info "$Backup" "$1"
if [[ $Skip != 1 ]]; then
echoRgb "備份$1數據"
# 判斷是否超過指定大小
if [[ $Filesize2 != *"bytes"* ]]; then
if [[ $Filesize2 = *"KB"* ]]; then
if [[ $(echo "${Filesize2% KB}" | bc) > 1 ]]; then
Start_backup="true"
else
Start_backup="false"
fi
else
Start_backup="true"
fi
else
Start_backup="false"
fi
if [[ $Start_backup = true ]]; then
case $1 in
user|user_de)
case $Compression_method in
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" 2>/dev/null ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" 2>/dev/null ;;
esac
;;
*)
case $Compression_method in
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" ;;
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" ;;
esac
;;
esac
echo_log "備份$1數據"
else
echoRgb "$1數據 $Filesize2太小" "0" && result=1
fi
if [[ $result = 0 ]]; then
Validation_file "$Backup_folder/$1.tar"*
if [[ $result = 0 ]]; then
if [[ ! $Filesize -eq 0 ]]; then
size2="$(stat -c %s "$Backup_folder/$1.tar"*)"
rate="$(echo "scale=2; (1 - ($size2 / $Filesize)) * 100" | bc)"
echoRgb "壓縮率${rate}% 大小$(size "$size2")"
fi
[[ ${Backup_folder##*/} = Media ]] && [[ $(sed -e '/^$/d' "$mediatxt" | grep -w "${REPLY##*/}.tar$" | head -1) = "" ]] && echo "$FILE_NAME" >> "$mediatxt"
if [[ $zsize != "" ]]; then
extra_content="{
\"$1\": {
\"path\": \"$2\",
\"Size\": \"$Filesize\"
},
\"Backup time\": {
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
else
extra_content="{
\"$1\": {
\"Size\": \"$Filesize\"
},
\"Backup time\": {
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
}
}"
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
fi
else
rm -rf "$Backup_folder/$1".tar.*
fi
fi
[[ $Compression_method1 != "" ]] && Compression_method="$Compression_method1"
unset Compression_method1
fi
else
[[ $Size != "" ]] && echoRgb "$1數據無發生變化 跳過備份" "2"
fi
}
else
[[ -f $data_path ]] && echoRgb "$1是一個文件 不支持備份" "0"
fi
}
# ===================== 子函數: 驗證備份參數 =====================
validate_backup_params() {
self_test
case $MODDIR in
/storage/emulated/0/Android/* | /data/media/0/Android/* | /sdcard/Android/*) echoRgb "請勿在$MODDIR內備份" "0" && exit 2 ;;
esac
case $Compression_method in
zstd | Zstd | ZSTD | tar | Tar | TAR) ;;
*) echoRgb "$Compression_method為不支持的壓縮算法" "0" && exit 2 ;;
esac
#校驗選填是否正確
case $Lo in
0)
[[ $Backup_Mode != "" ]] && isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx" || {
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
}
if [[ $Backup_Mode = true ]]; then
if [[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]]; then
if [[ $blacklist_mode != "" ]]; then
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
else
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔\n -警告! " "2"
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
fi
fi
fi
if [[ $Backup_Mode = true ]]; then
[[ $Backup_obb_data != "" ]] && isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx" || {
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_obb_data="$branch"
}
[[ $Backup_user_data != "" ]] && isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx" || {
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_user_data="$branch"
}
else
Backup_user_data="false"
Backup_obb_data="false"
fi
[[ $backup_media != "" ]] && isBoolean "$backup_media" "backup_media" && backup_media="$nsx" || {
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && backup_media="$branch"
}
[[ $setDisplayPowerMode != "" ]] && isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx" || {
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
}
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
get_version "忽略" "備份" && Background_apps_ignore="$branch"
} ;;
1)
[[ $Backup_Mode = "" ]] && {
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
} || isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
if [[ $Backup_Mode = true ]]; then
if [[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]]; then
[[ $blacklist_mode = "" ]] && {
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔" "2"
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
} || isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
fi
[[ $Backup_obb_data = "" ]] && {
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_obb_data="$branch"
} || isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
[[ $Backup_user_data = "" ]] && {
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && Backup_user_data="$branch"
} || isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
fi
[[ $backup_media = "" ]] && {
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
get_version "備份" "不備份" && backup_media="$branch"
} || isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
[[ $setDisplayPowerMode = "" ]] && {
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
} || isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
[[ $Background_apps_ignore = "" ]] && {
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
get_version "忽略" "備份" && Background_apps_ignore="$branch"
} || isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
;;
2)
[[ $Backup_Mode = "" ]] && {
Enter_options "輸入1備份應用+數據輸入0僅應用不包含數據" "應用+數據" "僅應用" && isBoolean "$parameter" "Backup_Mode" && Backup_Mode="$nsx"
} || {
isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
}
if [[ $Backup_Mode = true ]]; then
[[ $(echo "$blacklist" | egrep -v '#|' | wc -l) -gt 0 ]] && {
[[ $blacklist_mode = "" ]] && {
Enter_options "選擇黑名單模式輸入1不備份輸入0備份安裝檔" "不備份" "僅應用安裝檔" && isBoolean "$parameter" "blacklist_mode" && blacklist_mode="$nsx"
} || {
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
}
}
[[ $Backup_obb_data = "" ]] && {
Enter_options "是否備份外部數據 即比如原神的數據包\n -輸入1備份輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_obb_data" && Backup_obb_data="$nsx"
} || {
isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
}
[[ $Backup_user_data = "" ]] && {
Enter_options "是否備份使用者數據輸入1備份輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_user_data" && Backup_user_data="$nsx"
} || {
isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
}
fi
[[ $backup_media = "" ]] && {
Enter_options "全部應用備份結束後是否備份自定義目錄\n -輸入1備份0不備份" "備份" "不備份" && isBoolean "$parameter" "backup_media" && backup_media="$nsx"
} || {
isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
}
[[ $setDisplayPowerMode = "" ]] && {
Enter_options "應用備份開始後關閉螢幕\n -輸入1關閉0不關閉" "關閉" "不關閉" && isBoolean "$parameter" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
} || {
isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
}
[[ $Background_apps_ignore = "" ]] && {
Enter_options "存在進程忽略備份\n -輸入1不備份0備份" "忽略" "備份" && isBoolean "$parameter" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} || {
isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
} ;;
*) echoRgb "$conf_path Lo=$Lo填寫錯誤正確值0 1 2" "0" && exit 2 ;;
esac
}
# ===================== 子函數: 準備應用列表 =====================
prepare_app_list() {
i=1
#數據目錄
if [[ $list_location != "" ]]; then
if [[ ${list_location:0:1} = / ]]; then
txt="$list_location"
else
txt="$MODDIR/$list_location"
fi
else
txt="$MODDIR/appList.txt"
fi
txt="${txt/'/storage/emulated/'/'/data/media/'}"
txt_path="$txt"
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取應用列表再來備份" "0" && exit 1
TXT_NAME="${txt##*/}"
case ${TXT_NAME##*.} in
txt) ;;
*) echoRgb "$txt不是腳本讀取格式" "0" && exit 2 ;;
esac
sort -u "$txt" -o "$txt" &>/dev/null
data="$MODDIR"
hx="本地"
echoRgb "腳本受到內核機制影響 息屏後IO性能嚴重影響\n -請勿關閉終端或是息屏備份 如需終止腳本\n -請執行start.sh選擇終止腳本即可停止" "3"
backup_path
echoRgb "配置詳細:\n -壓縮方式:$Compression_method\n -音量鍵確認:$Lo\n -更新:$update\n -備份模式:$Backup_Mode\n -備份外部數據:$Backup_obb_data\n -備份user數據:$Backup_user_data\n -自定義目錄備份:$backup_media\n -存在進程忽略備份:$Background_apps_ignore\n -關閉螢幕:$setDisplayPowerMode"
D="1"
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
if [[ $Apk_info != "" ]]; then
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
else
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
fi
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
[[ ! -f ${0%/*}/app_details.json ]] && {
echoRgb "檢查備份列表中是否存在已經卸載應用" "3"
while read -r ; do
if [[ $(echo "$REPLY" | sed -E 's/^[ \t]*//; /^[ \t]*[#!]/d') != "" ]]; then
app=($REPLY $REPLY)
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
if [[ $(echo "$Apk_info" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') != "" ]]; then
[[ $Tmplist = "" ]] && Tmplist='#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market忽略安裝包和數據\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market僅忽略數據'
Tmplist="$Tmplist\n$REPLY"
else
echoRgb "$REPLY不存在系統,從列表中刪除" "0"
fi
fi
else
Tmplist="$Tmplist\n$REPLY"
fi
done < "$txt"
}
[[ $Update_backup = true ]] && {
echoRgb "檢查備份列表中已經更新應用" "3"
while read apk; do
Backup_folder="$Backup/$(echo "$apk" | cut -d':' -f1)"
app_details="$Backup_folder/app_details.json"
if [[ -d $Backup_folder ]]; then
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
apk_version2="$(pm list packages --show-versioncode --user "$user" "$(echo "$apk" | cut -d':' -f2)" </dev/null | cut -f3 -d ':' | head -n 1)"
[[ $apk_version != $apk_version2 ]] && {
[[ $Tmplist2 = "" ]] && Tmplist2="${apk/:/ }" || Tmplist2="$Tmplist2\n${apk/:/ }"
}
fi
done<<<"$(grep -Ev '^[#!]' "$txt" | awk '{print $1 ":" $2}')"
}
[[ $Tmplist != "" ]] && echo "$Tmplist" | sed -e '/^$/d' | sort>"$txt"
if [[ $Tmplist2 != "" ]]; then
txt="$(echo "$Tmplist2" | sort)"
else
[[ $Update_backup != "" ]] && echoRgb "應用目前無更新" "0" && exit 0
fi
if [[ ! -f $txt ]]; then
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
else
txt="$(egrep -v '#|' "$txt" | sed -e '/^$/d')"
fi
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
[[ -f ${0%/*}/app_details.json ]] && r=1
[[ $r = "" && ! -f ${0%/*}/app_details.json ]] && echoRgb "$MODDIR_NAME/appList.txt是空的或是包名被注釋備份個鬼\n -檢查是否注釋亦或者執行$MODDIR_NAME/start.sh" "0" && exit 1
}
# ===================== 子函數: 初始化備份存儲 =====================
init_backup_storage() {
if [[ $Backup_Mode = true ]]; then
[[ $Backup_user_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_user_data=0將不備份user數據" "0"
[[ $Backup_obb_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_obb_data=0將不備份外部數據" "0"
fi
[[ $backup_media = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -backup_media=0將不備份自定義資料夾" "0"
txt2="$Backup/appList.txt"
txt2="${txt2/'/storage/emulated/'/'/data/media/'}"
txt_path2="$txt2"
[[ ! -f $txt2 ]] && echo "#不需要恢復還原的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market">"$txt2"
txt2="$(cat "$txt2")"
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
if [[ -d $Backup/tools ]]; then
find "$Backup/tools" -maxdepth 1 -type f | while read; do
Tools_FILE_NAME="${REPLY##*/}"
if [[ -f $tools_path/$Tools_FILE_NAME ]]; then
filesha256="$(sha256sum "$tools_path/$Tools_FILE_NAME" 2>/dev/null | cut -d" " -f1)"
filesha256_1="$(sha256sum "$REPLY" 2>/dev/null | cut -d" " -f1)"
if [[ $filesha256 != $filesha256_1 ]]; then
cp -r "$tools_path/$Tools_FILE_NAME" "$REPLY"
echoRgb "更新$REPLY"
fi
fi
done
fi
filesize="$(find "$Backup" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
Quantity=0
#開始循環$txt內的資料進行備份
#記錄開始時間
en=118 # ANSI 256色起始編號用於通知欄圖標顏色
osn=0; osj=0; osk=0
#獲取已經開啟的無障礙
var="$(settings get secure enabled_accessibility_services 2>/dev/null)"
#獲取預設鍵盤
keyboard="$(settings get secure default_input_method 2>/dev/null)"
Set_screen_pause_seconds on
[[ $txt != "" ]] && [[ $(echo "$txt" | cut -d' ' -f2 | grep -w "^${keyboard%/*}$") != ${keyboard%/*} ]] && unset keyboard
if [[ -f ${0%/*}/app_details.json ]]; then
ssaid_info="$(get_ssaid "$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")")"
else
ssaid_info="$(get_ssaid "$(echo "$txt" | awk '{printf "%s ", $2}')")"
fi
starttime1="$(date -u "+%s")"
TIME="$starttime1"
notification "101" "開始備份"
}
# ===================== 子函數: 應用循環備份 =====================
backup_applications_loop() {
while [[ $i -le $r ]]; do
[[ $en -ge 229 ]] && en=118 # 超過229重置避免顏色越界
unset name1 name2 apk_path apk_path2
if [[ ! -f ${0%/*}/app_details.json ]]; then
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
else
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "${0%/*}/app_details.json" | head -n 1)"
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")"
name1="$ChineseName"
name2="$PackageName"
fi
[[ $name2 = "" || $name1 = "" ]] && echoRgb "警告! appList.txt應用包名獲取失敗可能修改有問題" "0" && exit 1
apk_path="$(pm path --user "$user" "$name2" 2>/dev/null | cut -f2 -d ':')"
apk_path2="$(echo "$apk_path" | head -1)"
apk_path2="${apk_path2%/*}"
if [[ -d $apk_path2 ]]; then
echoRgb "備份第$i/$r個應用 剩下$((r - i))" "3"
echoRgb "備份 $name1" "2"
notification "101" "備份第$i/$r個應用 剩下$((r - i))
備份 $name1"
unset Backup_folder ChineseName PackageName nobackup No_backupdata result apk_version apk_version2 zsize zmediapath Size data_path Ssaid ssaid Permissions
nobackup="false"
Background_application_list
[[ $Backstage != "" && $(echo "$Backstage" | egrep -w "^$name2$") != "" ]] && echoRgb "$name1存在後台 忽略備份" "0" && nobackup="true"
if [[ $Backup_Mode = true ]]; then
if [[ $name1 = !* || $name1 = * ]]; then
name1="$(echo "$name1" | sed 's/!//g ; s///g')"
echoRgb "跳過備份所有數據" "0"
No_backupdata=1
fi
if [[ $(echo "$blacklist" | grep -w "^$name2$") = $name2 ]]; then
if [[ $blacklist_mode = true ]]; then
echoRgb "黑名單應用跳過備份" "0"
nobackup="true"
else
echoRgb "黑名單應用跳過備份所有數據" "0"
fi
No_backupdata=1
fi
fi
Backup_folder="$Backup/$name1"
app_details="$Backup_folder/app_details.json"
if [[ -f $app_details ]]; then
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$app_details")"
[[ $PackageName != $name2 ]] && jq --arg name2 "$name2" 'walk(if type == "object" and .PackageName then .PackageName = $name2 else . end)' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
echoRgb "上次備份時間$(jq -r --arg entry "Backup time" '.[$entry] | select(.date != null).date' "$app_details" 2>/dev/null)"
fi
[[ $hx = USB && $PT = "" ]] && echoRgb "隨身碟意外斷開 請檢查穩定性" "0" && exit 1
starttime2="$(date -u "+%s")"
[[ $name2 = com.tencent.mobileqq ]] && echoRgb "QQ可能恢復備份失敗或是丟失聊天記錄請自行用你信賴的應用備份" "0"
[[ $name2 = com.tencent.mm ]] && echoRgb "WX可能恢復備份失敗或是丟失聊天記錄請自行用你信賴的應用備份" "0"
apk_number="$(echo "$apk_path" | wc -l)"
if [[ $nobackup != true ]]; then
if [[ $apk_number = 1 ]]; then
Backup_apk "非Split Apk" "3"
else
Backup_apk "Split Apk支持備份" "3"
fi
if [[ $result = 0 && $No_backupdata = "" ]]; then
if [[ $Backup_Mode = true ]]; then
if [[ $Backup_obb_data = true ]]; then
if [[ $name2 != bin.mt.plus ]]; then
#備份data數據
[[ $name1 = Nekogram ]] && rm -rf /data/media/0/Android/data/tw.nekomimi.nekogram/files/Telegram/Telegram\ {Video,Stories,Documents,Images}/{*,.*} 2>/dev/null
Backup_data "data"
#備份obb數據
Backup_data "obb"
else
echoRgb "$name1無法備份" "0"
fi
fi
#備份user數據
[[ $name2 != bin.mt.plus ]] && {
[[ $Backup_user_data = true ]] && {
Backup_data "user"
Backup_data "user_de"
}
}
[[ $name2 = github.tornaco.android.thanos ]] && Backup_data "thanox" "$(find "/data/system" -name "thanos"* -maxdepth 1 -type d 2>/dev/null)"
fi
fi
[[ -f $Backup_folder/${name2}.sh ]] && rm -rf "$Backup_folder/${name2}.sh"
[[ ! -f $Backup_folder/recover.sh ]] && touch_shell "3" "$Backup_folder/recover.sh"
[[ ! -f $Backup_folder/backup.sh ]] && touch_shell "1" "$Backup_folder/backup.sh"
fi
endtime 2 "$name1 備份" "3"
lxj="$(echo "$Occupation_status" | awk '{print $3}' | sed 's/%//g')"
echoRgb "完成$((i * 100 / r))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "3"
rgb_d="$rgb_a"
rgb_a=188 # ANSI 256色: 淺灰色分隔線
echoRgb "_________________$(endtime 1 "已經")___________________"
rgb_a="$rgb_d"
else
echoRgb "$name1[$name2] 不在安裝列表,備份個寂寞?" "0"
fi
if [[ $i = $r ]]; then
endtime 1 "應用備份" "3"
#設置無障礙開關
if [[ $var != "" ]]; then
if [[ $var != null ]]; then
settings put secure enabled_accessibility_services "$var" &>/dev/null
echo_log "設置無障礙"
settings put secure accessibility_enabled 1 &>/dev/null
echo_log "打開無障礙開關"
fi
fi
#設置鍵盤
if [[ $keyboard != "" ]]; then
ime enable "$keyboard" &>/dev/null
ime set "$keyboard" &>/dev/null
settings put secure default_input_method "$keyboard" &>/dev/null
echo_log "設置鍵盤$(appinfo2 "${keyboard%/*}" 2>/dev/null)"
fi
update_apk2="${update_apk2:="暫無更新"}"
add_app2="${add_app2:="暫無更新"}"
echoRgb "\n -已更新的apk=\"$osn\"\n -已新增的備份=\"$osk\"\n -apk版本號無變化=\"$osj\"\n -下列為版本號已變更的應用\n$update_apk2\n -新增的備份....\n$add_app2\n -包含SSAID的應用\n$SSAID_apk2" "3"
notification "101" "app備份完成 $(endtime 1 "應用備份" "3")"
[[ $txt2 != "" ]] && {
echo "$txt2" | sort | sed '/^$/d'>"$txt_path2"
}
if [[ $backup_media = true && ! -f ${0%/*}/app_details.json ]]; then
A=1
B="$(echo "$Custom_path" | egrep -v '#|' | awk 'NF != 0 { count++ } END { print count }')"
if [[ $B != "" ]]; then
echoRgb "備份結束,備份多媒體" "1"
notification "102" "Media備份開始"
starttime1="$(date -u "+%s")"
Backup_folder="$Backup/Media"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
app_details="$Backup_folder/app_details.json"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
mediatxt="$Backup/mediaList.txt"
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
while read; do
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))" "3"
notification "102" "備份第$A/$B個資料夾 剩下$((B - A))"
starttime2="$(date -u "+%s")"
if [[ ${REPLY##*/} = adb ]]; then
if [[ $ksu != ksu ]]; then
echoRgb "Magisk adb"
Backup_data "${REPLY##*/}" "$REPLY"
else
echoRgb "KernelSU adb不支持備份" "0"
Set_back_0
fi
else
Backup_data "${REPLY##*/}" "$REPLY"
fi
endtime 2 "${REPLY##*/}備份" "1"
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2"
rgb_d="$rgb_a"
rgb_a=188 # ANSI 256色: 淺灰色分隔線
echoRgb "_________________$(endtime 1 "已經")___________________"
rgb_a="$rgb_d" && A=$((A + 1))
done < "$TMPDIR/custom_paths"
echoRgb "目錄↓↓↓\n -$Backup_folder"
notification "102" "Media備份完成 $(endtime 1 "自定義備份")"
endtime 1 "自定義備份"
else
echoRgb "自定義路徑為空 無法備份" "0"
fi
fi
fi
i=$((i + 1)); en=$((en + 1)); nskg=$((nskg + 1))
done
}
# ===================== 子函數: 清理與統計 =====================
finalize_backup() {
backup_wifi "$Backup/wifi"
Set_screen_pause_seconds off
[[ $user != 0 ]] && am stop-user "$user"
Calculate_size "$Backup"
echoRgb "批量備份完成"
echoRgb "備份結束時間$(date +"%Y-%m-%d %H:%M:%S")"
starttime1="$TIME"
endtime 1 "批量備份開始到結束"
notification "105" "備份完成 $(endtime 1 "批量備份開始到結束")"
[[ -f $txt_path ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path"
[[ -f $txt_path2 ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path2"
exit 0
}
# ===================== 重構後的 backup() 入口 =====================
backup() {
validate_backup_params
prepare_app_list
init_backup_storage
backup_applications_loop
finalize_backup
}
backup_update_apk() {
Update_backup='true'
backup
}
backup_media() {
self_test
backup_path
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊start.sh選擇終止腳本,否則腳本將繼續執行直到結束" "0"
A=1
B="$(echo "$Custom_path" | egrep -v '#|' | awk 'NF != 0 { count++ } END { print count }')"
if [[ $B != "" ]]; then
starttime1="$(date -u "+%s")"
Backup_folder="$Backup/Media"
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
app_details="$Backup_folder/app_details.json"
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
filesize="$(find "$Backup_folder" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
mediatxt="$Backup/mediaList.txt"
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
Set_screen_pause_seconds on
notification "109" "Media備份開始"
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
while read; do
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))" "3"
starttime2="$(date -u "+%s")"
if [[ ${REPLY##*/} = adb ]]; then
if [[ $ksu != ksu ]]; then
echoRgb "Magisk adb"
Backup_data "${REPLY##*/}" "$REPLY"
fi
else
Backup_data "${REPLY##*/}" "$REPLY"
fi
endtime 2 "${REPLY##*/}備份" "1"
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2" && echoRgb "____________________________________" && A=$((A + 1))
done < "$TMPDIR/custom_paths"
Calculate_size "$Backup_folder"
Set_screen_pause_seconds off
endtime 1 "自定義備份"
notification "109" "Media備份完成 $(endtime 1 "自定義備份")"
else
echoRgb "自定義路徑為空 無法備份" "0"
fi
}
Set_screen_pause_seconds () {
if [[ $1 = on ]]; then
#獲取系統設置的無操作息屏秒數
if [[ $Get_dark_screen_seconds = "" ]]; then
Get_dark_screen_seconds="$(settings get system screen_off_timeout)"
#設置30分鐘後息屏
settings put system screen_off_timeout 1800000
echo_log "設置無操作息屏時間30分鐘"
fi
[[ $setDisplayPowerMode = true ]] && {
setDisplay 0
echo_log "設置螢幕狀態false"
}
elif [[ $1 = off ]]; then
if [[ $Get_dark_screen_seconds != "" ]]; then
settings put system screen_off_timeout "$Get_dark_screen_seconds"
echo_log "設置無操作息屏時間為$Get_dark_screen_seconds"
input keyevent 224
fi
[[ $setDisplayPowerMode = true ]] && {
setDisplay 2
echo_log "設置螢幕狀態true"
}
fi
}