diff --git a/README.md b/README.md index 972157e..2b35ffc 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,14 @@ generate_mac() { 平常使用直接 `./generate_mac.sh` 运行就行 -## ipv4.sh +## ip.sh 用来通过 Cloudflare 绑定的域名进行内网 DDNS 的脚本,目的是为了在大内网的其他地方也能够访问到内网的设备 需要自己调整 Cloudflare 的相关内容 +新版本同时支持 v6 & v4,只需要配置或留空对应的位置即可 + ```bash # Cloudflare API 相关信息 # Cloudflare API 端点,替换为实际的 API URL @@ -40,6 +42,9 @@ CF_TOKEN="" CF_DOMAIN="" ``` +<<<<<<< HEAD +平时使用直接 `./ip.fish` 运行就行 +======= `zone_id` 是你的 Cloudflare 的区域 ID,在域名的概览下就有 ![](https://bili33.eu.org/file/VkGuUz9S.png) @@ -75,6 +80,7 @@ EOF 通过对网关进行 ping 操作来判断机身是否断开 pppoe 连接,需要与 `generate_mac.sh` 和 `ipv4.sh` 一起使用(DDNS 脚本不用的话注释掉就行) 建议开机运行 +>>>>>>> upstream/fish ## swap.sh @@ -82,6 +88,24 @@ EOF 直接 `./swap.sh` 运行就行 +## kc.fish + +Keep Connection - 自动检测连接状态并在断线或者新设备加入触发设备踢出时重拨 + +使用方法: +```bash +./kc.fish +``` + +配置说明(在脚本开头可配置): +- `max_retry`: 最大重试次数(默认30次) +- `sleep_time`: 重拨后等待网络稳定时间(秒,默认10秒) +- `overhead_interval`: 正常状态检测间隔(秒,默认3秒) +- `max_fail`: 最大连续失败次数(触发重拨,默认3次) +- `ping_target`: 检测连接的目标地址(默认 https://baidu.com) + +脚本会持续运行,建议通过 `screen` 或 `tmux` 后台运行 + ## utils.sh Openwrt 备份脚本,注意自己调整备份文件保存的位置,建议使用 SMB 存储或者插个 U 盘 diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..64dc0f0 --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1,2 @@ +.envrc +l.fish \ No newline at end of file diff --git a/scripts/generate_mac.sh b/scripts/generate_mac.sh index 6259477..5f26de9 100644 --- a/scripts/generate_mac.sh +++ b/scripts/generate_mac.sh @@ -1,25 +1,35 @@ #!/bin/sh +# 获取默认接口 +get_default_iface() { + iface=$(ip route | grep "default" | awk '{print $5}' | head -n 1) + if [ -z "$iface" ]; then + iface=$(ip link show up | grep -v "lo" | head -n 1 | awk -F': ' '{print $2}') + fi + echo "$iface" +} + +IFACE=$(get_default_iface) + # 生成一个随机的MAC地址 generate_mac() { - # 固定的前缀,格式为 aa:bb:cc - PREFIX="" - # 使用 /dev/urandom 获取随机数 - HEX1=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) - HEX2=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) - HEX3=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) - echo "$PREFIX:$HEX1:$HEX2:$HEX3" + # 使用 /dev/urandom 获取随机数 + HEX1=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) + HEX2=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) + HEX3=$(hexdump -n 1 -e '1/1 "%02X"' /dev/urandom) + # 保持前缀 (可以使用固定前缀,这里演示随机) + echo "00:E0:4C:$HEX1:$HEX2:$HEX3" } # 获取新的MAC地址 NEW_MAC=$(generate_mac) -echo "生成的新MAC地址为: $NEW_MAC" +echo "针对接口 $IFACE 生成的新MAC地址为: $NEW_MAC" -# 使用新的MAC地址修改eth0的MAC地址 -ip link set dev eth0 down -ip link set dev eth0 address $NEW_MAC -ip link set dev eth0 up +# 使用新的MAC地址修改网卡的MAC地址 +ip link set dev "$IFACE" down +ip link set dev "$IFACE" address "$NEW_MAC" +ip link set dev "$IFACE" up # 验证修改是否成功 -ip link show eth0 | grep ether - +echo "验证修改结果:" +ip link show "$IFACE" | grep ether diff --git a/scripts/ipv4.fish b/scripts/ip.fish similarity index 68% rename from scripts/ipv4.fish rename to scripts/ip.fish index 30f5f8d..0756769 100755 --- a/scripts/ipv4.fish +++ b/scripts/ip.fish @@ -1,65 +1,54 @@ #!/usr/bin/env fish # Cloudflare Dynamic DNS Updater (IPv4 & IPv6) -# 自动更新 Cloudflare DNS 记录为当前 PPPoE 拨号的内网 IP 地址 +# 自动更新 Cloudflare DNS 记录为当前 default route 的 IP 地址 # 支持同时更新 IPv4 (A 记录) 和 IPv6 (AAAA 记录) # 如果不需要更新某种类型的记录,请将对应的域名设为空,ID 可以留空以启用自动检测 +source utils_lib.fish + # Cloudflare Zone ID # DNS 配置主界面(https://dash.cloudflare.com//) > API -set -g ZONE_ID xxx +# set ZONE_ID # Cloudflare API Token -set -g TOKEN xxx +# set TOKEN # IPv4 配置 (如果不需要 IPv4 更新,请将这些变量设为空) # RECORD_ID 可以通过 API 获取 -set -g IPV4_DOMAIN "" -set -g IPV4_RECORD_ID "" +# set IPV4_DOMAIN +# 留空以启用自动检测 +# set IPV4_RECORD_ID # IPv6 配置 (如果不需要 IPv6 更新,请将这些变量设为空) -set -g IPV6_DOMAIN "" -set -g IPV6_RECORD_ID "" - -# PPPoE 接口名称(根据实际情况修改) -set -g PPPOE_INTERFACE pppoe-wan +# set IPV6_DOMAIN +# 留空以启用自动检测 +# set IPV6_RECORD_ID # DNS 记录配置 -set -g DNS_TTL 120 -set -g DNS_PROXIED false +set DNS_TTL 120 +set DNS_PROXIED false -# 全局正则表达式映射 -set -g regex_A '^([0-9]{1,3}\.){3}[0-9]{1,3}$' -set -g regex_AAAA '^([0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}$' - -# ============================================================================= -# 辅助函数 -# ============================================================================= - -function log_info - printf "\033[36m%s %s\033[0m\n" "[INFO]" (string join " " $argv) >&2 -end - -function log_error - printf "\033[31m%s %s\033[0m\n" "[ERROR]" (string join " " $argv) >&2 -end - -function log_success - printf "\033[32m%s %s\033[0m\n" "[SUCCESS]" (string join " " $argv) >&2 -end function validate_ip + + set -l regex_A '^([0-9]{1,3}\.){3}[0-9]{1,3}$' + set -l regex_AAAA '^([0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}$' + set -l type $argv[1] set -l ip $argv[2] set -l regex (eval echo \${regex_$type}) string match -q -r $regex $ip end -function get_pppoe_ip +function get_ip set -l type $argv[1] - set -l flag (string match -q A $type; and echo -4; or echo -6) - set -l filter (string match -q A $type; and echo inet; or echo inet6.*global) - set -l ip (ip $flag addr show $PPPOE_INTERFACE 2>/dev/null | awk "/$filter/ {print \$2}" | cut -d'/' -f1 | head -n 1) + switch $type + case A + set ip (ip -4 route | grep "default" | awk '{print $NF}') + case AAAA + set ip (ip -6 addr show scope global | grep inet6 | head -1 | awk '{print $2}' | cut -d'/' -f1) + end test -n "$ip"; or begin log_error "获取 $type 地址失败: $ip"; return 1; end validate_ip $type $ip; or begin log_error "$type 地址格式无效: $ip"; return 1; end echo $ip @@ -72,10 +61,9 @@ function get_dns_record log_info "获取 $type 记录..." curl -s -X GET $url -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" | read -l response; or begin log_error "请求失败"; return 1; end - string match -q '"success":true' $response; or begin log_error "Cloudflare API 错误: $response"; return 1; end + string match -q '*"success":true*' $response; or begin log_error "Cloudflare API 错误: $response"; return 1; end - set -l regex (eval echo \${regex_$type}) - set -l ip (string match -r -o $regex $response) + set -l ip (echo $response | sed -n 's/.*"content":"\([^" ]*\)".*/\1/p') test -n "$ip"; or begin log_error "未提取到 IP"; return 1; end validate_ip $type $ip; or begin log_error "IP 格式无效: $ip"; return 1; end @@ -149,38 +137,6 @@ function update_cloudflare_dns_record end end # update_cloudflare_dns_record 函数结束 -# ============================================================================= -# 自动检测并更新 Record ID -# ============================================================================= -function auto_detect_record_id - # 参数: record_type, domain, record_id_var_name - set -l record_type $argv[1] - set -l domain $argv[2] - set -l record_id_var_name $argv[3] - - if test -z "$domain" - # 未配置域名, nothing to do - return - # 尝试解析错误信息 - set -l error_msg (echo $response | sed -n 's/.*"message":"\([^"]*\)".*/\1/p') - if test -n "$error_msg" - log_error "错误信息: $error_msg" - end - - # 检查常见错误原因 - if string match -q "*authentication*" $response - log_error "认证失败,请检查 TOKEN 是否正确" - else if string match -q "*not found*" $response - log_error "DNS 记录未找到,请检查 Record ID 是否正确" - else if string match -q "*invalid*" $response - log_error "请求参数无效,请检查配置" - end - - log_error "完整响应内容: $response" - return 1 - end -end # update_cloudflare_dns_record 函数结束 - # ============================================================================= # 自动检测并更新 Record ID # ============================================================================= @@ -212,10 +168,12 @@ function auto_detect_record_id set -l id (echo $resp | awk -F'"id":' '{print $2}' | cut -d '"' -f2 | head -n 1) if test -n "$id" log_success "$record_type Record ID 获取成功: $id" + # 确保变量定义未被注释 + sed -i "s@^#\s*set $record_id_var_name@set $record_id_var_name@" (status --current-filename) # 更新运行时变量 - set -g $record_id_var_name $id + set $record_id_var_name $id # 自我更新脚本文件中对应行 - sed -i "s@set -g $record_id_var_name.*@set -g $record_id_var_name \"$id\"@" (status --current-filename) + sed -i "s@set $record_id_var_name.*@set $record_id_var_name \"$id\"@" (status --current-filename) # 返回 id 以便调用处捕获 echo $id return 0 @@ -231,27 +189,17 @@ end log_info "开始检查 Cloudflare DNS 记录更新..." -# 自动检测并更新 Record ID -# auto_detect_record_id A IPV4_DOMAIN IPV4_RECORD_ID -# auto_detect_record_id AAAA IPV6_DOMAIN IPV6_RECORD_ID - -# 在主循环中按需自动检测 Record ID(也可单独调用) -# auto_detect_record_id A "$IPV4_DOMAIN" IPV4_RECORD_ID -# auto_detect_record_id AAAA "$IPV6_DOMAIN" IPV6_RECORD_ID - set -l record_var_names IPV4_RECORD_ID IPV6_RECORD_ID # 支持的记录类型数组 set -l types A AAAA set -l domains $IPV4_DOMAIN $IPV6_DOMAIN set -l record_ids $IPV4_RECORD_ID $IPV6_RECORD_ID -set -l pppoe_funcs get_pppoe_ipv4 get_pppoe_ipv6 for i in (seq (count $types)) set -l type $types[$i] set -l domain $domains[$i] set -l record_id $record_ids[$i] - set -l pppoe_func $pppoe_funcs[$i] set -l record_var_name $record_var_names[$i] if test -z "$domain" @@ -271,7 +219,7 @@ for i in (seq (count $types)) log_info "=== 处理 $type 记录 ($domain) ===" # 获取本地 IP - set -l local_ip ($pppoe_func) + set -l local_ip (get_ip $type) if test $status -ne 0 log_error "获取本地 $type 地址失败,跳过" continue diff --git a/scripts/ipv4.sh b/scripts/ipv4.sh deleted file mode 100644 index 3b6eae8..0000000 --- a/scripts/ipv4.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -# Cloudflare API 相关信息 -# Cloudflare API 端点,替换为实际的 API URL -CF_API="https://api.cloudflare.com/client/v4/zones/{{zone_id}}/dns_records/{{record_id}}" -# Cloudflare API Token,换成你自己的 -CF_TOKEN="" -# Cloudflare 域名,换成你自己的,这个是你要访问的域名 -CF_DOMAIN="" - -# 获取 PPPoE 拨号的内网 IP 地址 -INTERNAL_IP=$(ip -4 addr show pppoe-wan | awk '/inet/ {print $2}' | cut -d'/' -f1 | head -n 1) - -# 判断是否获取到 IP 地址 -if [ -z "$INTERNAL_IP" ]; then - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] 未能获取 PPPoE 内网 IP 地址" - exit 1 -fi - -echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] 当前 PPPoE 内网 IP 地址为:$INTERNAL_IP" - -# 获取 Cloudflare 上的现有 DNS 记录 IP 地址 -CURRENT_IP=$(curl -s -X GET "$CF_API" \ - -H "Authorization: Bearer $CF_TOKEN" \ - -H "Content-Type: application/json" | \ - sed -n 's/.*"content":"\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)".*/\1/p') - -echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] Cloudflare 上的 IP 地址为:$CURRENT_IP" - -# 检查是否需要更新 -if [ "$INTERNAL_IP" = "$CURRENT_IP" ]; then - echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] Cloudflare 上的 IP 地址与当前 PPPoE 拨号的 IP 地址相同,无需更新。" - exit 0 -fi - -echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] Cloudflare 上的 IP 地址为:$CURRENT_IP,正在更新为:$INTERNAL_IP" - -# 构建 Cloudflare API 请求的 JSON 数据 -DATA=$(cat </dev/null) + + if test "$http_code" = 204 + log_success "Connection check to $check_url successful." + sleep $overhead_interval + else + if test "$http_code" = 302 + log_info "Detected captive portal (HTTP 302). Attempting to login..." + else + log_info "Connection check failed (HTTP $http_code). Reconnecting..." + end + + reconnect + + # 登录/重连后多等一会儿,确保网关状态同步 + log_info "Post-reconnect cooldown..." + sleep 5 + end +end diff --git a/scripts/login.fish b/scripts/login.fish new file mode 100644 index 0000000..ff755e2 --- /dev/null +++ b/scripts/login.fish @@ -0,0 +1,31 @@ +#!/usr/bin/env fish + +source utils_lib.fish + +# 如果你采用 PPPOE 方案则不用设置账号和密码 +# set user_account "" +# set password "" + +# 动态获取默认接口 +set -l iface (get_default_iface) +# 使用 awk 替代 grep -oP 提取 IP +set -l ip_address (ip -4 addr show $iface | grep "inet " | awk '{print $2}' | cut -d/ -f1 | head -n 1) + +# 检查是否成功获取到 IP 地址 +if test -z "$ip_address" + log_error "无法获取接口 $iface 的 IP 地址" + exit 1 +end + +log_info "获取到的 IP 地址 ($iface): $ip_address" + +if test -n "$user_account"; and test -n "$password" + echo 正在尝试登录... + curl "http://10.0.3.2:801/eportal/portal/login?callback=dr1004&login_method=1&user_account=%2C0%2C$user_account&user_password=$password&wlan_user_ip=$ip_address&wlan_user_ipv6=&wlan_ac_ip=172.16.254.2&wlan_ac_name=&jsVersion=4.1.3&terminal_type=1&lang=zh-cn&v=6985&lang=zh" \ + -H 'Accept: */*' \ + -H 'Accept-Language: zh-CN,zh;q=0.9' \ + -H 'Connection: keep-alive' \ + -H 'Referer: http://10.0.3.2/' \ + -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' \ + --insecure +end diff --git a/scripts/mac.sh b/scripts/mac.sh index fac4d7a..24a9829 100644 --- a/scripts/mac.sh +++ b/scripts/mac.sh @@ -1,11 +1,22 @@ #!/bin/sh -MAC_FILE="/root/mac" +MAC_FILE="./mac" + +# 获取默认接口 +get_default_iface() { + iface=$(ip route | grep "default" | awk '{print $5}' | head -n 1) + if [ -z "$iface" ]; then + iface=$(ip link show up | grep -v "lo" | head -n 1 | awk -F': ' '{print $2}') + fi + echo "$iface" +} + +IFACE=$(get_default_iface) # 获取当前MAC地址并保存到文件 save_mac() { - CURRENT_MAC=$(ip link show eth0 | grep ether | awk '{print $2}') - echo "当前的MAC地址是: $CURRENT_MAC" + CURRENT_MAC=$(ip link show "$IFACE" | grep ether | awk '{print $2}') + echo "当前的MAC地址 ($IFACE) 是: $CURRENT_MAC" echo "$CURRENT_MAC" > "$MAC_FILE" echo "已保存当前MAC地址到 $MAC_FILE" } @@ -15,12 +26,12 @@ restore_mac() { if [ -f "$MAC_FILE" ]; then SAVED_MAC=$(cat "$MAC_FILE") echo "从文件恢复MAC地址: $SAVED_MAC" - ip link set dev eth0 down - ip link set dev eth0 address "$SAVED_MAC" - ip link set dev eth0 up - echo "已恢复MAC地址到eth0" + ip link set dev "$IFACE" down + ip link set dev "$IFACE" address "$SAVED_MAC" + ip link set dev "$IFACE" up + echo "已恢复MAC地址到 $IFACE" else - echo "MAC文件不存在,无法恢复MAC地址" + echo "MAC文件不存在: $MAC_FILE,无法恢复" fi } diff --git a/scripts/ping.sh b/scripts/ping.sh deleted file mode 100644 index 7ed48d6..0000000 --- a/scripts/ping.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh - -# 获取 PPPoE 网关地址 -get_gateway() { - GATEWAY=$(ip route | grep "default via" | grep "pppoe" | awk '{print $3}') - if [ -z "$GATEWAY" ]; then - echo "Failed to get gateway address. Waiting for network to stabilize and retrying..." - # 如果获取网关失败,等待一段时间后重试 - sleep 5 - GATEWAY=$(ip route | grep "default via" | grep "pppoe" | awk '{print $3}') - if [ -z "$GATEWAY" ]; then - echo "Still unable to get gateway address. Exiting..." - exit 1 - fi - fi - echo "Gateway detected: $GATEWAY" -} - -# 初始化网关地址 -# get_gateway - -# 设置失败次数计数器 -FAIL_COUNT=0 -# 设置连续失败的检测次数(3次) -MAX_FAIL=3 -# 设置重拨后休眠时间(秒) -SLEEP_TIME=120 -# 设置每次 ping 之间的间隔时间(1秒) -PING_INTERVAL=1 - -while true; do - # 进行一次 ping 操作,仅发送一个数据包,超时设置为1秒 - if ping -c 1 -W 1 "$GATEWAY" > /dev/null 2>&1; then - # ping 成功,重置失败计数器 - FAIL_COUNT=0 - echo "Ping to gateway $GATEWAY successful." - else - # ping 失败,增加失败计数器 - FAIL_COUNT=$((FAIL_COUNT + 1)) - echo "Ping to gateway $GATEWAY failed. Failure count: $FAIL_COUNT" - fi - - # 检查是否达到连续失败的最大次数 - if [ "$FAIL_COUNT" -ge "$MAX_FAIL" ]; then - echo "Network appears to be down... Executing scripts to reconnect..." - # 执行生成 MAC 地址脚本和重拨脚本 - /root/generate_mac.sh - sleep 10 - /root/ipv4.sh - # 休眠指定时间以等待网络恢复 - echo "Waiting for network to stabilize..." - sleep "$SLEEP_TIME" - # 重置失败计数器 - FAIL_COUNT=0 - # 重新获取网关地址(每次重拨后必须更新网关地址) - echo "Attempting to get new gateway address after reconnection..." - get_gateway - fi - - # 每次 ping 之间休眠指定时间 - sleep "$PING_INTERVAL" -done \ No newline at end of file diff --git a/scripts/startup.sh b/scripts/startup.sh new file mode 100644 index 0000000..e7bd218 --- /dev/null +++ b/scripts/startup.sh @@ -0,0 +1,24 @@ +# 创建会话 +tmux new-session -d -s "kc" + +# 获取 tmux 默认使用的 shell +TMUX_SHELL=$(tmux show-options -gv default-shell 2>/dev/null) +if [ -z "$TMUX_SHELL" ]; then + TMUX_SHELL="$SHELL" +fi + +# 根据 shell 类型决定 source 命令 +case "$TMUX_SHELL" in + *fish) + SOURCE_CMD="source .envrc" + ;; + *) + # ash/bash/sh 环境下使用 . 且必须带路径以兼容所有环境 + SOURCE_CMD=". ./.envrc" + ;; +esac + +# 发送指令 +tmux send-keys -t "kc" "$SOURCE_CMD" Enter "./kc.fish" Enter + +exit 0 diff --git a/scripts/utils.sh b/scripts/utils.sh index f49a188..7fe09c2 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -72,7 +72,7 @@ restore_full_image() { # 确认操作 read -p "确定要恢复系统镜像吗?此操作不可逆![y/N]: " confirm - [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return + [ "$confirm" != "y" ] && [ "$confirm" != "Y" ] && return # 解压并恢复 echo -e "${BLUE}[$(get_timestamp)] 正在解压镜像文件...${RESET}" @@ -112,7 +112,7 @@ restore_config() { # 确认操作 read -p "确定要恢复系统配置吗?[y/N]: " confirm - [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return + [ "$confirm" != "y" ] && [ "$confirm" != "Y" ] && return # 创建临时备份 local current_backup="$OPENWRT_CONFIG_BACKUP_DIR/current_config_$(date +%H%M%S).bak" @@ -151,7 +151,7 @@ restore_iptables() { # 确认操作 read -p "确定要恢复iptables规则吗?[y/N]: " confirm - [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return + [ "$confirm" != "y" ] && [ "$confirm" != "Y" ] && return if iptables-restore < $full_path; then echo -e "${GREEN}[$(get_timestamp)] iptables规则恢复成功!${RESET}" @@ -184,7 +184,7 @@ restore_firewall() { # 确认操作 read -p "确定要恢复防火墙配置吗?[y/N]: " confirm - [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return + [ "$confirm" != "y" ] && [ "$confirm" != "Y" ] && return # 备份当前配置 local current_backup="$FIREWALL_BACKUP_DIR/current_firewall_$(date +%H%M%S).bak" diff --git a/scripts/utils_lib.fish b/scripts/utils_lib.fish new file mode 100644 index 0000000..c6148e5 --- /dev/null +++ b/scripts/utils_lib.fish @@ -0,0 +1,21 @@ +function log_info + printf "\033[36m%s %s\033[0m\n" "[INFO]" (string join " " $argv) >&2 +end + +function log_error + printf "\033[31m%s %s\033[0m\n" "[ERROR]" (string join " " $argv) >&2 +end + +function log_success + printf "\033[32m%s %s\033[0m\n" "[SUCCESS]" (string join " " $argv) >&2 +end + +function get_default_iface + # 获取默认路由对应的网卡 + set -l iface (ip route | grep "default" | awk '{print $5}' | head -n 1) + if test -z "$iface" + # 回退到第一个非回环网卡 + set iface (ip link show up | grep -v "lo" | head -n 1 | awk -F': ' '{print $2}') + end + echo $iface +end \ No newline at end of file