Shell脚本实现在Linux命令行开启代理

Shell脚本实现在Linux命令行开启代理

背景

我们在 Linux 命令行中,经常会需要使用代理(参考:How To Use Proxy Server To Access Internet at Shell Prompt With http_proxy VariableLinux Proxy Server Settings – Set Proxy For Command Line),以便能访问某些资源。

在我使用 Clash 开启了一个代理,端口为 7890 后,我常常用下面的 shell 代码在命令行中启用或取消代理。

1
2
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 脚本,已经在 bashzsh 中通过测试。

代码实现

TIPS 代码的最新版本在 GitHub Gist 中维护
https://gist.github.com/ChenyangGao/b5abf2de8bb5a91753764f9403adcc99

文件名称为 mod-proxy.sh,代码中端口的默认值 PROXY_PORT 设为 7890,是 Clash 的默认端口,实现如下:

mod-proxy.sh
1
2
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
#? 设置代理 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。把上面的命令添加到对应的配置文件中去。

打印帮助信息

1
2
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 命令,在局域网的某台机器上启用本机上代理

1
2
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 命令,在局域网的某台机器上启用和停用本机上代理

1
2
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 中启用代理

1
2
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 中停用代理

1
2
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 中启用本机上代理

1
2
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 连接到这台机器的远程机器(仅适用于局域网)

1
2
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 中停用本机上代理

1
2
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"
}

评论

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×