#!/sbin/sh #初始化 OUTFD="$2" ZIPFILE="$3" Compression_method="zstd" #设置字符输出 ui_print() { echo "ui_print "$@"" > /proc/self/fd/$OUTFD } #设置工作目录 MODDIR=/dev/backup #安装二进制文件 rm -rf "$MODDIR" mkdir -p "$MODDIR" unzip -o "$ZIPFILE" 'tools/tools.sh' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/busybox' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/find' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/classes.dex' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/tar' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/zstd' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/bc' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/jq' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/keycheck' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/Device_List' -d "$MODDIR" unzip -o "$ZIPFILE" 'tools/soc.json' -d "$MODDIR" unzip -o "$ZIPFILE" 'restore_settings.conf' -d "$MODDIR" unzip -o "$ZIPFILE" '恢復自定義資料夾.sh' -d "$MODDIR" unzip -o "$ZIPFILE" '恢復備份.sh' -d "$MODDIR" unzip -o "$ZIPFILE" 'appList.txt' -d "$MODDIR" unzip -o "$ZIPFILE" '重新生成應用列表.sh' -d "$MODDIR" unzip -o "$ZIPFILE" '終止腳本.sh' -d "$MODDIR" unzip -o "$ZIPFILE" '壓縮檔完整性檢查.sh' -d "$MODDIR" unzip -o "$ZIPFILE" '轉換資料夾名稱.sh' -d "$MODDIR" unzip -o "$ZIPFILE" 'recover.sh' -d "$MODDIR" chmod -R 0755 "$MODDIR/tools/" busybox="$MODDIR/tools/busybox" "$busybox" --list | while read; do case $REPLY in tar|bc|find) ;; *) ln -s "$busybox" "$MODDIR/tools/$REPLY" ;; esac done chmod -R 0755 "$MODDIR/tools/" Set_back_0() { return 0 } Set_back_1() { return 1 } #设置环境 export PATH="$MODDIR/tools:$PATH" i=1 txt="$MODDIR/appList.txt" [[ ! -f $txt ]] && ui_print "$txt缺少" && exit 1 r="$(egrep -v '#|#' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')" [[ $r = "" ]] && ui_print "爬..appList.txt是空的備份個鬼" && exit 0 path="/data/media/0/Android" path2="/data/data" path3="/data/user_de/0" data="/data" ui_print "$(df -h "$data")" ui_print "$(df -h "$data" | awk 'END{print "剩餘:"$4"使用率:"$5}')" Backup="/data/media/0/Backup_$Compression_method" [[ ! -d $Backup ]] && mkdir -p "$Backup" || rm -rf "$Backup"/* echo "#不需要恢復還原的應用請在開頭注釋# 比如#xxxxxxxx 酷安" >"$Backup/appList.txt" cp -r "$MODDIR/tools" "$Backup" cp -r "$MODDIR/轉換資料夾名稱.sh" "$Backup/" cp -r "$MODDIR/restore_settings.conf" "$Backup/" cp -r "$MODDIR/壓縮檔完整性檢查.sh" "$Backup/" cp -r "$MODDIR/終止腳本.sh" "$Backup/" cp -r "$MODDIR/重新生成應用列表.sh" "$Backup/" cp -r "$MODDIR/恢復備份.sh" "$Backup/" cp -r "$MODDIR/恢復自定義資料夾.sh" "$Backup/" filesize="$(du -ks "$Backup" | awk '{print $1}')" #調用二進制 Quantity=0 #顯示執行結果 echo_log() { if [[ $? = 0 ]]; then ui_print "$1成功" && result=0 else ui_print "$1備份失敗,過世了" && result=1 fi } size() { local b_size get_size varr="$(echo "$1" | bc 2>/dev/null)" if [[ $varr != $1 ]]; then b_size="$(ls -l "$1" 2>/dev/null | awk '{print $5}')" else b_size="$1" fi if [[ $b_size -eq 0 ]]; then get_size="0 bytes" elif [[ $(echo "$b_size < 1024" | bc) -eq 1 ]]; then get_size="${b_size} bytes" elif [[ $(echo "$b_size < 1048576" | bc) -eq 1 ]]; then get_size="$(echo "scale=2; $b_size / 1024" | bc) KB" elif [[ $(echo "$b_size < 1073741824" | bc) -eq 1 ]]; then get_size="$(echo "scale=2; $b_size / 1048576" | bc) MB" else get_size="$(echo "scale=2; $b_size / 1073741824" | bc) GB" fi ui_print "$get_size" } Validation_file() { MODDIR_NAME="${1%/*}" MODDIR_NAME="${MODDIR_NAME##*/}" FILE_NAME="${1##*/}" ui_print "效驗$FILE_NAME" case ${FILE_NAME##*.} in zst) zstd -t "$1" 2>/dev/null ;; tar) tar -tf "$1" &>/dev/null ;; esac echo_log "效驗" "效驗壓縮檔" } #檢測apk狀態進行備份 Backup_apk() { #創建APP備份文件夾 [[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder" echo "{\n}">"$app_details" #備份apk ui_print "$1" rm -rf "$Backup_folder"/*.apk [[ $(cat "$Backup/appList.txt" | grep -v "#" | sed -e '/^$/d' | awk '{print $2}' | grep -w "^${name2}$" | head -1) = "" ]] && echo "$name1 $name2" >>"$Backup/appList.txt" unset Filesize Filesize="$(find "$apk_path" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')" [[ $lxj -ge 95 ]] && ui_print "$data空間不足,達到$lxj%" && exit 2 ( cd "$apk_path" case $Compression_method in 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 ui_print "新增app_details.json" extra_content="{ \"$name1\": { \"PackageName\": \"$name2\" } }" jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > temp.json && cp temp.json "$app_details" && rm -rf temp.json cp -r "$MODDIR/recover.sh" "$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)" ui_print "刪除文件:${OldFile%/*/*}" rm -rf "${OldFile%/*/*}" let "FileNum--" 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 [[ $name2 = bin.mt.plus && -f $apk_path/base.apk && ! -f $Backup/$name1.apk ]] && cp -r "$apk_path/base.apk" "$Backup/$name1.apk" unset PackageName ChineseName ; D=1 } #檢測數據位置進行備份 Backup_data() { unset zsize case $1 in user) data_path="$path2/$name2" ;; data) data_path="$path/$1/$name2" ;; obb) data_path="$path/$1/$name2" ;; user_de) data_path="$path3/$name2" ;; *) data_path="$2" if [[ $1 != storage-isolation && $1 != thanox ]]; then Compression_method1="$Compression_method" Compression_method=tar fi zsize=1 zmediapath=1 ;; esac if [[ -d $data_path ]]; then [[ $lxj -ge 95 ]] && ui_print "$data空間不足,達到$lxj%" && exit 2 unset Filesize Filesize="$(find "$data_path" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')" [[ $Filesize != "" ]] && { ui_print "備份$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 [[ $Start_backup = true ]] && { case $1 in user|user_de) case $Compression_method in 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##*/}/QQS" --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##*/}/QQS" --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 } || { ui_print "$1數據$Filesize2太小" && Set_back_1 } echo_log "備份$1數據" "備份" "$name1" if [[ $result = 0 ]]; then Validation_file "$Backup_folder/$1.tar"* if [[ $result = 0 ]]; then #[[ ${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\" } }" jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > temp.json && cp temp.json "$app_details" && rm -rf temp.json else extra_content="{ \"$1\": { \"Size\": \"$Filesize\" } }" jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > temp.json && cp temp.json "$app_details" && rm -rf temp.json fi else rm -rf "$Backup_folder/$1".tar.* fi fi [[ $Compression_method1 != "" ]] && Compression_method="$Compression_method1" unset Compression_method1 } || ui_print "$1資料夾是空的" "0" else [[ -f $data_path ]] && ui_print "$1是一個文件 不支持備份" || ui_print "$1數據不存在跳過備份" fi } endtime() { #計算總體切換時長耗費 case $1 in 1) starttime="$starttime1" ;; 2) starttime="$starttime2" ;; esac endtime="$(date -u "+%s")" duration="$(echo "$((endtime - starttime))" | awk '{t=split("60 秒 60 分 24 時 999 天",a);for(n=1;n/dev/null | wc -l)" if [[ $apk_number = 1 ]]; then Backup_apk "非Split Apk" else Backup_apk "Split Apk支持備份" fi if [[ $D != "" && $result = 0 && $No_backupdata = "" ]]; then #備份data數據 Backup_data "data" #備份obb數據 Backup_data "obb" #備份user數據 Backup_data "user" fi endtime 2 "$name1備份" lxj="$(echo "$Occupation_status" | awk '{print $3}' | sed 's/%//g')" ui_print "完成$((i*100/r))% $hx$(df -h "$data" | awk 'END{print "剩餘:"$4"使用率:"$5}')" else ui_print "$name1[$name2]不在安裝列表,備份個寂寞?" fi ui_print " " let i++ done ui_print "批量備份完成" filesizee="$(du -ks "$Backup" | awk '{print $1}')" dsize="$(($((filesizee - filesize)) / 1024))" ui_print "備份資料夾路徑:$Backup" ui_print "備份資料夾總體大小$(du -ksh "$Backup" | awk '{print $1}')" if [[ $dsize -gt 0 ]]; then if [[ $((dsize / 1024)) -gt 0 ]]; then ui_print "本次備份: $((dsize / 1024))gb" else ui_print "本次備份: ${dsize}mb" fi else ui_print "本次備份: $(($((filesizee - filesize)) * 1000 / 1024))kb" fi endtime 1 "批量備份開始到結束" chmod -R 0777 "$Backup/" rm -rf "$MODDIR" exit 0