MacOS下一些有用的脚本

摘要

  • 这里列举一些macos下一些有用的shell脚本

  • 与本文脚本对应的centos脚本可以参看centos-shell

查看端口被哪个进程占用

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env zsh
#################################################
#filename:port 授予执行权限
#功能:查询端口被哪个进程占用
#执行方法:port 8080
#################################################
port=$1

if [ -z "$port" ]; then
echo "请加上端口作为参数,如:port 8080"
else
lsof -i :${port}
fi

杀掉占用某个端口的进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env zsh
#################################################
#filename:killport 授予执行权限
#功能:关闭占用端口的进程
#执行方法:killport 8080
#################################################
if [ -z "$1" ]; then
echo "请加上端口作为参数,如:killport 8080"
else
echo "kill begin........"
pid=`port $1 | awk 'NR>1 {print $2}' | sort | uniq`
echo $pid
if [ -z "$pid" ] ; then
echo "No process running."
#exit -1;

else
echo "The process(${pid}) is running..."

kill ${pid}

echo "kill process(${pid}) OK"
fi
fi

本机端口通过跳板机转发到远程端口

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
#!/usr/bin/env zsh
#################################################
#filename:ssh_port 授予执行权限
#功能:本机端口通过跳板机转发到远程端口
#执行方法:
# ssh_port start 8080 10.10.2.2 80
# ssh_port stop 8080
# ssh_port status 8080
#################################################

check(){
#验证参数是否为空
if [[ ${#@} < 1 ]];then
echo "请输入参数!"
echo "开启端口转发:ssh_port start 8080 10.10.2.2 80"
echo "关闭端口转发:ssh_port stop 8080"
echo "查看端口状态:ssh_port status 8080"
exit 1
fi
}

check $@

# 工作目录
WORKDIR="/Users/$(whoami)/workdir"

# 操作类型
operation="$1"

# 本地端口
local_port="$2"

# 远程 IP 地址
remote_ip="$3"

# 远程端口
remote_port="$4"

# SSH 控制套接字文件
sock_file="$WORKDIR/$local_port.sock"

# 跳板机,~/.ssh/config中的配置
jump_name=jump-machine


start(){
echo "start port begin........"
pid=`port $local_port | grep ssh | awk '{print $2}'`
if [ -z "$pid" ] ; then
# 建立连接
ssh -nNT -S "$sock_file" -L 127.0.0.1:$local_port:$remote_ip:$remote_port $jump_name
echo "ssh -nNT -S ${sock_file} -L 127.0.0.1:${local_port}:${remote_ip}:${remote_port} ${jump_name}"
echo "start port success!"
sleep 1
else
echo "port is running."
fi
}

stop(){
echo "stop port begin........"
pid=`port $local_port | grep ssh | awk '{print $2}'`
if [ -z "$pid" ] ; then
echo "No port running."
else
echo "The port (${pid}) is running..."
# kill -9 $pid
ssh -S $sock_file -O exit $jump_name
echo "ssh -S $sock_file -O exit $jump_name"
echo "stop port (${pid}) success!"
fi
}

status(){
echo "check status port begin........"
pid=`port $local_port | grep ssh | awk '{print $2}'`
if [ -z "$pid" ] ; then
echo "No port running."
else
echo "The port (${pid}) is running..."
port $local_port
fi
}

if [ -n "$operation" ];then
# 判断操作类型
if [[ "$operation" == "start" ]]; then
# 建立连接
start
elif [[ "$operation" == "stop" ]]; then
# 关闭连接
stop
elif [[ "$operation" == "status" ]]; then
# 关闭连接
status
else
check
exit 1
fi
fi

查看域名到期时间

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
#!/usr/bin/env zsh
#################################################
#filename:check_domain 授予执行权限
#功能:检查域名到期时间
#执行方法:check_domain baidu.com google.com
#输出结果:
# baidu.com,过期日期:2026-10-11
# 距离到期还有958天
# google.com,过期日期:2028-09-14
# 距离到期还有1662天
#################################################

#验证域名参数是否为空
if [[ ${#@} < 1 ]];then
echo "请输入域名,多个域名空格分隔,如:check_domain baidu.com google.com"
fi

#检测域名到期时间
for domain in "$@";do
#取出域名过期时间
expire_date=`whois $domain | grep "Expiration Time" | awk '{print $3}'`
if [ -z "$expire_date" ];then
expire_date=`whois $domain | grep 'Expiry Date'| awk '{print $4}'| cut -d 'T' -f 1`
fi
echo "${domain},过期日期:${expire_date}"

#转换成时间戳
# 1.linux
# expire_date_timestamp=`date -d $expire_date +%s`

# 2.macos
# https://blog.csdn.net/zhw21w/article/details/126010972
expire_date_timestamp=`date -j -f %Y-%m-%d $expire_date +%s`
#以时间戳的形式显示当前时间
now=`date '+%s'`
#域名到期剩余天数
time_left=$[$[$expire_date_timestamp-$now]/86400]
echo "距离到期还有${time_left}天"
done

查看证书到期时间

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
#!/usr/bin/env zsh
#################################################
#filename:ssl_check 授予执行权限
#功能:检查证书到期时间
#执行方法:ssl_check www.baidu.com www.google.com
#输出结果:
# 证书:www.baidu.com,过期时间:2024-08-06 01:51:05
# 距离到期还有161天
# 证书:www.google.com,过期时间:2024-04-29 08:19:49
# 距离到期还有62天
#################################################

#验证证书参数是否为空
if [[ ${#@} < 1 ]];then
echo "请输入证书,多个证书空格分隔,如:ssl_check www.baidu.com www.google.com"
fi


#获取证书的有效时间
for domain in "$@";do
# 这里在openssl x509 -noout -dates后面加上了2>/dev/null,否则会打印告警信息:Warning: Reading certificate from stdin since no -in or -new option is given
# 您可以忽略此警告。它只是表明您没有使用 -in 或 -new 选项指定输入文件,因此 OpenSSL 将从标准输入 (stdin) 读取证书。
time=$(echo | openssl s_client -connect $domain:443 2>/dev/null | openssl x509 -noout -dates 2>/dev/null |awk -F'=' 'NR==2{print $2}')
#echo $time

#取出域名过期时间
expire_date=`env LC_ALL=en_US.en date -j -f '%b %d %H:%M:%S %Y GMT' "$time" '+%Y-%m-%d %H:%M:%S'`
echo "证书:$domain,过期时间:$expire_date"

expire_date_timestamp=`date -j -f '%Y-%m-%d %H:%M:%S' $expire_date +%s`
#以时间戳的形式显示当前时间
now=`date '+%s'`
#证书到期剩余天数
time_left=$[$[$expire_date_timestamp-$now]/86400]
echo "距离到期还有${time_left}天"
done

查看指定IP的详细信息

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
#!/usr/bin/env zsh
#################################################
# filename:ip-info 授权执行权限
# 功能:查看IP信息
# 使用方法:ip-info 10.10.2.24/24
# 输出结果:
# 网络地址: 10.10.2.0
# 网关地址: 10.10.2.1
# 广播地址: 10.10.2.255
# 主机地址范围: 10.10.2.1 到 10.10.2.254

# 也可以使用 sipcalc 命令,用法更多,显示也会更为详细:
# 安装:brew install sipcalc
# 使用示例:
# sipcalc 1.119.161.30/30
# sipcalc 10.10.2.32 255.255.255.0
# sipcalc 10.10.2.32 0xffffff00
#################################################

# 检查是否提供了正确数量的参数
if [ "$#" -ne 1 ]; then
echo "用法: $0 <IP地址/子网掩码CIDR>"
exit 1
fi

# 获取命令行参数
ip_cidr="$1"

# 分割IP地址和子网掩码
IFS='/' read -A ip_parts <<< "$ip_cidr"
ip=${ip_parts[1]}
subnet_cidr=${ip_parts[2]}

# 计算子网掩码的二进制表示
subnet_binary=$(printf '1%.0s' $(seq 1 "$subnet_cidr") && printf '0%.0s' $(seq 1 $((32 - $subnet_cidr))))

# 将二进制子网掩码转换为点分十进制表示
subnet_mask_decimal=$(printf "%d.%d.%d.%d" $((2#${subnet_binary:0:8})) $((2#${subnet_binary:8:8})) $((2#${subnet_binary:16:8})) $((2#${subnet_binary:24:8})))

# 使用按位与运算计算网络地址
network_address=$(printf "%d.%d.%d.%d" $(( $(echo $ip | cut -d. -f1) & $(echo $subnet_mask_decimal | cut -d. -f1) )) $(( $(echo $ip | cut -d. -f2) & $(echo $subnet_mask_decimal | cut -d. -f2) )) $(( $(echo $ip | cut -d. -f3) & $(echo $subnet_mask_decimal | cut -d. -f3) )) $(( $(echo $ip | cut -d. -f4) & $(echo $subnet_mask_decimal | cut -d. -f4) )))

# 计算广播地址和网关地址
broadcast_address=$(printf "%d.%d.%d.%d" $(( $(echo $network_address | cut -d. -f1) | (255 - $(echo $subnet_mask_decimal | cut -d. -f1) % 256) )) $(( $(echo $network_address | cut -d. -f2) | (255 - $(echo $subnet_mask_decimal | cut -d. -f2) % 256) )) $(( $(echo $network_address | cut -d. -f3) | (255 - $(echo $subnet_mask_decimal | cut -d. -f3) % 256) )) $(( $(echo $network_address | cut -d. -f4) | (255 - $(echo $subnet_mask_decimal | cut -d. -f4) % 256) )))

gateway_address=$(printf "%d.%d.%d.%d" $(echo $network_address | cut -d. -f1) $(echo $network_address | cut -d. -f2) $(echo $network_address | cut -d. -f3) $(( $(echo $network_address | cut -d. -f4) + 1)))

# 输出结果
echo "网络地址: $network_address"
echo "网关地址: $gateway_address"
echo "广播地址: $broadcast_address"

# 计算主机地址范围
first_host=$(printf "%d.%d.%d.%d" $(echo $network_address | cut -d. -f1) $(echo $network_address | cut -d. -f2) $(echo $network_address | cut -d. -f3) $(( $(echo $network_address | cut -d. -f4) + 1)))
last_host=$(printf "%d.%d.%d.%d" $(echo $broadcast_address | cut -d. -f1) $(echo $broadcast_address | cut -d. -f2) $(echo $broadcast_address | cut -d. -f3) $(( $(echo $broadcast_address | cut -d. -f4) - 1)))

echo "主机地址范围: $first_host$last_host"

查看本机网络信息

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
#!/usr/bin/env zsh
#################################################
#filename:ip
#执行方法:ip
#功能:查看本机网络信息
#################################################

# 默认获取外网ip超时3秒
maxtime=3

net_ip=$(curl --max-time $maxtime --silent https://ipv4.icanhazip.com)
MAC=$(ifconfig | grep en0: -A 7 | grep ether | cut -d " " -f 2)
local_ip=$(ifconfig | grep -A 1 "en" | grep broadcast | cut -d " " -f 2 | tr "\\n" " ")
DNS=$(nslookup localhost | grep Server | awk '{print $2}')

echo "MAC: ${MAC}"
echo "IP: ${local_ip} ${net_ip}"
echo "DNS: ${DNS}"

ip=`ifconfig | grep -A 1 "en" | grep broadcast | cut -d " " -f 2`
netmask=`ifconfig | grep -A 1 "en" | grep broadcast | cut -d " " -f 4`
Network=$(ipcalc -b $ip $netmask | grep Network | sed 's/ / /' | awk -F ' ' '{print $2}')

echo "\nIP详细信息:"
ip-info ${Network}

将十进制IP地址转换为二进制形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env zsh
#################################################
#功能:将十进制IP地址转换为二进制形式
#filename:ipTo2
#执行方法:ipTo2 192.168.16.155
#输出结果:11000000.10101000.00010000.10011011
#################################################

# 检查是否提供了正确数量的参数
if [ "$#" -ne 1 ]; then
echo "用法: ipTo2 192.168.16.155"
exit 1
fi

ip=$1
for i in $(echo ${ip} | tr '.' ' '); do echo "obase=2 ; $i" | bc; done | awk '{printf ".%08d", $1}' | cut -c2-

将二进制形式转换为十进制IP地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env zsh
#################################################
#功能:将十进制IP地址转换为二进制形式
#filename:2Toip
#执行方法:2Toip 11111111.11111111.11100000.00000000
#输出结果:255.255.224.0
#################################################

# 检查是否提供了正确数量的参数
if [ "$#" -ne 1 ]; then
echo "用法: 2Toip 11111111.11111111.11100000.00000000"
exit 1
fi

echo $1|awk -F. 'function bin2dec(a,b,i,c){b=length(a);c=0;for(i=1;i<=b;i++){c+=c;if(substr(a,i,1)=="1")c++}return c}{for(j=1;j<=NF;j++)printf("%d%s",bin2dec($j),j!=NF?".":"\n")}'

字符串转ascii码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env zsh
#################################################
#filename:strtoascii
#功能:字符串转ascii码
#执行方法:strtoascii 你好hello
#输出结果:\u4f60\u597d\u68\u65\u6c\u6c\u6f
#################################################

# 检查是否提供了正确数量的参数
if [ "$#" -ne 1 ]; then
echo "用法: strtoascii 你好hello"
exit 1
fi

str=$1
newstr=""

for ((i=0;$i<${#str};i=$i+1));
do
text="${str:$i:1}"
newtext=`printf "%x" "'$text"`;
newstr=$newstr"\\\\u"$newtext
done
echo $newstr

ascii码转字符串

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
#!/usr/bin/env zsh
#################################################
#filename:asciitostr
#功能: ascii码转字符串
#执行方法:asciitostr \u4f60\u597d\u68\u65\u6c\u6c\u6f
#输出结果:你好hello
#################################################

# 检查是否提供了正确数量的参数
if [ "$#" -ne 1 ]; then
echo "用法: asciitostr \u4f60\u597d\u68\u65\u6c\u6c\u6f"
exit 1
fi

# 检查是否包含\u,如果不包含,添加双引号
if [[ "$1" != *\\u* ]]; then
input=$(echo -e $1 | sed 's/u/\\u/g')
else
input=$1
fi
# 将Unicode转义序列解析为中文字符
result=$(echo -e $input | perl -pe 's/\\u([0-9a-fA-F]{4})/chr(hex($1))/eg')

# 输出结果
echo "$result"

获取java文件的jdk编译版本

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
#!/usr/bin/env zsh
#################################################
#filename:jdk_version
#功能:获取对应java文件的jdk编译版本
# 如果是jar,这里只会检索jar包中第一个class的jdk编译版本,所以也不一定准确
#
#执行方法:jdk_version <file_path:[.jar|.class|.war]>
# 1.jar示例:
# jdk_version xxl-job-admin-2.3.0.jar
# 输出:
# Build-Jdk: 1.8.0_271
# file_path:[xxl-job-admin-2.3.0.jar] major_version:[52] jdk_version:[jdk1.8]

# 2.class示例
# jdk_version XxlJobAdminConfig.class
# 输出:
# file_path:[XxlJobAdminConfig.class] major_version:[52] jdk_version:[jdk1.8]

# 3.war示例
# jdk_version wardir/BOSS.war
# 输出:
# file_path:[wardir/BOSS.war] jdk_version:[1.7.0_67-b01 (Oracle Corporation)]
#################################################

# 定义关联数组
typeset -A my_map

# 向关联数组中添加键值对
# LTS:
# 8(2014年初发布,免费版本至8u202,从8u211开始商用收费,支持到2030年9月)
# 11(2018年9月发布,免费版本版本至11.0.2,后续版本商用收费,支持到2032年9月)
# 17(2021年9月发布,免费,支持到2029年9月)
# 21(2023年9月发布,免费,支持到2031年9月)
# 25(预计2025年9月发布,支持到2033年9月)
# 后续Oracle计划每半年发布一个新的大版本(3、9月),每两年发布一次LTS(9月),建议正式环境只使用LTS版本
# 此处可以查看jdk发布计划:https://www.java.com/en/releases/matrix/
# JDK下载地址:https://www.oracle.com/cn/java/technologies/downloads/
my_map=(
[45]="jdk1.1"
[46]="jdk1.2"
[47]="jdk1.3"
[48]="jdk1.4"
[49]="jdk1.5"
[50]="jdk1.6"
[51]="jdk1.7"
[52]="jdk1.8"
[53]="jdk9"
[54]="jdk10"
[55]="jdk11"
[56]="jdk12"
[57]="jdk13"
[58]="jdk14"
[59]="jdk15"
[60]="jdk16"
[61]="jdk17"
[62]="jdk18"
[63]="jdk19"
[64]="jdk20"
[65]="jdk21"
[66]="jdk22"
[67]="jdk23"
[68]="jdk24"
[69]="jdk25"
)

# 获取所有键并排序
sorted_keys=(${(o)${(k)my_map}})
# echo $sorted_keys

original_string="\tmajor_version\tjdk_version"
# tr '[:lower:]' '[:upper:]' 将字符串中的小写字母转换为大写字母。输出结果将是 "MAJOR_VERSION\tJDK_VERSION"
uppercase_string=$(echo "$original_string" | tr '[:lower:]' '[:upper:]')

# 打印map
echo_map() {
echo "${uppercase_string}"
# 按排序后的键输出键值对
for key in $sorted_keys; do
echo "\t$key\t\t${my_map[$key]}"
done
}

error_message() {
echo "Usage: jdk_version <file_path:[.jar|.class|.war]>"
echo_map
exit 1
}

# 检查是否传递了参数
if [ -z "$1" ]; then
error_message
fi

file_path=$1

# 检查文件是否存在
if [ ! -e "$file_path" ]; then
echo "文件不存在:file_path:[${file_path}]"
error_message
fi

greap_content="major version"

if [[ $file_path == *.class ]]; then
# 通过javap命令获取class文件的详细信息,grep过滤出包含 “major version” 的行,awk取出其版本号
key=$(javap -verbose ${file_path} | grep "${greap_content}" | awk -F': ' '{print $2}')
elif [[ $file_path == *.jar ]]; then
# 先通过“jar tf”命令获取jar包中的class文件,这里只取出第一个,注意这里class路径必须去掉“.class”,之后再通过javap命令获取“-classpath”所指定的jar中的这个第一个class文件的详细信息,grep过滤出包含 “major version” 的行,awk取出其版本号
key=$(javap -classpath ${file_path} -verbose $(jar tf ${file_path} | grep ".class$" | head -n 1 | sed s/.class//) | grep "${greap_content}" | awk -F': ' '{print $2}')
# 通过jar包中的MANIFEST.MF文件来判断打包的jdk版本
result=$(unzip -p ${file_path} $(jar tf ${file_path} | grep "MANIFEST.MF$" | head -n 1) | grep "^Build-Jdk")
if [ -z "$result" ]; then
result=$(unzip -p ${file_path} $(jar tf ${file_path} | grep "MANIFEST.MF$" | head -n 1) | grep "^Created-By")
fi

if [ -n "$result" ]; then
echo $result
else
echo "没有在 MANIFEST.MF 中发现jdk相关信息"
fi
elif [[ $file_path == *.war ]]; then
# 查找 Build-Jdk
jdk_version=$(unzip -p "${file_path}" META-INF/MANIFEST.MF | grep -i "^Build-Jdk" | awk -F': ' '{print $2}')

# 如果 Build-Jdk 未找到,则尝试查找 Created-By
if [ -z "$jdk_version" ]; then
jdk_version=$(unzip -p "${file_path}" META-INF/MANIFEST.MF | grep -i "^Created-By" | awk -F': ' '{print $2}')
fi
else
error_message
fi

# 打印结果
# 输出 JDK 版本信息
if [ -n "$key" ]; then
echo "file_path:[${file_path}]\tmajor_version:[$key]\tjdk_version:[${my_map[$key]}]"
elif [ -n "$jdk_version" ]; then
# jdk_version含有特殊字符,通过如下命令查看包含哪些特殊字符,结果发现包含\r\n
# echo "${jdk_version}" | od -c
# 去掉特殊字符 \r\n
jdk_version_cleaned=$(echo "${jdk_version}" | tr -d '\r\n')

echo "file_path:[${file_path}]\t\tjdk_version:[${jdk_version_cleaned}]"
else
echo "JDK Version information not found"
fi