From e51bfedc23d28be8d0e725e4da031e9784769143 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:07:11 +0000 Subject: [PATCH 1/5] feat: add Ubuntu 24 support in installer and docs Agent-Logs-Url: https://github.com/m-danaee/Clean-IP-Scanner/sessions/8bfd87e8-142e-45fa-88ee-6c466a98012d Co-authored-by: m-danaee <240194977+m-danaee@users.noreply.github.com> --- README.md | 56 ++++++++++++++++---- install.sh | 151 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 151 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 3c54e98..e89a94f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-ابزار پیدا کردن IP‌های تمیز CDN‌های مختلف برای Termux (اندروید ARM64) +ابزار پیدا کردن IP‌های تمیز CDN‌های مختلف برای **Ubuntu 24.04** و **Termux (اندروید ARM64)** --- @@ -36,9 +36,30 @@ ## 📥 نصب -### روش اول: دانلود مستقیم فایل آماده (پیشنهادی برای اکثر کاربران) +### روش Ubuntu 24.04 (پیشنهادی) -این روش سریع‌ترین و ساده‌ترین روش است. فقط یک دستور در Termux وارد کنید: +این روش برای اجرای پروژه روی Ubuntu 24.04 بهینه شده است: + +```bash +curl -fsSL https://raw.githubusercontent.com/4n0nymou3/Clean-IP-Scanner/main/install.sh | bash +``` + +پس از اتمام: + +```bash +clean-ip-scanner +``` + +**در Ubuntu این اسکریپت به‌صورت خودکار:** +- پیش‌نیازها را با `apt` نصب می‌کند +- هسته‌ی Xray مناسب معماری لینوکس شما را دانلود می‌کند +- لانچر `clean-ip-scanner` را در PATH نصب می‌کند + +--- + +### روش Termux: دانلود مستقیم فایل آماده + +این روش سریع‌ترین و ساده‌ترین روش برای کاربران Termux است: ```bash pkg update && pkg upgrade -y && pkg install -y wget unzip && wget https://github.com/4n0nymou3/Clean-IP-Scanner/releases/latest/download/clean-ip-scanner-arm64.zip && unzip clean-ip-scanner-arm64.zip && chmod +x clean-ip-scanner @@ -61,13 +82,13 @@ pkg update && pkg upgrade -y && pkg install -y wget unzip && wget https://github ### روش دوم: ساخت از سورس کد -این روش برای کاربرانی است که می‌خواهند ابزار را مستقیماً از سورس کد در دستگاه خودشان بسازند. یک دستور ساده: +این روش برای کاربرانی است که می‌خواهند ابزار را مستقیماً از سورس کد در دستگاه خودشان بسازند (Ubuntu و Termux). یک دستور ساده: ```bash curl -sL https://raw.githubusercontent.com/4n0nymou3/Clean-IP-Scanner/main/install.sh | bash ``` -پس از اتمام، از هرجایی در Termux اجرا کنید: +پس از اتمام، از هرجایی اجرا کنید: ```bash clean-ip-scanner @@ -75,7 +96,7 @@ clean-ip-scanner **مزایا:** - ۱۰۰٪ سازگار با دستگاه شما -- ساخت مستقیم در Termux +- ساخت مستقیم روی Ubuntu/Termux - نصب خودکار در PATH (از هر پوشه‌ای قابل اجراست) - دریافت خودکار هسته‌ی Xray @@ -496,6 +517,13 @@ rm -rf config/ xray/ ```bash rm -rf ~/Clean-IP-Scanner +# Ubuntu (اگر لانچر در /usr/local/bin نصب شده باشد) +sudo rm -f /usr/local/bin/clean-ip-scanner + +# Ubuntu (اگر لانچر در ~/.local/bin نصب شده باشد) +rm -f ~/.local/bin/clean-ip-scanner + +# Termux rm -f /data/data/com.termux/files/usr/bin/clean-ip-scanner ``` @@ -533,6 +561,10 @@ rm -f /data/data/com.termux/files/usr/bin/clean-ip-scanner **تست سرعت صفر نشان می‌دهد؟** ```bash +# Ubuntu +sudo apt install -y ca-certificates + +# Termux pkg install ca-certificates ``` @@ -549,6 +581,10 @@ chmod +x clean-ip-scanner **خطا: `wget not found` یا `unzip not found` یا `curl not found`** ```bash +# Ubuntu +sudo apt install -y wget unzip curl + +# Termux pkg install wget unzip curl ``` @@ -572,11 +608,11 @@ pkg install wget unzip curl **ابزار کرش می‌کند یا پاسخ نمی‌دهد** -Termux را ببندید و دوباره باز کنید: +ترمینال را ببندید و دوباره باز کنید: ```bash exit ``` -سپس Termux را دوباره اجرا کنید و ابزار را مجدداً اجرا کنید. +سپس دوباره ابزار را اجرا کنید. --- @@ -586,7 +622,7 @@ exit - فایل `clean_ips.txt` را برای استفاده‌ی بعدی نگه دارید - در حالت Xray فقط **یکی** از دو فایل کانفیگ را پر کنید (اگر هر دو پر باشند، فایل txt اولویت دارد) - **کانفیگ Xray شما باید با CDN مورد نظر هم‌خوانی داشته باشد** — اگر IP‌های Akamai را اسکن می‌کنید، کانفیگتان باید روی سرور Akamai باشد -- حداقل ۵۰ مگابایت فضای خالی در Termux داشته باشید +- حداقل ۵۰ مگابایت فضای خالی روی سیستم داشته باشید - اگر نتیجه‌ی خوبی نگرفتید، در زمان دیگری دوباره امتحان کنید — شرایط شبکه متغیر است - در هر لحظه با **Ctrl+C** می‌توانید اسکن را متوقف کنید و نتایج تا آن لحظه ذخیره می‌شوند @@ -610,4 +646,4 @@ exit - یک **Star ⭐** به repository بدهید - آن را با دوستانتان به اشتراک بگذارید -
\ No newline at end of file + diff --git a/install.sh b/install.sh index 6e43fa3..edb3efc 100644 --- a/install.sh +++ b/install.sh @@ -1,41 +1,79 @@ -#!/data/data/com.termux/files/usr/bin/bash +#!/usr/bin/env bash -set -e +set -euo pipefail clear + echo "==========================================" echo " Clean IP Scanner - Installer" echo "==========================================" echo "" -echo "Installing for Termux (Android ARM64)" + +PLATFORM="linux" +XRAY_ASSET="" +INSTALL_TARGET="" + +if [[ -n "${TERMUX_VERSION:-}" ]] || [[ "${PREFIX:-}" == *"/com.termux/"* ]]; then + PLATFORM="termux" + XRAY_ASSET="Xray-android-arm64-v8a.zip" + INSTALL_TARGET="${PREFIX}/bin" +else + ARCH="$(uname -m)" + case "${ARCH}" in + x86_64|amd64) + XRAY_ASSET="Xray-linux-64.zip" + ;; + aarch64|arm64) + XRAY_ASSET="Xray-linux-arm64-v8a.zip" + ;; + armv7l|armv7) + XRAY_ASSET="Xray-linux-arm32-v7a.zip" + ;; + *) + echo "✗ Unsupported Linux architecture: ${ARCH}" + echo " Supported: x86_64, aarch64, armv7" + exit 1 + ;; + esac + INSTALL_TARGET="/usr/local/bin" +fi + +echo "Detected platform: ${PLATFORM}" +echo "Xray package: ${XRAY_ASSET}" echo "" echo "[1/6] Checking and installing packages..." -if ! command -v git &> /dev/null; then - echo " → Installing git..." - pkg install -y git || { echo "✗ Failed to install git"; exit 1; } -fi -if ! command -v go &> /dev/null; then - echo " → Installing golang..." - pkg install -y golang || { echo "✗ Failed to install golang"; exit 1; } -fi -if ! command -v curl &> /dev/null; then - echo " → Installing curl..." - pkg install -y curl -fi -if ! command -v unzip &> /dev/null; then - echo " → Installing unzip..." - pkg install -y unzip -fi -if ! command -v jq &> /dev/null; then - echo " → Installing jq..." - pkg install -y jq +if [[ "${PLATFORM}" == "termux" ]]; then + for cmd in git go curl unzip jq; do + if ! command -v "${cmd}" &> /dev/null; then + pkg_name="${cmd}" + if [[ "${cmd}" == "go" ]]; then + pkg_name="golang" + fi + echo " → Installing ${pkg_name}..." + pkg install -y "${pkg_name}" || { echo "✗ Failed to install ${pkg_name}"; exit 1; } + fi + done +else + export DEBIAN_FRONTEND=noninteractive + SUDO="" + if [[ "$(id -u)" -ne 0 ]]; then + if command -v sudo &> /dev/null; then + SUDO="sudo" + else + echo "✗ sudo is required on Ubuntu to install dependencies" + exit 1 + fi + fi + + ${SUDO} apt-get update + ${SUDO} apt-get install -y git golang-go curl unzip jq ca-certificates fi echo "✓ All packages ready" echo "" echo "[2/6] Downloading source code..." -cd ~ +cd "${HOME}" if [ -d "Clean-IP-Scanner" ]; then echo " → Removing old installation..." rm -rf Clean-IP-Scanner @@ -50,7 +88,7 @@ go mod tidy || { echo "✗ Failed to download dependencies"; exit 1; } echo "✓ Dependencies ready" echo "" -echo "[4/6] Installing Xray core (latest stable for Android ARM64-v8a)..." +echo "[4/6] Installing Xray core (${XRAY_ASSET})..." if [ -f "./xray/xray" ]; then echo " → Xray binary already present, skipping download." @@ -59,17 +97,17 @@ else RETRY_COUNT=0 download_xray() { - echo " → Fetching latest Xray version from GitHub API (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)..." + echo " → Fetching latest Xray release URL (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)..." - LATEST_URL=$(curl -sL "https://api.github.com/repos/XTLS/Xray-core/releases/latest" | jq -r '.assets[] | select(.name=="Xray-android-arm64-v8a.zip") | .browser_download_url') + LATEST_URL=$(curl -sL "https://api.github.com/repos/XTLS/Xray-core/releases/latest" | jq -r ".assets[] | select(.name==\"${XRAY_ASSET}\") | .browser_download_url") - if [ -z "$LATEST_URL" ] || [ "$LATEST_URL" = "null" ]; then + if [ -z "${LATEST_URL}" ] || [ "${LATEST_URL}" = "null" ]; then echo " → Could not get download URL from API" return 1 fi - echo " → Downloading from $LATEST_URL" - curl -L --retry 3 --retry-delay 5 -o xray-core.zip "$LATEST_URL" || { echo " → Download failed"; return 1; } + echo " → Downloading from ${LATEST_URL}" + curl -L --retry 3 --retry-delay 5 -o xray-core.zip "${LATEST_URL}" || { echo " → Download failed"; return 1; } if [ ! -f xray-core.zip ] || [ ! -s xray-core.zip ]; then echo " → Downloaded file is missing or empty" @@ -84,13 +122,13 @@ else return 0 } - while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + while [ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]; do if download_xray; then echo "✓ Xray core installed" break else RETRY_COUNT=$((RETRY_COUNT + 1)) - if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then + if [ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]; then echo " → Retrying in 15 seconds..." sleep 15 fi @@ -101,16 +139,16 @@ else echo "" echo " → Auto-detection failed. Trying fallback version..." FALLBACK_VERSION="v26.3.27" - FALLBACK_URL="https://github.com/XTLS/Xray-core/releases/download/${FALLBACK_VERSION}/Xray-android-arm64-v8a.zip" - echo " → Downloading $FALLBACK_VERSION from $FALLBACK_URL" + FALLBACK_URL="https://github.com/XTLS/Xray-core/releases/download/${FALLBACK_VERSION}/${XRAY_ASSET}" + echo " → Downloading ${FALLBACK_VERSION} from ${FALLBACK_URL}" - if curl -L --retry 3 -o xray-core.zip "$FALLBACK_URL" && \ + if curl -L --retry 3 -o xray-core.zip "${FALLBACK_URL}" && \ unzip -o xray-core.zip -d xray_temp && \ mkdir -p xray && \ cp xray_temp/xray xray/ && \ chmod +x xray/xray; then rm -rf xray_temp xray-core.zip - echo "✓ Xray core installed (fallback version $FALLBACK_VERSION)" + echo "✓ Xray core installed (fallback version ${FALLBACK_VERSION})" else rm -rf xray_temp xray-core.zip echo "✗ Failed to install Xray core. Please check your internet connection." @@ -124,7 +162,7 @@ echo "[5/6] Setting up Xray config files..." mkdir -p config if [ ! -f "config/xray_config.json" ]; then - cat > config/xray_config.json << 'EOF' + cat > config/xray_config.json << 'JSON_EOF' { "log": { "loglevel": "warning" }, "inbounds": [ @@ -160,14 +198,14 @@ if [ ! -f "config/xray_config.json" ]; then } ] } -EOF +JSON_EOF echo "✓ Sample JSON config created at config/xray_config.json" else echo "✓ Existing xray_config.json found, keeping it." fi if [ ! -f "config/xray_config.txt" ]; then - cat > config/xray_config.txt << 'EOF' + cat > config/xray_config.txt << 'TXT_EOF' # Xray URL Config # Put your proxy URL on the line below (remove the # at the start). # Supported formats: vless://, vmess://, trojan://, ss:// @@ -175,7 +213,7 @@ if [ ! -f "config/xray_config.txt" ]; then # vless://your-uuid@your-server.com:443?type=ws&security=tls&host=your-server.com&path=%2F&sni=your-server.com#MyConfig # # If this file has a valid URL, it will be used instead of xray_config.json. -EOF +TXT_EOF echo "✓ Sample URL config created at config/xray_config.txt" else echo "✓ Existing xray_config.txt found, keeping it." @@ -192,14 +230,35 @@ fi echo "✓ Build completed" echo "" -echo "Installing to system..." -cat > $PREFIX/bin/clean-ip-scanner << 'SCRIPT' -#!/data/data/com.termux/files/usr/bin/bash -cd ~/Clean-IP-Scanner +echo "Installing launcher..." + +LAUNCHER_TMP="$(mktemp)" +cat > "${LAUNCHER_TMP}" << 'SCRIPT' +#!/usr/bin/env bash +cd "$HOME/Clean-IP-Scanner" ./clean-ip-scanner "$@" SCRIPT -chmod +x $PREFIX/bin/clean-ip-scanner -echo "✓ Installed to PATH" + +if [[ "${PLATFORM}" == "termux" ]]; then + mkdir -p "${INSTALL_TARGET}" + install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" + echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" +else + if [ -w "${INSTALL_TARGET}" ]; then + install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" + echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" + elif command -v sudo &> /dev/null; then + sudo install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" + echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" + else + mkdir -p "${HOME}/.local/bin" + install -m 755 "${LAUNCHER_TMP}" "${HOME}/.local/bin/clean-ip-scanner" + echo "✓ Installed to ${HOME}/.local/bin/clean-ip-scanner" + echo " → Add this to PATH if needed: export PATH=\"${HOME}/.local/bin:\$PATH\"" + fi +fi + +rm -f "${LAUNCHER_TMP}" echo "" echo "==========================================" @@ -220,4 +279,4 @@ echo "" echo " Results saved to: clean_ips.txt and clean_ips_list.txt" echo "" echo "You can now run: clean-ip-scanner" -echo "" \ No newline at end of file +echo "" From 68d5699aef2804a038841cac97e0fac4d4426eaf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:08:33 +0000 Subject: [PATCH 2/5] fix: improve linux launcher install fallback logic Agent-Logs-Url: https://github.com/m-danaee/Clean-IP-Scanner/sessions/8bfd87e8-142e-45fa-88ee-6c466a98012d Co-authored-by: m-danaee <240194977+m-danaee@users.noreply.github.com> --- install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install.sh b/install.sh index edb3efc..9411420 100644 --- a/install.sh +++ b/install.sh @@ -244,8 +244,7 @@ if [[ "${PLATFORM}" == "termux" ]]; then install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" else - if [ -w "${INSTALL_TARGET}" ]; then - install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" + if install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" 2>/dev/null; then echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" elif command -v sudo &> /dev/null; then sudo install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" From aaae8298ebefc78bd1df34b3256967c0abeb2dbf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:09:19 +0000 Subject: [PATCH 3/5] chore: clarify architecture and linux sudo messages Agent-Logs-Url: https://github.com/m-danaee/Clean-IP-Scanner/sessions/8bfd87e8-142e-45fa-88ee-6c466a98012d Co-authored-by: m-danaee <240194977+m-danaee@users.noreply.github.com> --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 9411420..1d3fd13 100644 --- a/install.sh +++ b/install.sh @@ -31,7 +31,7 @@ else ;; *) echo "✗ Unsupported Linux architecture: ${ARCH}" - echo " Supported: x86_64, aarch64, armv7" + echo " Supported: x86_64/amd64, aarch64/arm64, armv7l/armv7" exit 1 ;; esac @@ -61,7 +61,7 @@ else if command -v sudo &> /dev/null; then SUDO="sudo" else - echo "✗ sudo is required on Ubuntu to install dependencies" + echo "✗ sudo is required on Linux to install dependencies" exit 1 fi fi From 39d0fcff231fd04433f56f9b41652b5f13dfa949 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:10:01 +0000 Subject: [PATCH 4/5] fix: address installer review feedback Agent-Logs-Url: https://github.com/m-danaee/Clean-IP-Scanner/sessions/8bfd87e8-142e-45fa-88ee-6c466a98012d Co-authored-by: m-danaee <240194977+m-danaee@users.noreply.github.com> --- install.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 1d3fd13..0fd95d4 100644 --- a/install.sh +++ b/install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -euo pipefail +set -eo pipefail clear @@ -122,13 +122,13 @@ else return 0 } - while [ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]; do + while [ "${RETRY_COUNT}" -lt "${MAX_RETRIES}" ]; do if download_xray; then echo "✓ Xray core installed" break else RETRY_COUNT=$((RETRY_COUNT + 1)) - if [ ${RETRY_COUNT} -lt ${MAX_RETRIES} ]; then + if [ "${RETRY_COUNT}" -lt "${MAX_RETRIES}" ]; then echo " → Retrying in 15 seconds..." sleep 15 fi @@ -244,9 +244,10 @@ if [[ "${PLATFORM}" == "termux" ]]; then install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" else - if install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" 2>/dev/null; then + if install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner"; then echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" elif command -v sudo &> /dev/null; then + echo " → Direct install failed, retrying with sudo..." sudo install -m 755 "${LAUNCHER_TMP}" "${INSTALL_TARGET}/clean-ip-scanner" echo "✓ Installed to ${INSTALL_TARGET}/clean-ip-scanner" else From 14fdac6f7f395129174bf82fc59c138ca3ac3cfe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:10:46 +0000 Subject: [PATCH 5/5] chore: clean up installer heredocs and PATH hint Agent-Logs-Url: https://github.com/m-danaee/Clean-IP-Scanner/sessions/8bfd87e8-142e-45fa-88ee-6c466a98012d Co-authored-by: m-danaee <240194977+m-danaee@users.noreply.github.com> --- install.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install.sh b/install.sh index 0fd95d4..ccf6f09 100644 --- a/install.sh +++ b/install.sh @@ -162,7 +162,7 @@ echo "[5/6] Setting up Xray config files..." mkdir -p config if [ ! -f "config/xray_config.json" ]; then - cat > config/xray_config.json << 'JSON_EOF' + cat > config/xray_config.json << 'EOF_JSON' { "log": { "loglevel": "warning" }, "inbounds": [ @@ -198,14 +198,14 @@ if [ ! -f "config/xray_config.json" ]; then } ] } -JSON_EOF +EOF_JSON echo "✓ Sample JSON config created at config/xray_config.json" else echo "✓ Existing xray_config.json found, keeping it." fi if [ ! -f "config/xray_config.txt" ]; then - cat > config/xray_config.txt << 'TXT_EOF' + cat > config/xray_config.txt << 'EOF_TXT' # Xray URL Config # Put your proxy URL on the line below (remove the # at the start). # Supported formats: vless://, vmess://, trojan://, ss:// @@ -213,7 +213,7 @@ if [ ! -f "config/xray_config.txt" ]; then # vless://your-uuid@your-server.com:443?type=ws&security=tls&host=your-server.com&path=%2F&sni=your-server.com#MyConfig # # If this file has a valid URL, it will be used instead of xray_config.json. -TXT_EOF +EOF_TXT echo "✓ Sample URL config created at config/xray_config.txt" else echo "✓ Existing xray_config.txt found, keeping it." @@ -233,11 +233,11 @@ echo "" echo "Installing launcher..." LAUNCHER_TMP="$(mktemp)" -cat > "${LAUNCHER_TMP}" << 'SCRIPT' +cat > "${LAUNCHER_TMP}" << 'EOF_SCRIPT' #!/usr/bin/env bash cd "$HOME/Clean-IP-Scanner" ./clean-ip-scanner "$@" -SCRIPT +EOF_SCRIPT if [[ "${PLATFORM}" == "termux" ]]; then mkdir -p "${INSTALL_TARGET}" @@ -254,7 +254,7 @@ else mkdir -p "${HOME}/.local/bin" install -m 755 "${LAUNCHER_TMP}" "${HOME}/.local/bin/clean-ip-scanner" echo "✓ Installed to ${HOME}/.local/bin/clean-ip-scanner" - echo " → Add this to PATH if needed: export PATH=\"${HOME}/.local/bin:\$PATH\"" + echo " → Add this to PATH if needed: export PATH=\"\$HOME/.local/bin:\$PATH\"" fi fi