#!/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" "$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)" "$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 }