某神必的 Bash 代码分析
这个样例代码是$$的一键搭建脚本,老朽常年懒得看代码,唯独对这个又臭又长的神必脚本有点好奇(这里面可是有一千多行呢)
控制台下的颜色
颜色定义
1 | red='\033[0;31m' |
使用方法
1 | echo -e "[${red}Error${plain}] This script must be run as root!" |
记得把颜色切换回来啊
判断是否为ROOT用户执行脚本
if的简略写法
1 | [[ $EUID -ne 0 ]] && echo -e "[${red}Error${plain}] This script must be run as root!" && exit 1 |
RUID
即用户UID
,进程真实用户号。创建该进程的用户的UID
为该进程的真实用户号RUID
EUID
用于系统决定用户对文件资源的访问权限,一般情况下等同于RUID
。 原文链接
关闭SELinux
bash函数 if, sed, grep的用法
1 | disable_selinux(){ |
安全增强式Linux(SELinux,Security-Enhanced Linux)是一个Linux内核的安全模块,其提供了访问控制安全策略机制 全文链接
setenforce 0
: 临时关闭selinux 原文链接
-s
的表达式用于判断文件存在且不为空。当config文件存在时,该表达式为真 原文链接
grep
判断文件内是否存在某字符串,若字符串存在,即该命令成功执行,该表达式为真 参考文章
sed s/textA/textB
用于把textA
替换为textB
-i
参数代表直接修改读取的文件内容,而不是输出到终端/g
代表若一行内多次出现被textA
,则将所有textA
全部替换为textB
更多资料
系统发行版判断
grep, if用法
1 | if [[ -f /etc/redhat-release ]]; then |
if
的-f
判断文件是否存在 参考资料grep
的-E
是使用正则表达式的扩展语法,-q
不打印任何标准输出,-i
忽略大小写 更多资料
检查内核版本是否高于3.7.0
tr, cut, sort, head, test, uname的用法,Linux特殊变量
1 | version_gt(){ |
uname
用于打印当前系统相关信息-r
显示操作系统的发行编号 uname的更多用法cut
命令用来显示行中的指定部分,删除文件中指定字段-d
指定字段的分隔符-f
显示指定字段的内容 cut的更多用法
Example 1
2
3# uname -r
# uname -r | cut -d- -f1
# uname -r | cut -d- -f2
1 | 4.15.0-52-generic |
test
命令用于检查某个条件是否成立 test的样例代码$@
传递给脚本或函数的所有参数$1
表示第一个参数 更多特殊变量tr
命令用于转换或删除文件中的字符 tr的更多资料sort -V
命令用于版本的排序 参考资料head -n 1
只显示第一行的内容 head的更多资料
Example 脚本文件 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21fun1() {
echo ---fun1---
echo $@
echo ---end---
}
fun2() {
echo ---fun2---
echo $1
echo ---end---
}
fun3() {
echo ---fun3---
echo "$@" | tr " " "\n" | sort -V
echo ---end---
}
fun1 4.15.0 3.7.0
fun2 4.15.0 3.7.0
fun3 4.15.0 3.7.0
fun3 2.15.0 3.7.0
脚本输出 1
2
3
4
5
6
7
8
9
10
11
12
13
14---fun1---
4.15.0 3.7.0
---end---
---fun2---
4.15.0
---end---
---fun3---
3.7.0
4.15.0
---end---
---fun3---
2.15.0
3.7.0
---end---
判断是否为64位系统
if判断命令输出内容
1 | is_64bit(){ |
getconf
命令获取系统配置变量值 更多信息
获取具体发行版版本
awk用法, 捕获标准输出作为变量值
1 | get_opsy(){ |
AWK
是一种处理文本文件的语言-F
指定输入文件折分隔符 `[awk的更多例子](http://www.runoob.com/linux/linux-comm-awk.html)
/DESCRIPTION/{print $2}用正则表达式匹配
DESCRIPTION`字符串,并输出该行第2列的内容
Example
1 | # cat /etc/lsb-release |
获得公网IPv4地址
ip, egrep, wget用法
1 | get_ip(){ |
ip
命令是网路管理命令,ip addr
查看所有已分配到网络接口的地址egrep
即等于grep -E
,使用扩展的正则表达式语法,-o
只输出匹配的行,-v
只输出不匹配的结果
egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
是匹配出IPv4地址egrep -v "^192\.168|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-2]\.|^10\.|^127\.|^255\.|^0\."
过滤掉本地地址,如192.168.1.1[ -z ${IP} ]
如果IP变量为空,该表达式为真,&&
后的语句会被执行
wget
命令用来从指定的URL下载文件-t
设置下载失败重试最大次数-T
设置网络超时时间-q
安静模式-O-
把结果输出到控制台(把结果输出到标准输出
ipv4.icanhazip.com
,ipinfo.io/ip
访问这两个网站可以得到公网IP地址
获得IPv6地址
逻辑运算顺序
1 | get_ipv6(){ |
&&
的运算优先级高于||
百度百科
· 所以这句话可写为( ${ipv6}为空 && 返回1 ) || 返回0
,即ipv6
变量为空的时候,返回值为1(报错)
下载文件
判断命令执行失败
1 | download(){ |
wget
命令,--no-check-certificate
不检查HTTPS站点的证书,-c
继续下载之前未完成下载的文件,-O
输出到文件$?
是上个命令的退出状态,或函数的返回值,为0是正常退出,其他值为异常退出 更多特殊变量
输入密码不显示字符
stty用法
1 | get_char(){ |
stty
命令修改终端命令行的相关设置 更多资料-g
以stty可读方式打印当前的所有配置-echo
禁止回显cbreak
开启立即响应 参考资料 立即响应的解释dd if=/dev/tty bs=1 count=1 2>/dev/null
则是获取刚刚输入的字符 全解析-raw
不对输入的信息进行处理,如忽略Ctrl+C 更多信息
- 综上,这串代码可以模拟Linux输入密码无回显,但是一次只能读入一个字符,类似于C语言的
getchar()
带异常检测批量安装程序
字符串数组,for循环,stderr重定向
1 | error_detect_depends(){ |
${command} > /dev/null 2>&1
的2>&1
是指把stderr的错误信息重定向到stdout 更多信息${apt_depends[@]}
表示apt_depends内的所有元素 更多信息
Example 测试脚本内容 1
2
3
4
5
6
7
8apt_depends=(
gettext build-essential unzip gzip python python-dev python-setuptools curl openssl libssl-dev
)
echo ${apt_depends[@]}
for depend in ${apt_depends[@]}; do
echo ${depend}
done
输出结果 1
2
3
4
5
6
7
8
9
10
11gettext build-essential unzip gzip python python-dev python-setuptools curl openssl libssl-dev
gettext
build-essential
unzip
gzip
python
python-dev
python-setuptools
curl
openssl
libssl-dev
用户输入数字,并判断数值是否有效,或使用随机数值
shuf, if, read用法,判断输入为纯数字的方法
1 | install_prepare_port() { |
shuf
命令的用法有很多,这里用来生成随机数 更多用法echo -e
可让echo
处理字符串内的特殊字符,例如输出\n
时换行而不是显示一个斜杠和n 参考资料expr
命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值 更多资料
- 值得注意的是,
expr ${shad0wsocksport} + 1 &>/dev/null
,这一句话利用了给非数字做加法会报错来判断输入内容是否为纯数字 ${shad0wsocksport:0:1}
这句的:0:1
指获取字符串从0位置起长度为1的子串 参考资料
多选一程序
for循环, case, while, 数组用法
1 | software=(shad0wsocks-Python shad0wsocksR shad0wsocks-Go shad0wsocks-libev) |
${\#software\[@\]}
可获取数组长度 更多写法case "${selected}" in 1|2|3|4)
指让selected
这个变量匹配1-4的数字范围 更多写法*)
相当于default
,;;
标志着语块的结束 更多写法
脚本输出配置文件
cat, EOF用法
1 | cat > ${shad0wsocks_python_config}<<-EOF |
cat << EOF
是Here Document相关语句,Here Document是在Linux Shell 中的一种特殊的重定向方式,它的作用就是将两个delimiter (EOF) 之间的内容 (Here Document Content部分) 传递给程序 (cat) 作为输入参数 更多信息 如果重定向的操作符是<<-
,那么分界符(EOF)所在行的开头部分的制表符(Tab)都将被去除 更多信息