mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 12:41:46 +00:00
Merge branch 'main' of github.com:tw93/Mole
This commit is contained in:
82
bin/clean.sh
82
bin/clean.sh
@@ -69,10 +69,10 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
fi
|
||||
|
||||
case "$line" in
|
||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /var/db/*)
|
||||
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||
continue
|
||||
;;
|
||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /var/db/*)
|
||||
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
duplicate="false"
|
||||
@@ -86,7 +86,7 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
fi
|
||||
[[ "$duplicate" == "true" ]] && continue
|
||||
WHITELIST_PATTERNS+=("$line")
|
||||
done <"$HOME/.config/mole/whitelist"
|
||||
done < "$HOME/.config/mole/whitelist"
|
||||
else
|
||||
WHITELIST_PATTERNS=("${DEFAULT_WHITELIST_PATTERNS[@]}")
|
||||
fi
|
||||
@@ -140,7 +140,7 @@ cleanup() {
|
||||
fi
|
||||
CLEANUP_DONE=true
|
||||
|
||||
stop_inline_spinner 2>/dev/null || true
|
||||
stop_inline_spinner 2> /dev/null || true
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
printf "\r\033[K" >&2 || true
|
||||
@@ -166,8 +166,8 @@ start_section() {
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
ensure_user_file "$EXPORT_LIST_FILE"
|
||||
echo "" >>"$EXPORT_LIST_FILE"
|
||||
echo "=== $1 ===" >>"$EXPORT_LIST_FILE"
|
||||
echo "" >> "$EXPORT_LIST_FILE"
|
||||
echo "=== $1 ===" >> "$EXPORT_LIST_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ normalize_paths_for_cleanup() {
|
||||
done
|
||||
fi
|
||||
[[ "$is_child" == "true" ]] || result_paths+=("$path")
|
||||
done <<<"$sorted_paths"
|
||||
done <<< "$sorted_paths"
|
||||
|
||||
if [[ ${#result_paths[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${result_paths[@]}"
|
||||
@@ -232,9 +232,9 @@ get_cleanup_path_size_kb() {
|
||||
local path="$1"
|
||||
|
||||
if [[ -f "$path" && ! -L "$path" ]]; then
|
||||
if command -v stat >/dev/null 2>&1; then
|
||||
if command -v stat > /dev/null 2>&1; then
|
||||
local bytes
|
||||
bytes=$(stat -f%z "$path" 2>/dev/null || echo "0")
|
||||
bytes=$(stat -f%z "$path" 2> /dev/null || echo "0")
|
||||
if [[ "$bytes" =~ ^[0-9]+$ && "$bytes" -gt 0 ]]; then
|
||||
echo $(((bytes + 1023) / 1024))
|
||||
return 0
|
||||
@@ -243,9 +243,9 @@ get_cleanup_path_size_kb() {
|
||||
fi
|
||||
|
||||
if [[ -L "$path" ]]; then
|
||||
if command -v stat >/dev/null 2>&1; then
|
||||
if command -v stat > /dev/null 2>&1; then
|
||||
local bytes
|
||||
bytes=$(stat -f%z "$path" 2>/dev/null || echo "0")
|
||||
bytes=$(stat -f%z "$path" 2> /dev/null || echo "0")
|
||||
if [[ "$bytes" =~ ^[0-9]+$ && "$bytes" -gt 0 ]]; then
|
||||
echo $(((bytes + 1023) / 1024))
|
||||
else
|
||||
@@ -461,9 +461,9 @@ safe_clean() {
|
||||
[[ ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||
|
||||
if [[ "$size" -gt 0 ]]; then
|
||||
echo "$size 1" >"$temp_dir/result_${idx}"
|
||||
echo "$size 1" > "$temp_dir/result_${idx}"
|
||||
else
|
||||
echo "0 0" >"$temp_dir/result_${idx}"
|
||||
echo "0 0" > "$temp_dir/result_${idx}"
|
||||
fi
|
||||
|
||||
((idx++))
|
||||
@@ -488,17 +488,17 @@ safe_clean() {
|
||||
[[ ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||
local tmp_file="$temp_dir/result_${idx}.$$"
|
||||
if [[ "$size" -gt 0 ]]; then
|
||||
echo "$size 1" >"$tmp_file"
|
||||
echo "$size 1" > "$tmp_file"
|
||||
else
|
||||
echo "0 0" >"$tmp_file"
|
||||
echo "0 0" > "$tmp_file"
|
||||
fi
|
||||
mv "$tmp_file" "$temp_dir/result_${idx}" 2>/dev/null || true
|
||||
mv "$tmp_file" "$temp_dir/result_${idx}" 2> /dev/null || true
|
||||
) &
|
||||
pids+=($!)
|
||||
((idx++))
|
||||
|
||||
if ((${#pids[@]} >= MOLE_MAX_PARALLEL_JOBS)); then
|
||||
wait "${pids[0]}" 2>/dev/null || true
|
||||
wait "${pids[0]}" 2> /dev/null || true
|
||||
pids=("${pids[@]:1}")
|
||||
((completed++))
|
||||
|
||||
@@ -511,7 +511,7 @@ safe_clean() {
|
||||
|
||||
if [[ ${#pids[@]} -gt 0 ]]; then
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2> /dev/null || true
|
||||
((completed++))
|
||||
|
||||
if [[ "$show_spinner" == "true" && -t 1 ]]; then
|
||||
@@ -527,11 +527,11 @@ safe_clean() {
|
||||
for path in "${existing_paths[@]}"; do
|
||||
local result_file="$temp_dir/result_${idx}"
|
||||
if [[ -f "$result_file" ]]; then
|
||||
read -r size count <"$result_file" 2>/dev/null || true
|
||||
read -r size count < "$result_file" 2> /dev/null || true
|
||||
local removed=0
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2>/dev/null && removed=1
|
||||
rm "$path" 2> /dev/null && removed=1
|
||||
else
|
||||
if safe_remove "$path" true; then
|
||||
removed=1
|
||||
@@ -568,7 +568,7 @@ safe_clean() {
|
||||
local removed=0
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
if [[ -L "$path" ]]; then
|
||||
rm "$path" 2>/dev/null && removed=1
|
||||
rm "$path" 2> /dev/null && removed=1
|
||||
else
|
||||
if safe_remove "$path" true; then
|
||||
removed=1
|
||||
@@ -629,9 +629,9 @@ safe_clean() {
|
||||
local size=0
|
||||
|
||||
if [[ -n "${temp_dir:-}" && -f "$temp_dir/result_${idx}" ]]; then
|
||||
read -r size count <"$temp_dir/result_${idx}" 2>/dev/null || true
|
||||
read -r size count < "$temp_dir/result_${idx}" 2> /dev/null || true
|
||||
else
|
||||
size=$(get_cleanup_path_size_kb "$path" 2>/dev/null || echo "0")
|
||||
size=$(get_cleanup_path_size_kb "$path" 2> /dev/null || echo "0")
|
||||
fi
|
||||
|
||||
[[ "$size" == "0" || -z "$size" ]] && {
|
||||
@@ -639,7 +639,7 @@ safe_clean() {
|
||||
continue
|
||||
}
|
||||
|
||||
echo "$(dirname "$path")|$size|$path" >>"$paths_temp"
|
||||
echo "$(dirname "$path")|$size|$path" >> "$paths_temp"
|
||||
((idx++))
|
||||
done
|
||||
fi
|
||||
@@ -670,9 +670,9 @@ safe_clean() {
|
||||
' | while IFS='|' read -r display_path total_size child_count; do
|
||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
if [[ $child_count -gt 1 ]]; then
|
||||
echo "$display_path # $size_human ($child_count items)" >>"$EXPORT_LIST_FILE"
|
||||
echo "$display_path # $size_human ($child_count items)" >> "$EXPORT_LIST_FILE"
|
||||
else
|
||||
echo "$display_path # $size_human" >>"$EXPORT_LIST_FILE"
|
||||
echo "$display_path # $size_human" >> "$EXPORT_LIST_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -708,7 +708,7 @@ start_cleanup() {
|
||||
SYSTEM_CLEAN=false
|
||||
|
||||
ensure_user_file "$EXPORT_LIST_FILE"
|
||||
cat >"$EXPORT_LIST_FILE" <<EOF
|
||||
cat > "$EXPORT_LIST_FILE" << EOF
|
||||
# Mole Cleanup Preview - $(date '+%Y-%m-%d %H:%M:%S')
|
||||
#
|
||||
# How to protect files:
|
||||
@@ -998,7 +998,7 @@ perform_cleanup() {
|
||||
echo "# Potential cleanup: ${freed_gb}GB"
|
||||
echo "# Items: $files_cleaned"
|
||||
echo "# Categories: $total_items"
|
||||
} >>"$EXPORT_LIST_FILE"
|
||||
} >> "$EXPORT_LIST_FILE"
|
||||
|
||||
summary_details+=("Detailed file list: ${GRAY}$EXPORT_LIST_FILE${NC}")
|
||||
summary_details+=("Use ${GRAY}mo clean --whitelist${NC} to add protection rules")
|
||||
@@ -1047,17 +1047,17 @@ perform_cleanup() {
|
||||
main() {
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run" | "-n")
|
||||
DRY_RUN=true
|
||||
;;
|
||||
"--whitelist")
|
||||
source "$SCRIPT_DIR/../lib/manage/whitelist.sh"
|
||||
manage_whitelist "clean"
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run" | "-n")
|
||||
DRY_RUN=true
|
||||
;;
|
||||
"--whitelist")
|
||||
source "$SCRIPT_DIR/../lib/manage/whitelist.sh"
|
||||
manage_whitelist "clean"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ clean_deep_system() {
|
||||
continue
|
||||
fi
|
||||
local item_flags
|
||||
item_flags=$($STAT_BSD -f%Sf "$item" 2>/dev/null || echo "")
|
||||
item_flags=$($STAT_BSD -f%Sf "$item" 2> /dev/null || echo "")
|
||||
if [[ "$item_flags" == *"restricted"* ]]; then
|
||||
continue
|
||||
fi
|
||||
if safe_sudo_remove "$item"; then
|
||||
((updates_cleaned++))
|
||||
fi
|
||||
done < <(find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2>/dev/null || true)
|
||||
done < <(find /Library/Updates -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
[[ $updates_cleaned -gt 0 ]] && log_success "System library updates"
|
||||
fi
|
||||
fi
|
||||
@@ -76,7 +76,7 @@ clean_deep_system() {
|
||||
last_update_time=$current_time
|
||||
fi
|
||||
fi
|
||||
done < <(run_with_timeout 5 command find /private/var/folders -type d -name "*.code_sign_clone" -path "*/X/*" -print0 2>/dev/null || true)
|
||||
done < <(run_with_timeout 5 command find /private/var/folders -type d -name "*.code_sign_clone" -path "*/X/*" -print0 2> /dev/null || true)
|
||||
stop_section_spinner
|
||||
[[ $code_sign_cleaned -gt 0 ]] && log_success "Browser code signature caches ($code_sign_cleaned items)"
|
||||
|
||||
@@ -101,7 +101,7 @@ clean_deep_system() {
|
||||
# Incomplete Time Machine backups.
|
||||
clean_time_machine_failed_backups() {
|
||||
local tm_cleaned=0
|
||||
if ! command -v tmutil >/dev/null 2>&1; then
|
||||
if ! command -v tmutil > /dev/null 2>&1; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} No incomplete backups found"
|
||||
return 0
|
||||
fi
|
||||
@@ -155,9 +155,9 @@ clean_time_machine_failed_backups() {
|
||||
fi
|
||||
for volume in "${backup_volumes[@]}"; do
|
||||
local fs_type
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2>/dev/null | tail -1 | awk '{print $2}' || echo "unknown")
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}' || echo "unknown")
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav | unknown) continue ;;
|
||||
nfs | smbfs | afpfs | cifs | webdav | unknown) continue ;;
|
||||
esac
|
||||
local backupdb_dir="$volume/Backups.backupdb"
|
||||
if [[ -d "$backupdb_dir" ]]; then
|
||||
@@ -185,11 +185,11 @@ clean_time_machine_failed_backups() {
|
||||
note_activity
|
||||
continue
|
||||
fi
|
||||
if ! command -v tmutil >/dev/null 2>&1; then
|
||||
if ! command -v tmutil > /dev/null 2>&1; then
|
||||
echo -e " ${YELLOW}!${NC} tmutil not available, skipping: $backup_name"
|
||||
continue
|
||||
fi
|
||||
if tmutil delete "$inprogress_file" 2>/dev/null; then
|
||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete backup: $backup_name ${GREEN}($size_human)${NC}"
|
||||
((tm_cleaned++))
|
||||
((files_cleaned++))
|
||||
@@ -199,14 +199,14 @@ clean_time_machine_failed_backups() {
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} Could not delete: $backup_name · try manually with sudo"
|
||||
fi
|
||||
done < <(run_with_timeout 15 find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2>/dev/null || true)
|
||||
done < <(run_with_timeout 15 find "$backupdb_dir" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
fi
|
||||
# APFS bundles.
|
||||
for bundle in "$volume"/*.backupbundle "$volume"/*.sparsebundle; do
|
||||
[[ -e "$bundle" ]] || continue
|
||||
[[ -d "$bundle" ]] || continue
|
||||
local bundle_name=$(basename "$bundle")
|
||||
local mounted_path=$(hdiutil info 2>/dev/null | grep -A 5 "image-path.*$bundle_name" | grep "/Volumes/" | awk '{print $1}' | head -1 || echo "")
|
||||
local mounted_path=$(hdiutil info 2> /dev/null | grep -A 5 "image-path.*$bundle_name" | grep "/Volumes/" | awk '{print $1}' | head -1 || echo "")
|
||||
if [[ -n "$mounted_path" && -d "$mounted_path" ]]; then
|
||||
while IFS= read -r inprogress_file; do
|
||||
[[ -d "$inprogress_file" ]] || continue
|
||||
@@ -231,10 +231,10 @@ clean_time_machine_failed_backups() {
|
||||
note_activity
|
||||
continue
|
||||
fi
|
||||
if ! command -v tmutil >/dev/null 2>&1; then
|
||||
if ! command -v tmutil > /dev/null 2>&1; then
|
||||
continue
|
||||
fi
|
||||
if tmutil delete "$inprogress_file" 2>/dev/null; then
|
||||
if tmutil delete "$inprogress_file" 2> /dev/null; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Incomplete APFS backup in $bundle_name: $backup_name ${GREEN}($size_human)${NC}"
|
||||
((tm_cleaned++))
|
||||
((files_cleaned++))
|
||||
@@ -244,7 +244,7 @@ clean_time_machine_failed_backups() {
|
||||
else
|
||||
echo -e " ${YELLOW}!${NC} Could not delete from bundle: $backup_name"
|
||||
fi
|
||||
done < <(run_with_timeout 15 find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2>/dev/null || true)
|
||||
done < <(run_with_timeout 15 find "$mounted_path" -maxdepth 3 -type d \( -name "*.inProgress" -o -name "*.inprogress" \) 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
done
|
||||
@@ -260,20 +260,20 @@ clean_time_machine_failed_backups() {
|
||||
# Returns 2 if status cannot be determined
|
||||
tm_is_running() {
|
||||
local st
|
||||
st="$(tmutil status 2>/dev/null)" || return 2
|
||||
st="$(tmutil status 2> /dev/null)" || return 2
|
||||
|
||||
# If we can't find a Running field at all, treat as unknown.
|
||||
if ! grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=' <<<"$st"; then
|
||||
if ! grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=' <<< "$st"; then
|
||||
return 2
|
||||
fi
|
||||
|
||||
# Match: Running = 1; OR "Running" = 1 (with or without trailing ;)
|
||||
grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=[[:space:]]*1([[:space:]]*;|$)' <<<"$st"
|
||||
grep -qE '(^|[[:space:]])("Running"|Running)[[:space:]]*=[[:space:]]*1([[:space:]]*;|$)' <<< "$st"
|
||||
}
|
||||
|
||||
# Local APFS snapshots (keep the most recent).
|
||||
clean_local_snapshots() {
|
||||
if ! command -v tmutil >/dev/null 2>&1; then
|
||||
if ! command -v tmutil > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -292,7 +292,7 @@ clean_local_snapshots() {
|
||||
|
||||
start_section_spinner "Checking local snapshots..."
|
||||
local snapshot_list
|
||||
snapshot_list=$(tmutil listlocalsnapshots / 2>/dev/null)
|
||||
snapshot_list=$(tmutil listlocalsnapshots / 2> /dev/null)
|
||||
stop_section_spinner
|
||||
[[ -z "$snapshot_list" ]] && return 0
|
||||
local cleaned_count=0
|
||||
@@ -305,14 +305,14 @@ clean_local_snapshots() {
|
||||
local snap_name="${BASH_REMATCH[0]}"
|
||||
snapshots+=("$snap_name")
|
||||
local date_str="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]} ${BASH_REMATCH[4]:0:2}:${BASH_REMATCH[4]:2:2}:${BASH_REMATCH[4]:4:2}"
|
||||
local snap_ts=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_str" "+%s" 2>/dev/null || echo "0")
|
||||
local snap_ts=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_str" "+%s" 2> /dev/null || echo "0")
|
||||
[[ "$snap_ts" == "0" ]] && continue
|
||||
if [[ "$snap_ts" -gt "$newest_ts" ]]; then
|
||||
newest_ts="$snap_ts"
|
||||
newest_name="$snap_name"
|
||||
fi
|
||||
fi
|
||||
done <<<"$snapshot_list"
|
||||
done <<< "$snapshot_list"
|
||||
|
||||
[[ ${#snapshots[@]} -eq 0 ]] && return 0
|
||||
[[ -z "$newest_name" ]] && return 0
|
||||
@@ -331,7 +331,7 @@ clean_local_snapshots() {
|
||||
echo -e " ${GRAY}The most recent snapshot will be kept.${NC}"
|
||||
echo -ne " ${PURPLE}${ICON_ARROW}${NC} Remove all local snapshots except the most recent one? ${GREEN}Enter${NC} continue, ${GRAY}Space${NC} skip: "
|
||||
local choice
|
||||
if type read_key >/dev/null 2>&1; then
|
||||
if type read_key > /dev/null 2>&1; then
|
||||
choice=$(read_key)
|
||||
else
|
||||
IFS= read -r -s -n 1 choice || choice=""
|
||||
@@ -356,7 +356,7 @@ clean_local_snapshots() {
|
||||
((cleaned_count++))
|
||||
note_activity
|
||||
else
|
||||
if sudo tmutil deletelocalsnapshots "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}" >/dev/null 2>&1; then
|
||||
if sudo tmutil deletelocalsnapshots "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}" > /dev/null 2>&1; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Removed snapshot: $snap_name"
|
||||
((cleaned_count++))
|
||||
note_activity
|
||||
|
||||
@@ -20,7 +20,7 @@ clean_chrome_old_versions() {
|
||||
)
|
||||
|
||||
# Match the exact Chrome process name to avoid false positives
|
||||
if pgrep -x "Google Chrome" >/dev/null 2>&1; then
|
||||
if pgrep -x "Google Chrome" > /dev/null 2>&1; then
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Google Chrome running · old versions cleanup skipped"
|
||||
return 0
|
||||
fi
|
||||
@@ -39,7 +39,7 @@ clean_chrome_old_versions() {
|
||||
[[ -L "$current_link" ]] || continue
|
||||
|
||||
local current_version
|
||||
current_version=$(readlink "$current_link" 2>/dev/null || true)
|
||||
current_version=$(readlink "$current_link" 2> /dev/null || true)
|
||||
current_version="${current_version##*/}"
|
||||
[[ -n "$current_version" ]] || continue
|
||||
|
||||
@@ -69,9 +69,9 @@ clean_chrome_old_versions() {
|
||||
cleaned_any=true
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
if has_sudo_session; then
|
||||
safe_sudo_remove "$dir" >/dev/null 2>&1 || true
|
||||
safe_sudo_remove "$dir" > /dev/null 2>&1 || true
|
||||
else
|
||||
safe_remove "$dir" true >/dev/null 2>&1 || true
|
||||
safe_remove "$dir" true > /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -100,7 +100,7 @@ clean_edge_old_versions() {
|
||||
)
|
||||
|
||||
# Match the exact Edge process name to avoid false positives (e.g., Microsoft Teams)
|
||||
if pgrep -x "Microsoft Edge" >/dev/null 2>&1; then
|
||||
if pgrep -x "Microsoft Edge" > /dev/null 2>&1; then
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Microsoft Edge running · old versions cleanup skipped"
|
||||
return 0
|
||||
fi
|
||||
@@ -119,7 +119,7 @@ clean_edge_old_versions() {
|
||||
[[ -L "$current_link" ]] || continue
|
||||
|
||||
local current_version
|
||||
current_version=$(readlink "$current_link" 2>/dev/null || true)
|
||||
current_version=$(readlink "$current_link" 2> /dev/null || true)
|
||||
current_version="${current_version##*/}"
|
||||
[[ -n "$current_version" ]] || continue
|
||||
|
||||
@@ -149,9 +149,9 @@ clean_edge_old_versions() {
|
||||
cleaned_any=true
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
if has_sudo_session; then
|
||||
safe_sudo_remove "$dir" >/dev/null 2>&1 || true
|
||||
safe_sudo_remove "$dir" > /dev/null 2>&1 || true
|
||||
else
|
||||
safe_remove "$dir" true >/dev/null 2>&1 || true
|
||||
safe_remove "$dir" true > /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -177,7 +177,7 @@ clean_edge_updater_old_versions() {
|
||||
local updater_dir="$HOME/Library/Application Support/Microsoft/EdgeUpdater/apps/msedge-stable"
|
||||
[[ -d "$updater_dir" ]] || return 0
|
||||
|
||||
if pgrep -x "Microsoft Edge" >/dev/null 2>&1; then
|
||||
if pgrep -x "Microsoft Edge" > /dev/null 2>&1; then
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Microsoft Edge running · updater cleanup skipped"
|
||||
return 0
|
||||
fi
|
||||
@@ -215,7 +215,7 @@ clean_edge_updater_old_versions() {
|
||||
((cleaned_count++))
|
||||
cleaned_any=true
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
safe_remove "$dir" true >/dev/null 2>&1 || true
|
||||
safe_remove "$dir" true > /dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -242,20 +242,20 @@ scan_external_volumes() {
|
||||
[[ -d "$volume" && -w "$volume" && ! -L "$volume" ]] || continue
|
||||
[[ "$volume" == "/" || "$volume" == "/Volumes/Macintosh HD" ]] && continue
|
||||
local protocol=""
|
||||
protocol=$(run_with_timeout 1 command diskutil info "$volume" 2>/dev/null | grep -i "Protocol:" | awk '{print $2}' || echo "")
|
||||
protocol=$(run_with_timeout 1 command diskutil info "$volume" 2> /dev/null | grep -i "Protocol:" | awk '{print $2}' || echo "")
|
||||
case "$protocol" in
|
||||
SMB | NFS | AFP | CIFS | WebDAV)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
SMB | NFS | AFP | CIFS | WebDAV)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
local fs_type=""
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2>/dev/null | tail -1 | awk '{print $2}' || echo "")
|
||||
fs_type=$(run_with_timeout 1 command df -T "$volume" 2> /dev/null | tail -1 | awk '{print $2}' || echo "")
|
||||
case "$fs_type" in
|
||||
nfs | smbfs | afpfs | cifs | webdav)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
nfs | smbfs | afpfs | cifs | webdav)
|
||||
network_volumes+=("$volume")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
candidate_volumes+=("$volume")
|
||||
done
|
||||
@@ -275,7 +275,7 @@ scan_external_volumes() {
|
||||
if [[ -d "$volume_trash" && "$DRY_RUN" != "true" ]] && ! is_path_whitelisted "$volume_trash"; then
|
||||
while IFS= read -r -d '' item; do
|
||||
safe_remove "$item" true || true
|
||||
done < <(command find "$volume_trash" -mindepth 1 -maxdepth 1 -print0 2>/dev/null || true)
|
||||
done < <(command find "$volume_trash" -mindepth 1 -maxdepth 1 -print0 2> /dev/null || true)
|
||||
fi
|
||||
if [[ "$PROTECT_FINDER_METADATA" != "true" ]]; then
|
||||
clean_ds_store_tree "$volume" "$(basename "$volume") volume (.DS_Store)"
|
||||
@@ -362,7 +362,7 @@ clean_mail_downloads() {
|
||||
((cleaned_kb += file_size_kb))
|
||||
fi
|
||||
fi
|
||||
done < <(command find "$target_path" -type f -mtime +"$mail_age_days" -print0 2>/dev/null || true)
|
||||
done < <(command find "$target_path" -type f -mtime +"$mail_age_days" -print0 2> /dev/null || true)
|
||||
fi
|
||||
done
|
||||
if [[ $count -gt 0 ]]; then
|
||||
@@ -420,7 +420,7 @@ process_container_cache() {
|
||||
local cache_dir="$container_dir/Data/Library/Caches"
|
||||
[[ -d "$cache_dir" ]] || return 0
|
||||
# Fast non-empty check.
|
||||
if find "$cache_dir" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q .; then
|
||||
if find "$cache_dir" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$cache_dir")
|
||||
((total_size += size))
|
||||
found_any=true
|
||||
@@ -451,7 +451,7 @@ clean_browsers() {
|
||||
safe_clean ~/Library/Caches/company.thebrowser.dia/* "Dia cache"
|
||||
safe_clean ~/Library/Caches/BraveSoftware/Brave-Browser/* "Brave cache"
|
||||
local firefox_running=false
|
||||
if pgrep -x "Firefox" >/dev/null 2>&1; then
|
||||
if pgrep -x "Firefox" > /dev/null 2>&1; then
|
||||
firefox_running=true
|
||||
fi
|
||||
if [[ "$firefox_running" == "true" ]]; then
|
||||
@@ -504,7 +504,7 @@ clean_virtualization_tools() {
|
||||
}
|
||||
# Application Support logs/caches.
|
||||
clean_application_support_logs() {
|
||||
if [[ ! -d "$HOME/Library/Application Support" ]] || ! ls "$HOME/Library/Application Support" >/dev/null 2>&1; then
|
||||
if [[ ! -d "$HOME/Library/Application Support" ]] || ! ls "$HOME/Library/Application Support" > /dev/null 2>&1; then
|
||||
note_activity
|
||||
echo -e " ${YELLOW}${ICON_WARNING}${NC} Skipped: No permission to access Application Support"
|
||||
return 0
|
||||
@@ -536,7 +536,7 @@ clean_application_support_logs() {
|
||||
local -a start_candidates=("$app_dir/log" "$app_dir/logs" "$app_dir/activitylog" "$app_dir/Cache/Cache_Data" "$app_dir/Crashpad/completed")
|
||||
for candidate in "${start_candidates[@]}"; do
|
||||
if [[ -d "$candidate" ]]; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q .; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$candidate")
|
||||
((total_size += size))
|
||||
((cleaned_count++))
|
||||
@@ -544,7 +544,7 @@ clean_application_support_logs() {
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
for item in "$candidate"/*; do
|
||||
[[ -e "$item" ]] || continue
|
||||
safe_remove "$item" true >/dev/null 2>&1 || true
|
||||
safe_remove "$item" true > /dev/null 2>&1 || true
|
||||
done
|
||||
fi
|
||||
fi
|
||||
@@ -560,7 +560,7 @@ clean_application_support_logs() {
|
||||
local -a gc_candidates=("$container_path/Logs" "$container_path/Library/Logs")
|
||||
for candidate in "${gc_candidates[@]}"; do
|
||||
if [[ -d "$candidate" ]]; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q .; then
|
||||
if find "$candidate" -mindepth 1 -maxdepth 1 -print -quit 2> /dev/null | grep -q .; then
|
||||
local size=$(get_path_size_kb "$candidate")
|
||||
((total_size += size))
|
||||
((cleaned_count++))
|
||||
@@ -568,7 +568,7 @@ clean_application_support_logs() {
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
for item in "$candidate"/*; do
|
||||
[[ -e "$item" ]] || continue
|
||||
safe_remove "$item" true >/dev/null 2>&1 || true
|
||||
safe_remove "$item" true > /dev/null 2>&1 || true
|
||||
done
|
||||
fi
|
||||
fi
|
||||
@@ -597,7 +597,7 @@ check_ios_device_backups() {
|
||||
if [[ -d "$backup_dir" ]]; then
|
||||
local backup_kb=$(get_path_size_kb "$backup_dir")
|
||||
if [[ -n "${backup_kb:-}" && "$backup_kb" -gt 102400 ]]; then
|
||||
local backup_human=$(command du -sh "$backup_dir" 2>/dev/null | awk '{print $1}')
|
||||
local backup_human=$(command du -sh "$backup_dir" 2> /dev/null | awk '{print $1}')
|
||||
if [[ -n "$backup_human" ]]; then
|
||||
note_activity
|
||||
echo -e " Found ${GREEN}${backup_human}${NC} iOS backups"
|
||||
|
||||
200
lib/core/ui.sh
200
lib/core/ui.sh
@@ -168,47 +168,47 @@ read_key() {
|
||||
return 0
|
||||
}
|
||||
case "$key" in
|
||||
$'\n' | $'\r') echo "ENTER" ;;
|
||||
$'\x7f' | $'\x08') echo "DELETE" ;;
|
||||
$'\x1b')
|
||||
# Check if this is an escape sequence (arrow keys) or ESC key
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest 2>/dev/null; then
|
||||
if [[ "$rest" == "[" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest2 2>/dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;;
|
||||
"B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;;
|
||||
"D") echo "LEFT" ;;
|
||||
"3")
|
||||
IFS= read -r -s -n 1 -t 0.1 rest3 2>/dev/null
|
||||
[[ "$rest3" == "~" ]] && echo "DELETE" || echo "OTHER"
|
||||
;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "QUIT"; fi
|
||||
elif [[ "$rest" == "O" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest2 2>/dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;;
|
||||
"B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;;
|
||||
"D") echo "LEFT" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "OTHER"; fi
|
||||
$'\n' | $'\r') echo "ENTER" ;;
|
||||
$'\x7f' | $'\x08') echo "DELETE" ;;
|
||||
$'\x1b')
|
||||
# Check if this is an escape sequence (arrow keys) or ESC key
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest 2> /dev/null; then
|
||||
if [[ "$rest" == "[" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest2 2> /dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;;
|
||||
"B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;;
|
||||
"D") echo "LEFT" ;;
|
||||
"3")
|
||||
IFS= read -r -s -n 1 -t 0.1 rest3 2> /dev/null
|
||||
[[ "$rest3" == "~" ]] && echo "DELETE" || echo "OTHER"
|
||||
;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "QUIT"; fi
|
||||
elif [[ "$rest" == "O" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 0.1 rest2 2> /dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;;
|
||||
"B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;;
|
||||
"D") echo "LEFT" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "OTHER"; fi
|
||||
else
|
||||
# Not an escape sequence, it's ESC key
|
||||
echo "QUIT"
|
||||
fi
|
||||
else
|
||||
# Not an escape sequence, it's ESC key
|
||||
# No following characters, it's ESC key
|
||||
echo "QUIT"
|
||||
fi
|
||||
else
|
||||
# No following characters, it's ESC key
|
||||
echo "QUIT"
|
||||
fi
|
||||
;;
|
||||
' ') echo "SPACE" ;; # Allow space in filter mode for selection
|
||||
[[:print:]]) echo "CHAR:$key" ;;
|
||||
*) echo "OTHER" ;;
|
||||
;;
|
||||
' ') echo "SPACE" ;; # Allow space in filter mode for selection
|
||||
[[:print:]]) echo "CHAR:$key" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
return 0
|
||||
fi
|
||||
@@ -218,53 +218,53 @@ read_key() {
|
||||
return 0
|
||||
}
|
||||
case "$key" in
|
||||
$'\n' | $'\r') echo "ENTER" ;;
|
||||
' ') echo "SPACE" ;;
|
||||
'/') echo "FILTER" ;;
|
||||
'q' | 'Q') echo "QUIT" ;;
|
||||
'R') echo "RETRY" ;;
|
||||
'm' | 'M') echo "MORE" ;;
|
||||
'u' | 'U') echo "UPDATE" ;;
|
||||
't' | 'T') echo "TOUCHID" ;;
|
||||
'j' | 'J') echo "DOWN" ;;
|
||||
'k' | 'K') echo "UP" ;;
|
||||
'h' | 'H') echo "LEFT" ;;
|
||||
'l' | 'L') echo "RIGHT" ;;
|
||||
$'\x03') echo "QUIT" ;;
|
||||
$'\x7f' | $'\x08') echo "DELETE" ;;
|
||||
$'\x1b')
|
||||
if IFS= read -r -s -n 1 -t 1 rest 2>/dev/null; then
|
||||
if [[ "$rest" == "[" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 1 rest2 2>/dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;; "B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;; "D") echo "LEFT" ;;
|
||||
"3")
|
||||
IFS= read -r -s -n 1 -t 1 rest3 2>/dev/null
|
||||
[[ "$rest3" == "~" ]] && echo "DELETE" || echo "OTHER"
|
||||
;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "QUIT"; fi
|
||||
elif [[ "$rest" == "O" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 1 rest2 2>/dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;; "B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;; "D") echo "LEFT" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
$'\n' | $'\r') echo "ENTER" ;;
|
||||
' ') echo "SPACE" ;;
|
||||
'/') echo "FILTER" ;;
|
||||
'q' | 'Q') echo "QUIT" ;;
|
||||
'R') echo "RETRY" ;;
|
||||
'm' | 'M') echo "MORE" ;;
|
||||
'u' | 'U') echo "UPDATE" ;;
|
||||
't' | 'T') echo "TOUCHID" ;;
|
||||
'j' | 'J') echo "DOWN" ;;
|
||||
'k' | 'K') echo "UP" ;;
|
||||
'h' | 'H') echo "LEFT" ;;
|
||||
'l' | 'L') echo "RIGHT" ;;
|
||||
$'\x03') echo "QUIT" ;;
|
||||
$'\x7f' | $'\x08') echo "DELETE" ;;
|
||||
$'\x1b')
|
||||
if IFS= read -r -s -n 1 -t 1 rest 2> /dev/null; then
|
||||
if [[ "$rest" == "[" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 1 rest2 2> /dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;; "B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;; "D") echo "LEFT" ;;
|
||||
"3")
|
||||
IFS= read -r -s -n 1 -t 1 rest3 2> /dev/null
|
||||
[[ "$rest3" == "~" ]] && echo "DELETE" || echo "OTHER"
|
||||
;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "QUIT"; fi
|
||||
elif [[ "$rest" == "O" ]]; then
|
||||
if IFS= read -r -s -n 1 -t 1 rest2 2> /dev/null; then
|
||||
case "$rest2" in
|
||||
"A") echo "UP" ;; "B") echo "DOWN" ;;
|
||||
"C") echo "RIGHT" ;; "D") echo "LEFT" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
else echo "OTHER"; fi
|
||||
else echo "OTHER"; fi
|
||||
else echo "OTHER"; fi
|
||||
else echo "QUIT"; fi
|
||||
;;
|
||||
[[:print:]]) echo "CHAR:$key" ;;
|
||||
*) echo "OTHER" ;;
|
||||
else echo "QUIT"; fi
|
||||
;;
|
||||
[[:print:]]) echo "CHAR:$key" ;;
|
||||
*) echo "OTHER" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
drain_pending_input() {
|
||||
local drained=0
|
||||
while IFS= read -r -s -n 1 -t 0.01 _ 2>/dev/null; do
|
||||
while IFS= read -r -s -n 1 -t 0.01 _ 2> /dev/null; do
|
||||
((drained++))
|
||||
[[ $drained -gt 100 ]] && break
|
||||
done
|
||||
@@ -288,7 +288,7 @@ INLINE_SPINNER_PID=""
|
||||
INLINE_SPINNER_STOP_FILE=""
|
||||
|
||||
start_inline_spinner() {
|
||||
stop_inline_spinner 2>/dev/null || true
|
||||
stop_inline_spinner 2> /dev/null || true
|
||||
local message="$1"
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
@@ -312,11 +312,11 @@ start_inline_spinner() {
|
||||
done
|
||||
|
||||
# Clean up stop file before exiting
|
||||
rm -f "$stop_file" 2>/dev/null || true
|
||||
rm -f "$stop_file" 2> /dev/null || true
|
||||
exit 0
|
||||
) &
|
||||
INLINE_SPINNER_PID=$!
|
||||
disown 2>/dev/null || true
|
||||
disown 2> /dev/null || true
|
||||
else
|
||||
echo -n " ${BLUE}|${NC} $message" >&2 || true
|
||||
fi
|
||||
@@ -326,25 +326,25 @@ stop_inline_spinner() {
|
||||
if [[ -n "$INLINE_SPINNER_PID" ]]; then
|
||||
# Cooperative stop: create stop file to signal spinner to exit
|
||||
if [[ -n "$INLINE_SPINNER_STOP_FILE" ]]; then
|
||||
touch "$INLINE_SPINNER_STOP_FILE" 2>/dev/null || true
|
||||
touch "$INLINE_SPINNER_STOP_FILE" 2> /dev/null || true
|
||||
fi
|
||||
|
||||
# Wait briefly for cooperative exit
|
||||
local wait_count=0
|
||||
while kill -0 "$INLINE_SPINNER_PID" 2>/dev/null && [[ $wait_count -lt 5 ]]; do
|
||||
sleep 0.05 2>/dev/null || true
|
||||
while kill -0 "$INLINE_SPINNER_PID" 2> /dev/null && [[ $wait_count -lt 5 ]]; do
|
||||
sleep 0.05 2> /dev/null || true
|
||||
((wait_count++))
|
||||
done
|
||||
|
||||
# Only use SIGKILL as last resort if process is stuck
|
||||
if kill -0 "$INLINE_SPINNER_PID" 2>/dev/null; then
|
||||
kill -KILL "$INLINE_SPINNER_PID" 2>/dev/null || true
|
||||
if kill -0 "$INLINE_SPINNER_PID" 2> /dev/null; then
|
||||
kill -KILL "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
fi
|
||||
|
||||
wait "$INLINE_SPINNER_PID" 2>/dev/null || true
|
||||
wait "$INLINE_SPINNER_PID" 2> /dev/null || true
|
||||
|
||||
# Cleanup
|
||||
rm -f "$INLINE_SPINNER_STOP_FILE" 2>/dev/null || true
|
||||
rm -f "$INLINE_SPINNER_STOP_FILE" 2> /dev/null || true
|
||||
INLINE_SPINNER_PID=""
|
||||
INLINE_SPINNER_STOP_FILE=""
|
||||
|
||||
@@ -361,8 +361,8 @@ with_spinner() {
|
||||
start_inline_spinner "$msg"
|
||||
local exit_code=0
|
||||
if [[ -n "${MOLE_TIMEOUT_BIN:-}" ]]; then
|
||||
"$MOLE_TIMEOUT_BIN" "$timeout" "$@" >/dev/null 2>&1 || exit_code=$?
|
||||
else "$@" >/dev/null 2>&1 || exit_code=$?; fi
|
||||
"$MOLE_TIMEOUT_BIN" "$timeout" "$@" > /dev/null 2>&1 || exit_code=$?
|
||||
else "$@" > /dev/null 2>&1 || exit_code=$?; fi
|
||||
stop_inline_spinner "$msg"
|
||||
return $exit_code
|
||||
}
|
||||
@@ -379,14 +379,14 @@ format_last_used_summary() {
|
||||
local value="$1"
|
||||
|
||||
case "$value" in
|
||||
"" | "Unknown")
|
||||
echo "Unknown"
|
||||
return 0
|
||||
;;
|
||||
"Never" | "Recent" | "Today" | "Yesterday" | "This year" | "Old")
|
||||
echo "$value"
|
||||
return 0
|
||||
;;
|
||||
"" | "Unknown")
|
||||
echo "Unknown"
|
||||
return 0
|
||||
;;
|
||||
"Never" | "Recent" | "Today" | "Yesterday" | "This year" | "Old")
|
||||
echo "$value"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $value =~ ^([0-9]+)[[:space:]]+days?\ ago$ ]]; then
|
||||
@@ -444,7 +444,7 @@ has_full_disk_access() {
|
||||
if [[ -e "$test_path" ]]; then
|
||||
tested_count=$((tested_count + 1))
|
||||
# Try to stat the ACTUAL protected path - this requires FDA
|
||||
if stat "$test_path" >/dev/null 2>&1; then
|
||||
if stat "$test_path" > /dev/null 2>&1; then
|
||||
accessible_count=$((accessible_count + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user