背景
我们在 Linux 命令行中,经常会需要使用代理(参考:How To Use Proxy Server To Access Internet at Shell Prompt With http_proxy Variable,Linux Proxy Server Settings – Set Proxy For Command Line),以便能访问某些资源。
在我使用 Clash 开启了一个代理,端口为 7890 后,我常常用下面的 shell 代码在命令行中启用或取消代理。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | # 可以把代码写入 bash 和 zsh 的启动文件中,新开 session 中可用#cat >> ~/.bashrc >> ~/.zshrc << 'EOF'
 export proxy=localhost:7890
 # 如果是在 WSL 中,可以替换为下面的语句
 #export proxy=$(cat /etc/resolv.conf | grep -m 1 nameserver | awk '{ print $2 }'):7890;
 # proxy+ 命令用于启用代理
 alias proxy+='\
 export https_proxy=http://$proxy
 export http_proxy=http://$proxy
 export all_proxy=socks5://$proxy
 export no_proxy=localhost,127.0.0.1,::1
 echo "proxy has been set to: $proxy"
 curl ipinfo.io';
 # proxy- 命令用于停用代理
 alias proxy-='\
 unset http_proxy https_proxy all_proxy no_proxy
 echo "proxy has been cleared";
 curl ipinfo.io';
 #EOF
 
 | 
为了便于做到这一点,我从自己的偏好出发,编写了一个 shell 脚本,已经在 bash 和 zsh 中通过测试。
代码实现
TIPS 代码的最新版本在 GitHub Gist 中维护
https://gist.github.com/ChenyangGao/b5abf2de8bb5a91753764f9403adcc99
文件名称为 mod-proxy.sh,代码中端口的默认值 PROXY_PORT 设为 7890,是 Clash 的默认端口,实现如下:
mod-proxy.sh| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 
 | #? 设置代理 ip,请运行 proxy# 获取帮助信息
 # AUTHOR='ChenyangGao <https://chenyanggao.github.io/>''
 # VERSION=0.2
 
 if [ -n "${MOD_PROXY_sourced+x}" ]; then
 return 0
 fi
 
 # inetutils (apt)
 # net-tools (yum)
 -proxy-get-local-ipv4-using-hostname() {
 hostname -I 2>&- | awk '{print $1}'
 }
 
 # iproute2
 -proxy-get-local-ipv4-using-iproute2() {
 # OR ip route get 1.2.3.4 | awk '{print $7}'
 ip -4 route 2>&- | awk '{print $NF}' | grep -Eo --color=never '[0-9]+(\.[0-9]+){3}'
 }
 
 # net-tools
 -proxy-get-local-ipv4-using-ifconfig() {
 ( ifconfig 2>&- || ip addr show 2>&- ) | grep -Eo '^\s+inet\s+\S+' | grep -Eo '[0-9]+(\.[0-9]+){3}' | grep -Ev '127\.0\.0\.1|0\.0\.0\.0'
 }
 
 # 获取本机 IPv4 地址
 -proxy-get-local-ipv4() {
 set -o pipefail
 -proxy-get-local-ipv4-using-hostname || -proxy-get-local-ipv4-using-iproute2 || -proxy-get-local-ipv4-using-ifconfig
 }
 
 -proxy-get-local-ipv4-select() {
 local ips=$(-proxy-get-local-ipv4)
 local retcode=$?
 if [ $retcode -ne 0 ]; then
 return $retcode
 fi
 grep -m 1 "^192\." <<<"$ips" || \
 grep -m 1 "^172\." <<<"$ips" || \
 grep -m 1 "^10\." <<<"$ips" || \
 head -n 1 <<<"$ips"
 }
 
 -proxy-get-remote-ssh-ip-using-ssh-env() {
 ( [ -n "$SSH_CLIENT" ] && awk '{print $1}' <<<"$SSH_CLIENT" ) || \
 ( [ -n "$SSH_CONNECTION" ] && awk '{print $1}' <<<"$SSH_CONNECTION" )
 }
 
 -proxy-get-remote-ssh-ip-using-who() {
 who am i 2>&- | awk '{print $NF}' | grep '([^)]\+)' | grep -o '[^()]\+'
 }
 
 # 获取远程连接到本机的 ssh 客户端的 ip 地址
 -proxy-get-remote-ssh-ip() {
 -proxy-get-remote-ssh-ip-using-ssh-env || -proxy-get-remote-ssh-ip-using-who
 }
 
 # 默认的代理服务的主机地址,可以是 ip 或 域名
 PROXY_HOST=$(( -proxy-get-local-ipv4-select | head -n 1 ) || echo 127.0.0.1)
 # 默认的代理端口(clash 的默认端口是 7890),0-65535 之间的数字
 PROXY_PORT=7890
 
 -proxy-format-proxy() {
 if [ -z "$1" ]; then
 echo "$PROXY_HOST:$PROXY_PORT"
 elif [[ $1 == \[* ]]; then
 # as ipv6
 if [[ $1 == *\] ]]; then
 # as without port
 echo "$1:$PROXY_PORT"
 else
 # as with port
 echo "$1"
 fi
 elif [[ $1 =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then
 # as ipv4 (without port)
 echo "$1:$PROXY_PORT"
 elif [[ $1 =~ ^:[0-9]+$ ]]; then
 # as bare port
 echo "$PROXY_HOST$1"
 elif [[ $1 == *:*:* && $1 =~ ^[0-9a-zA-Z:]+$ ]]; then
 # as ipv6 (without port)
 echo "[$1]:$PROXY_PORT"
 elif [[ $1 != *:* ]]; then
 # as hostname without port
 echo "$1:$PROXY_PORT"
 else
 # no op, here may be a mistake
 echo "$1"
 fi
 }
 
 # 在 shell 中启用代理,默认采用本机上的 7890 端口的代理服务
 proxy-set() {
 local proxy=$(-proxy-format-proxy "$1")
 export https_proxy=http://$proxy
 export http_proxy=http://$proxy
 export all_proxy=socks5://$proxy
 export no_proxy='localhost,127.0.0.1,::1'
 echo "proxy has been set to: $proxy"
 ## Get public ip in Linux command line
 ## NOTE curl ≈ wget -qO -
 # curl api.ipify.org
 # curl checkip.amazonaws.com
 # curl cip.cc
 # curl httpbin.org/ip
 # curl ifconfig.cat # ipv6
 # curl ifconfig.co # ipv6
 # curl ifconfig.io # ipv6
 # curl ifconfig.me
 # curl ifconfig.net # ipv6
 # curl inet-ip.info
 # curl ipinfo.io # 👍
 # curl ipv4.icanhazip.com
 # curl ipv4.ident.me
 # curl ipv4.ip.gs
 # curl ipv4.ip.sb
 # curl ip-api.com # 👍
 # curl ip.threep.top
 # curl ipecho.net/plain
 # curl myip.dnsomatic.com # 👎
 # curl myip.ipip.net
 # curl https://checkipv4.dedyn.io
 # curl https://ip.tool.lu
 # dig +short myip.opendns.com @resolver1.opendns.com
 # dig ANY +short @resolver2.opendns.com myip.opendns.com
 curl ipinfo.io
 }
 alias proxy+=proxy-set
 
 # 在 shell 中启用代理,代理服务来自 ssh 连接到这台机器的远程机器(仅适用于局域网)
 proxy-set-from-remote() {
 -proxy-get-remote-ssh-ip 1>&- && proxy-set $(-proxy-get-remote-ssh-ip)
 }
 alias proxy++=proxy-set-from-remote
 
 # 在 shell 中停用代理
 proxy-clear() {
 unset http_proxy https_proxy all_proxy no_proxy
 echo 'proxy has been cleared'
 curl ipinfo.io
 }
 alias proxy-=proxy-clear
 
 # 打印 export 命令,用于启用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用)
 proxy-export() {
 local proxy=$(-proxy-format-proxy "$1")
 echo "\
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 export https_proxy=http://$proxy
 export http_proxy=http://$proxy
 export all_proxy=socks5://$proxy
 export no_proxy='localhost,127.0.0.1,::1'
 # 用以下命令停用代理
 # unset http_proxy https_proxy all_proxy no_proxy"
 }
 alias proxy@=proxy-export
 
 # 打印 alias 命令,用于启用和停用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用),其中:
 # proxy+ 在 shell 中启用代理
 # proxy- 在 shell 中停用代理
 proxy-alias() {
 local proxy=$(-proxy-format-proxy "$1")
 cat << EOF
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 alias proxy+="\
 export https_proxy=http://$proxy
 export http_proxy=http://$proxy
 export all_proxy=socks5://$proxy
 export no_proxy='localhost,127.0.0.1,::1'
 echo 'proxy has been set to: $proxy'
 curl ipinfo.io"
 
 # 在 shell 中停用代理,命令可在局域网中的其他机器上使用
 alias proxy-="\
 unset http_proxy https_proxy all_proxy no_proxy
 echo 'proxy has been cleared'
 curl ipinfo.io"
 EOF
 }
 alias proxy@@=proxy-alias
 
 alias proxy#='echo "\
 [环境变量]
 
 PROXY_HOST  默认的代理服务的主机地址,可以是 ip 或 域名
 PROXY_PORT  默认的代理端口(clash 和 ssr(shadowsocksR,酸酸乳) 等科学上网软件的默认端口是 7890),0-65535 之间的数字
 ----------------------------------------
 [命令]
 
 proxy#   输出使用说明
 proxy+   在 shell 中启用代理,默认采用本机上的 7890 端口(clash 和 ssr 等科学上网软件的默认端口)的代理服务。如果要指定端口,运行形如 PROXY_PORT=<port> proxy+
 proxy++  在 shell 中启用代理,代理服务来自 ssh 连接到这台机器的远程机器(仅适用于局域网)。如果要指定端口,运行形如 PROXY_PORT=<port> proxy++
 proxy-   停用 shell 中的代理
 proxy@   打印 export 命令,用于启用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用)
 proxy@@  打印 alias 命令,用于启用和停用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用)"'
 
 MOD_PROXY_sourced=
 
 
 | 
 
使用说明
假设上面的代码位于 /path/to/mod-proxy.sh。要加载这个脚本,就在命令行运行如下命令
| 1
 | source /path/to/mod-proxy.sh
 | 
或者修改当前用户所用 shell 的配置文件,对于 bash 就是 ∼/.bashrc ,对于 zsh 就是 ∼/.zshrc。把上面的命令添加到对应的配置文件中去。
打印帮助信息
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | $ proxy#[环境变量]
 
 PROXY_HOST  默认的代理服务的主机地址,可以是 ip 或 域名
 PROXY_PORT  默认的代理端口(clash 和 ssr(shadowsocksR,酸酸乳) 等科学上网软件的默认端口是 7890),0-65535 之间的数字
 ----------------------------------------
 [命令]
 
 proxy#   输出使用说明
 proxy+   在 shell 中启用代理,默认采用本机上的 7890 端口(clash 和 ssr 等科学上网软件的默认端口)的代理服务。如果要指定端口,运行形如 PROXY_PORT=<port> proxy+
 proxy++  在 shell 中启用代理,代理服务来自 ssh 连接到这台机器的远程机器(仅适用于局域网)。如果要指定端口,运行形如 PROXY_PORT=<port> proxy++
 proxy-   停用 shell 中的代理
 proxy@   打印 export 命令,用于启用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用)
 proxy@@  打印 alias 命令,用于启用和停用 shell 中的代理(来自这台机器,命令可在局域网中的其他机器上使用)
 
 | 
打印 export 命令,在局域网的某台机器上启用本机上代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | # 可接受 1 个参数,用于指定 host:port。如果要单独指定 host,写作 host;如果要单独指定 port,写作 :port;如果省略参数,则用 $PROXY_HOST:$PROXY_PORT。$ proxy@
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 export https_proxy=http://192.168.1.2:7890
 export http_proxy=http://192.168.1.2:7890
 export all_proxy=socks5://192.168.1.2:7890
 export no_proxy='localhost,127.0.0.1,::1'
 # 用以下命令停用代理
 # unset http_proxy https_proxy all_proxy no_proxy
 $ proxy@ 192.168.1.66
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 export https_proxy=http://192.168.1.66:7890
 export http_proxy=http://192.168.1.66:7890
 export all_proxy=socks5://192.168.1.66:7890
 export no_proxy='localhost,127.0.0.1,::1'
 # 用以下命令停用代理
 # unset http_proxy https_proxy all_proxy no_proxy
 
 | 
上述命令输出几个 export 命令,可以在同一局域网中的任何 Linux 机器的命令行中运行,即可启用代理。
如果要取消代理,在那台机器的命令行中运行
| 1
 | unset http_proxy https_proxy all_proxy no_proxy
 | 
打印 alias 命令,在局域网的某台机器上启用和停用本机上代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | # 可接受 1 个参数,用于指定 host:port。如果要单独指定 host,写作 host;如果要单独指定 port,写作 :port;如果省略参数,则用 $PROXY_HOST:$PROXY_PORT。$ proxy@@
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 alias proxy+="export https_proxy=http://192.168.1.2:7890
 export http_proxy=http://192.168.1.2:7890
 export all_proxy=socks5://192.168.1.2:7890
 export no_proxy='localhost,127.0.0.1,::1'
 echo 'proxy has been set to: 192.168.1.2:7890'
 curl ipinfo.io"
 
 # 在 shell 中停用代理,命令可在局域网中的其他机器上使用
 alias proxy-="unset http_proxy https_proxy all_proxy no_proxy
 echo 'proxy has been cleared'
 curl ipinfo.io"
 $ proxy@@ 192.168.1.66
 # 在 shell 中启用代理,命令可在局域网中的其他机器上使用
 alias proxy+="export https_proxy=http://192.168.1.66:7890
 export http_proxy=http://192.168.1.66:7890
 export all_proxy=socks5://192.168.1.66:7890
 export no_proxy='localhost,127.0.0.1,::1'
 echo 'proxy has been set to: 192.168.1.66:7890'
 curl ipinfo.io"
 
 # 在 shell 中停用代理,命令可在局域网中的其他机器上使用
 alias proxy-="unset http_proxy https_proxy all_proxy no_proxy
 echo 'proxy has been cleared'
 curl ipinfo.io"
 
 | 
上述命令可输出 2 个 alias 命令,可以在同一局域网中的任何 Linux 机器的命令行中运行,以生成别名:
proxy+
在 shell 中启用代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | $ proxy+proxy has been set to: 192.168.1.2:7890
 {
 "ip": "103.47.100.220",
 "city": "Sham Shui Po",
 "region": "Sham Shui Po",
 "country": "HK",
 "loc": "22.3302,114.1595",
 "org": "AS38136 Akari Networks",
 "timezone": "Asia/Hong_Kong",
 "readme": "https://ipinfo.io/missingauth"
 }
 
 | 
proxy-
在 shell 中停用代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | $ proxy-proxy has been cleared
 {
 "ip": "111.3.34.208",
 "city": "Shanghai",
 "region": "Shanghai",
 "country": "CN",
 "loc": "31.2222,121.4581",
 "org": "AS56041 China Mobile communications corporation",
 "timezone": "Asia/Shanghai",
 "readme": "https://ipinfo.io/missingauth"
 }
 
 | 
shell 命令:proxy+、proxy++ 和 proxy-
proxy+
在 shell 中启用本机上代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | # 可接受 1 个参数,用于指定 host:port。如果要单独指定 host,写作 host;如果要单独指定 port,写作 :port;如果省略参数,则用 $PROXY_HOST:$PROXY_PORT。$ proxy+
 proxy has been set to: 192.168.1.2:7890
 {
 "ip": "103.47.100.220",
 "city": "Sham Shui Po",
 "region": "Sham Shui Po",
 "country": "HK",
 "loc": "22.3302,114.1595",
 "org": "AS38136 Akari Networks",
 "timezone": "Asia/Hong_Kong",
 "readme": "https://ipinfo.io/missingauth"
 }
 
 | 
proxy++
在 shell 中启用远程代理,代理服务来自 ssh 连接到这台机器的远程机器(仅适用于局域网)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | $ proxy++proxy has been set to: 192.168.1.66:7890
 {
 "ip": "103.47.100.220",
 "city": "Sham Shui Po",
 "region": "Sham Shui Po",
 "country": "HK",
 "loc": "22.3302,114.1595",
 "org": "AS38136 Akari Networks",
 "timezone": "Asia/Hong_Kong",
 "readme": "https://ipinfo.io/missingauth"
 }
 $ PROXY_PORT=1234 proxy++
 proxy has been set to: 192.168.1.66:1234
 {
 "ip": "103.47.100.220",
 "city": "Sham Shui Po",
 "region": "Sham Shui Po",
 "country": "HK",
 "loc": "22.3302,114.1595",
 "org": "AS38136 Akari Networks",
 "timezone": "Asia/Hong_Kong",
 "readme": "https://ipinfo.io/missingauth"
 }
 
 | 
proxy-
在 shell 中停用本机上代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | $ proxy-proxy has been cleared
 {
 "ip": "111.3.34.208",
 "city": "Shanghai",
 "region": "Shanghai",
 "country": "CN",
 "loc": "31.2222,121.4581",
 "org": "AS56041 China Mobile communications corporation",
 "timezone": "Asia/Shanghai",
 "readme": "https://ipinfo.io/missingauth"
 }
 
 |