在网络受限的VPS上 运行一个脚本 向外访问网络时暂停 使用者进行替代操作

前言

上一篇我们解决DD脚本的问题的方案, 其实就可以用来解决所有网络受限的VPS环境运行脚本的问题 (
当然整个操作看起来比较繁琐.
而且, 上一篇的方案只处理了curl的情况. 而常见的脚本, 有些是用curl访问网络, 有些是用wget.
能力弱一点的朋友, 不知道怎么改成处理wget的情况.

那么, 我们尝试做一个通用一点的方案.
而且考虑如何方便使用.

思路

我们
做一个 fake-curl-wget.sh 脚本.
1) fake-curl-wget.sh 包含一个 curl() 的壳子, 和一个 wget() 的壳子
2) 使用者在终端先 source fake-curl-wget.sh 再执行其它脚本
3) 这样, 后面执行的脚本会调用到 fake-curl-wget.sh 中的 curl()壳子 和 wget()壳子
4) 每次调用 curl 或 wget 时, 打印一个调用序号. 这个序号每次调用时, 自增1
为了避免管道命令导致的序号问题, 使用临时文件保存序号.
5) 对于所有 curl 和 wget 调用, 这个序号是统一. 
先调用curl时, 序号为1.
接着调用wget时, 序号为2.
6) 输出 pwd 当前目录
7) 输出 完整的 curl 命令和全部参数
8) 输出 完整的 wget 命令和全部参数
9) 这个curl壳 或 wget壳, 并不去真正访问网络
10) 根据调用序号, 执行预设的命令. 如,
cp file1 /path/to/file

cat file2
用来替代 curl -LO 或 curl -Lo 的保存文件的命令
或 curl -L 的输出到stdout的命令
11) 这些预设命令是会被人工编辑而增加的. 用case实现 10) 的逻辑.
12) 这个curl壳 或 wget壳, 永远返回成功.

实践与调试

把上面这一大段需求发给GPT.
得到的结果大方向是对的, 但是有小问题. 而且对话几次, 也不能让我满意.
还是要动用我的编码能力, 综合多个GPT的回答.
* 也许是我用的免费的GPT, 能力不够.

版本V1

最终得到 fake-curl-wget.sh 文件内容:
#!/usr/bin/env bash
# ==========================================
# fake-curl-wget.sh
# 拦截 curl 和 wget 调用,用于调试/离线测试
# ==========================================

# 调用计数文件(全局共享)
COUNT_FILE="${HOME}/fake_curl_wget_count.tmp"
echo "0" > "$COUNT_FILE"

# 导出变量,使其在子shell中也可用
export COUNT_FILE

# =====================================================
# 通用打印与执行逻辑
# =====================================================
__fake_net_common() {

    local cmd="$1"; shift
    local fake_net_call_count

    fake_net_call_count=$(cat "$COUNT_FILE")
    fake_net_call_count=$((fake_net_call_count + 1))
    echo "$fake_net_call_count" > "$COUNT_FILE"


    echo "[DEBUG] curl/wget call # $fake_net_call_count" >&2
    echo "[DEBUG] Current directory: $(pwd)" >&2

    printf '[DEBUG] Command: %s ' "$cmd" >&2
    printf '%q ' "$@" >&2
    printf '\n' >&2

    # 根据调用序号执行预设命令
    case "$fake_net_call_count" in
        *)
            # 未设置预设命令
            echo "[DEBUG] 未设置预设命令" >&2
            ;;
    esac

    echo >&2

    # 永远返回成功
    return 0
}

# =====================================================
# curl 壳
# =====================================================
curl() {
    __fake_net_common curl "$@"
}

# =====================================================
# wget 壳
# =====================================================
wget() {
    __fake_net_common wget "$@"
}

# 导出函数,使其在子shell中也可用
export -f curl
export -f wget
export -f __fake_net_common

# ------------------------------------------
# 提示加载成功
# ------------------------------------------
echo "[INFO] fake-curl-wget.sh 已加载"
echo "[INFO] 所有 curl / wget 调用将被拦截"

我们以常见的233脚本为例.
假设我们的VPS网络有问题, 访问不了github.com, 还有一些其它网络问题. 用233脚本官方默认的方式调用失败了.
bash <(wget -qO- -o- https://github.com/233boy/Xray/raw/main/install.sh)
那么我们先把 https://github.com/233boy/Xray/raw/main/install.sh 用我们的办法先下载, 再上传到VPS上.
然后
source fake-curl-wget.sh
bash install.sh
观察到了很奇怪的日志.
不仅序号都为1
而且序号与命令和参数的打印也是顺序混乱的.

把我们的 fake-curl-wget.sh 脚本内容, 和 install.sh 脚本内容, 再加上日志, 全部复制粘贴问GPT. (感谢现在GPT的输入窗口足够大)
得知原因是在 install.sh 下面的代码中
[[ ! $is_core_file ]] && download core &
[[ ! $local_install ]] && download sh &
[[ $jq_not_found ]] && download jq &
get_ip
wait
这3个 download xxxxx & 都是后台多线程执行的. 所以导致序号文件的读写出问题.

GPT的建议是在我们的 fake-curl-wget.sh 脚本中加入锁机制. 具体而言, 是用一个文件当作锁标志.

版本V2

我们把 fake-curl-wget.sh 改成这样
#!/usr/bin/env bash
# ==========================================
# fake-curl-wget.sh
# 拦截 curl 和 wget 调用,用于调试/离线测试
# ==========================================

# 调用计数文件(全局共享)
COUNT_FILE="${HOME}/fake_curl_wget_count.tmp"
echo "0" > "$COUNT_FILE"

# 锁文件
LOCK_FILE="${COUNT_FILE}.lock"

# 导出变量,使其在子shell中也可用
export COUNT_FILE
export LOCK_FILE

# =====================================================
# 通用打印与执行逻辑
# =====================================================
__fake_net_common() {

    # 使用文件锁保护整个函数执行过程
    {
        flock 200 || {
            echo "[ERROR] 无法获取锁,超时" >&2
            return 1
        }

        local cmd="$1"; shift
        local fake_net_call_count

        fake_net_call_count=$(cat "$COUNT_FILE")
        fake_net_call_count=$((fake_net_call_count + 1))
        echo "$fake_net_call_count" > "$COUNT_FILE"


        echo "[DEBUG] curl/wget call # $fake_net_call_count" >&2
        echo "[DEBUG] Current directory: $(pwd)" >&2

        printf '[DEBUG] Command: %s ' "$cmd" >&2
        printf '%q ' "$@" >&2
        printf '\n' >&2

        # 根据调用序号执行预设命令
        case "$fake_net_call_count" in
            *)
                # 未设置预设命令
                echo "[DEBUG] 未设置预设命令" >&2
                ;;
        esac

        echo >&2

    } 200>"$LOCK_FILE"

    # 永远返回成功
    return 0
}

# =====================================================
# curl 壳
# =====================================================
curl() {
    __fake_net_common curl "$@"
}

# =====================================================
# wget 壳
# =====================================================
wget() {
    __fake_net_common wget "$@"
}

# 导出函数,使其在子shell中也可用
export -f curl
export -f wget
export -f __fake_net_common

# ------------------------------------------
# 提示加载成功
# ------------------------------------------
echo "[INFO] fake-curl-wget.sh 已加载"
echo "[INFO] 所有 curl / wget 调用将被拦截"
再试试
source fake-curl-wget.sh
bash install.sh
这下日志正常了.


但是多次执行, 发现日志中的 wget 命令的顺序会变化. 
哎, 看来本文一开头, 我的口气太大了, 哈哈.
并不是所有的脚本在向外访问网络时, 用到的命令的次序都是固定的.

即使修改脚本, 把那几个download改成不后台了. wget 命令的顺序固定了.
[[ ! $is_core_file ]] && download core
[[ ! $local_install ]] && download sh
[[ $jq_not_found ]] && download jq
但是, 临时目录的目录名也有随机字符. 
哎呀, 再次打脸了, 就算次序固定了, 也可能用到随机字符的临时目录呀.
不能提前编写预设命令.

我想了一个办法, 能不能让脚本暂停这在这里, 等使用者自己把文件上传到指定的位置, 再继续?

脚本暂停的方案

我想
1) 当根据序号 进行case逻辑 发现没有匹配的预设命令时, 脚本暂停. 等待使用者输入.
2) 根据日志打印的 curl 或 wget 命令及参数. 使用者判断 当前序号的操作是要保存文件, 还是要输出信息到stdout.
2a) 如果当前操作是保存文件, 则 使用者自己上传文件到指定位置. 输入 空. 脚本继续执行.
2b) 如果当前操作是输出 信息到 stdout, 则 使用者输入 替代指令. 脚本执行替代指令.
如, 使用者输入 echo "something" 或 cat /path/to/file
把这一段发给GPT, 再结合我自己的整理, 得到:

版本V3

再试试
source fake-curl-wget.sh
bash install.sh

在保存文件的部分都是正常的.
在 wget --no-check-certificate -4 -qO- https://one.one.one.one/cdn-cgi/trace 时, 
我输入 wget --no-check-certificate -4 -qO- https://www.cloudflare.com/cdn-cgi/trace
程序就卡死了.

拿着两个 .sh 的脚本内容复制粘贴到GPT里, 再加上刚刚的日志打印, 一起问GPT.
原因是 当执行我输入的 wget xxxx 时, 不是调用系统的wget, 而调用了我的 wget 壳子.

所以再根据GPT的提示, 修改脚本.

版本V4


搞定!



上传Github



后记

如果你看不懂 wget 命令和 curl 命令的参数, 你可以问GPT

以下是我的shell终端操作和日志

root@localhost:~# source fake-curl-wget.sh
[INFO] fake-curl-wget.sh 已加载
[INFO] 所有 curl / wget 调用将被拦截
root@localhost:~# bash install.sh

........... Xray script by 233boy ..........

10:43:06) 开始安装...
10:43:06) 提醒!!! 无法设置自动同步时间, 可能会影响使用 VMess 协议.
10:43:06) 下载 Xray > https://github.com/xtls/xray-core/releases/latest/download/xray-linux-64.zip
10:43:06) 下载 Xray 脚本 > https://github.com/233boy/xray/releases/latest/download/code.zip
[DEBUG] curl/wget call # 1
[DEBUG] Current directory: /root
[DEBUG] Command: wget --no-check-certificate -4 -qO- https://one.one.one.one/cdn-cgi/trace
[DEBUG] 未设置预设命令

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[WARN] 序号 1 未设置预设命令
请根据 curl/wget的原始命令及参数 进行操作:
 - 如需保存文件:请手动上传文件到目标位置,然后输入空行继续
 - 如需输出到stdout:请输入替代命令(如: echo "something" 或 cat /path/file)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[INPUT] 请输入替代命令(或按回车表示文件已上传):
wget --no-check-certificate -4 -qO- https://www.cloudflare.com/cdn-cgi/trace

[DEBUG] curl/wget call # 2
[DEBUG] Current directory: /root
[DEBUG] Command: wget --no-check-certificate -t 3 -q -c https://github.com/xtls/xray-core/releases/latest/download/xray-linux-64.zip -O /tmp/tmp.ce4nDtgcDI/tmpcore
[DEBUG] 未设置预设命令

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[WARN] 序号 2 未设置预设命令
请根据 curl/wget的原始命令及参数 进行操作:
 - 如需保存文件:请手动上传文件到目标位置,然后输入空行继续
 - 如需输出到stdout:请输入替代命令(如: echo "something" 或 cat /path/file)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[INPUT] 请输入替代命令(或按回车表示文件已上传):

请问我现在应该做什么?

这是DeepSeek的回答

评论

The Hot3 in Last 7 Days

酒馆SillyTavern 玩英文角色卡 也能以中文输出 设置世界书Lorebooks

极简一键脚本 搭NaiveProxy梯子 自定义端口 与Caddy V2前置的VLESS/Vmess V2Ray共存 可开CDN