shell编程的一些Tips

Posted by FanHao on 2019-03-18

前言

本文主要总结编写shelll脚本时会遇到的一些坑,另外总结shell编程中如何实现外部传参。

shell编程

注意事项

.sh文件头部需要添加如下信息,笔者曾因为漏写了bin前面的/,执行时出现各种未知问题。

1
#!/bin/bash

注意.sh文件的编码格式,打开.sh文件

1
vim example.sh

不进入.sh文件的编辑模式,输入如下命令然后回车查看文件格式

1
:set ff

文件编码的格式必须如下所示,为unix类型。如果是dos类型,执行脚本时可能会报错。

1
fileformat=unix

注意重定向和追加的区别

1
2
3
4
# 将输出内容重定向至test.log,此时test.log内容被覆盖为新内容。
$ echo "This is a Test." > /var/test.log
# 追加到test.log文件的内容后,不覆盖原有内容
$ echo "Test for >>" >> /var/test.log
常用函数理解
shift函数

主要用来左移位置参数。比如执行shift 2命令表示将原来的外部参数\$3变成\$1,原来的\$1和\$2被丢弃。而不带参数的shift命令相当于shift 1。创建test_shift.sh文件,内容如下所示。

1
2
3
4
5
6
#!/bin/sh
until [$# -eq 0]
do
echo "第一个参数为:$1,参数个数为:$#"
shift
done

执行脚本

1
./test_shift.sh a b c d
getopt函数

这里讲的getopt函数其实就是Linux系统的getopt命令,它主要用来传递参数,支持长参数和短参数。

在Linux命令中,给命令附上不同的参数,命令能够实现不同的功能,比如下面的例子。

1
ls -l

有时候我们需要在自己写的shell脚本上实现外部传参时,就需要用到getopt这个函数。执行如下命令查看帮助信息。比较常用的的是-a,-l,-o等选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ getopt -h
Usage:
getopt <optstring> <parameters>
getopt [options] [--] <optstring> <parameters>
getopt [options] -o|--options <optstring> [options] [--] <parameters>

Parse command options.

Options:
-a, --alternative allow long options starting with single -
-l, --longoptions <longopts> the long options to be recognized
-n, --name <progname> the name under which errors are reported
-o, --options <optstring> the short options to be recognized
-q, --quiet disable error reporting by getopt(3)
-Q, --quiet-output no normal output
-s, --shell <shell> set quoting conventions to those of <shell>
-T, --test test for getopt(1) version
-u, --unquoted do not quote the output

-h, --help display this help
-V, --version display version

-o后面为短参数,-l后面为长参数。具体这两者没什么区别。不过按照约定俗称,-后面接单个字母不带参数值,–后面接单词,带参数值。例如下面内容所示,其中短参数t后面可以带参数值,而长参数help,version不能带参数值。

1
2
argv=`getopt -a -o hv,t: -l name:,age:,help,version -- $@`
eval set -- $argv

创建test_getopt.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
#!/bin/sh
#
argv=`getopt -a -o hv -l name:,age:,help,version -- $@`
#将符合getopt参数规则的参数摆在前面,其他摆在后面,并在最后面添加--
eval set -- $argv
#help函数,打印此shell脚本的帮助信息
help()
{
Usage="Usage: $0\n\
options:\n\
[--name] \t ---your name\n \
[--age] \t ---your age\n \"
}
#函数,打印此shell脚本的版本
show_version()
{
echo "Version1.0"
}
#给定参数默认参数值
Name=""
Age=0
#解析外部传参数,将外部传入的参数值赋给内部变量
while true
do
case $i in:
--name)
Name=$2
shift 2
;;
--age)
Age=$2
shift 2
;;
-h | --help)
help
shift
;;
-v | --version)
show_version
shitf
;;
--)
break
;;
esac
done

if [-n $Name -a $Age -eq 0]
then
echo "Hello,My Name is $Name."
elif [-n $Name -a $Age -nq 0]
then
echo "Hello,My Name is $Name.I am $Age"
fi

外部传参并执行test_getopt.sh文件

1
2
3
$ chmod +x test_getopt.sh
$ ./test_getopt.sh -h
$ ./test_getopt.sh --name Alan --age 19

代码实例

实例一
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#!/bin/sh
#config_W522U.sh
# ---------------------------------------------------------------------#
# Description: 配置W522U网卡
#
# History:
# <author> <time> <version > <desc>
# ---------------------------------------------------------------------#


help()
{
Usage="Usage: $0\n\
Options:\n\
[--intf if_name] \t-- W522U接口名称,默认为ra0\n \
[--scan 2.4/5] \t-- 扫描无线列表信息(扫描2.4G或5G无线列表)\n \
[--ssid ssid] \t\t-- 连接SSID\n \
[--workhz 2.4/5] \t-- 连接无线的频段,默认2.4G\n \
[--channel 信道] \t-- 设置信道\n \
[--authmode mode] \t-- 认证模式 取值范围\"OPEN, SHARED, WPAPSK, WPA2PSK\"\n \
[--encryptype type] \t-- 加密类型 取值范围\"NONE, TKIP, AES, WEP\"\n \
[--wpapsk wpa密码] \t-- WPA密码\n \
[--defkeyid num] \t-- wep默认密码\n \
[--key1 wep密码1] \t-- wep密码1\n \
[--key2 wep密码2] \t-- wep密码2\n \
[--key3 wep密码3] \t-- wep密码3\n \
[--key4 wep密码4] \t-- wep密码4\n \
[-h | --help] \t\t-- 显示此帮助信息\n \
"
echo -e $Usage
}


#定义公共变量
normal='\033[0m';red='\033[31m';green='\033[32m';yellow='\033[33m'
Intf="ra0"
Scan="0"
WorkHz="2.4"
SSID=""
Channel=""
AuthMode="OPEN"
EncrypType="NONE"
WpaPsk=""
DefKeyId="1"
Key1="12345"
Key2="12345"
Key3="12345"
Key4="12345"

#定义选项变量
OPTIND=1
argv=`getopt -a -o h -l intf:,scan:,ssid:,channel:,workhz:,authmode:,encryptype:,wpapsk:,defkeyid:,key1:,key2:,key3:,key4:,help -- $@`
#[ $? -ne 0 ] && (help;exit 127)
if [ "$?" != "0" ]
then
help
exit 127
fi
eval set -- $argv
while true
do
case "$1" in
--intf)
Intf=$2
shift 2
;;
--scan)
if [ "$2" == "2.4" -o "$2" == "5" ]
then
Scan=$2
else
help
exit 0
fi
shift 2
;;
--ssid)
SSID=$2
shift 2
;;
--channel)
Channel=$2
shift 2
;;
--workhz)
WorkHz=$2
shift 2
;;
--authmode)
AuthMode=$2
shift 2
;;
--encryptype)
EncrypType=$2
shift 2
;;
--wpapsk)
WpaPsk=$2
shift 2
;;
--defkeyid)
DefKeyId=$2
shift 2
;;
--key1)
Key1=$2
shift 2
;;
--key2)
Key2=$2
shift 2
;;
--key3)
Key3=$2
shift 2
;;
--key4)
Key4=$2
shift 2
;;
-h | --help)
help
exit 0
shift
;;
--)
break
;;
esac
done

:<<BLOCK
echo -e ${green}
echo Scan=$Scan
echo SSID=$SSID
echo WorkHz=$WorkHz
echo AuthMode=$AuthMode
echo EncrypType=$EncrypType
echo WpaPsk=$WpaPsk
echo DefKeyId=$DefKeyId
echo Key1=$Key1
echo Key2=$Key2
echo Key3=$Key3
echo Key4=$Key4
echo -e ${normal}
BLOCK

ifconfig $Intf up
if [ "$Scan" != "0" ]
then
WirelessMode=`iwpriv $Intf show WirelessMode |awk '{print $3}'`
case $WirelessMode in
"11B")
WirelessMode="1"
;;
"11A")
WirelessMode="2"
;;
"11A/B/G")
WirelessMode="3"
;;
"11G")
WirelessMode="4"
;;
"11A/B/G/N")
WirelessMode="5"
;;
"11N")
WirelessMode="6"
;;
"11G/N")
WirelessMode="7"
;;
"11A/N")
WirelessMode="8"
;;
"11B/G/N")
WirelessMode="9"
;;
"11A/G/N")
WirelessMode="10"
;;
esac
SSID=`iwpriv $Intf show SSID |awk '{print $3}'`
AuthMode=`iwpriv $Intf show AuthMode |awk '{print $3}'`
EncrypType=`iwpriv $Intf show EncrypType |awk '{print $3}'`
WpaPsk=`iwpriv $Intf show WPAPSK |awk '{print $5}'`
DefKeyId=`iwpriv $Intf show DefaultKeyID |awk '{print $3}'`
Key1=`iwpriv $Intf show Key1 |awk '{print $3}'`
Key2=`iwpriv $Intf show Key2 |awk '{print $3}'`
Key3=`iwpriv $Intf show Key3 |awk '{print $3}'`
Key4=`iwpriv $Intf show Key4 |awk '{print $3}'`
iwpriv $Intf set SSID=""
:<<BLOCK
echo -e ${yellow}
echo WirelessMode=$WirelessMode
echo SSID=$SSID
echo AuthMode=$AuthMode
echo EncrypType=$EncrypType
echo WpaPsk=$WpaPsk
echo DefKeyId=$DefKeyId
echo Key1=$Key1
echo Key2=$Key2
echo Key3=$Key3
echo Key4=$Key4
echo -e ${normal}
BLOCK
if [ "$Scan" == "2.4" ]
then
iwpriv $Intf set WirelessMode="9"
iwpriv $Intf set CountryRegion="5"
else
iwpriv $Intf set WirelessMode="8"
iwpriv $Intf set CountryRegion="7"
fi
echo -e ${green}"正在使用W522U扫描${Scan}G无线信号..."${normal}
#sleep 2
#iwpriv $Intf set SiteSurvey="1"
sleep 4
iwpriv $Intf get_site_survey

iwpriv $Intf set WirelessMode="${WirelessMode}" &>/dev/null
iwpriv $Intf set SSID="${SSID}" &>/dev/null
iwpriv $Intf set CountryRegionABand="7" &>/dev/null
iwpriv $Intf set CountryRegion="5" &>/dev/null
iwpriv $Intf set AuthMode="${AuthMode}" &>/dev/null
iwpriv $Intf set EncrypType="${EncrypType}" &>/dev/null
iwpriv $Intf set WPAPSK="${WpaPsk}" &>/dev/null
iwpriv $Intf set DefaultKeyID="${DefKeyId}" &>/dev/null
iwpriv $Intf set Key1="${Key1}" &>/dev/null
iwpriv $Intf set Key2="${Key2}" &>/dev/null
iwpriv $Intf set Key3="${Key3}" &>/dev/null
iwpriv $Intf set Key4="${Key4}" &>/dev/null
exit 0
fi

if [ "$WorkHz" == "2.4" ]
then
iwpriv $Intf set WirelessMode="9"
else
iwpriv $Intf set WirelessMode="8"
fi
if [ "$Channel" != "" ]
then
iwpriv $Intf set Channel="${Channel}" &>/dev/null
fi
iwpriv $Intf set SSID="${SSID}" &>/dev/null
iwpriv $Intf set CountryRegionABand="7" &>/dev/null
iwpriv $Intf set CountryRegion="5" &>/dev/null
iwpriv $Intf set AuthMode="${AuthMode}" &>/dev/null
iwpriv $Intf set EncrypType="${EncrypType}" &>/dev/null
iwpriv $Intf set WPAPSK="${WpaPsk}" &>/dev/null
iwpriv $Intf set DefaultKeyID="${DefKeyId}" &>/dev/null
iwpriv $Intf set Key1="${Key1}" &>/dev/null
iwpriv $Intf set Key2="${Key2}" &>/dev/null
iwpriv $Intf set Key3="${Key3}" &>/dev/null
iwpriv $Intf set Key4="${Key4}" &>/dev/null

echo -e ${green}"正在使用W522U ${WorkHz}G连接SSID:${SSID}"${normal}
suctime=0
for((i=1;i<=45;i++))
do
result=`iwpriv $Intf connStatus |grep Disconnected |wc -m`
if [ "$result" == "0" ]
then
suctime=`expr $suctime + 1`
if [ "$suctime" -ge "3" ]
then
break
fi
fi
sleep 1
done
iwpriv $Intf connStatus
if [ "$i" -ge "46" ]
then
echo Link Fail
else
echo Link Suc
fi
exit 0
实例二

主要实现配置PPPoE server的参数。

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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/sh
#config_PppoeSerCfg.sh
# ---------------------------------------------------------------------#
# FileName: config_PppoeSerCfg.sh
# Version: V1.0
# Description: 编辑PPPoE服务器配置文件
#
# History:
# <author> <time> <version > <desc>
# ---------------------------------------------------------------------#


help()
{
Usage="Usage: config_PppoeSerCfg.sh\n\
Options:\n\
[--auth pap|chap|mschap|mschap-v2] \t-- 配置认证方式,默认为chap加密\n \
[--mppe type] \t\t\t\t-- MPPE加密要求,type[40,128,both],若开启此选项,认证协议将自动使用mschap-v2协议\n \
[--mtu num] \t\t\t\t-- 配置MTU值选项\n \
[--mru num] \t\t\t\t-- 配置MRU值选项,0为拒绝协商\n \
[--echointerval num] \t\t\t-- 配置维链次数选项,默认12\n \
[--echofailure num] \t\t\t-- 配置维链失败次数选项,默认8s\n \
[--dns dnsip] \t\t\t\t-- 配置下发的DNS地址\n \
[--wins winsip] \t\t\t-- 配置下发的WINS地址\n \
[--custom args] \t\t\t-- 添加自定义参数,按pppoe-server配置文件格式即可\n \
[-o options file] \t\t\t-- 输出到配置文件(默认:/etc/ppp/pppoe-options\n \
[-h | --help] \t\t\t\t-- 显示此帮助信息\n \
"
echo -e $Usage
}


#定义公共变量
normal='\033[0m';red='\033[31m';green='\033[32m';yellow='\033[33m'
tmp=0
AUTH=chap
MPPE=""
MRU=""
MTU=""
EchoInterval=12
EchoFailure=8
DNS=""
dnsnum=0
WINS=""
winsnum=0
CUSTOM="" #自定义参数
customnum=0
OPTDIR=""

#定义配置文件
PPPOESEROPT=/etc/ppp/pppoe-server-options

#定义选项变量
OPTIND=1
argv=`getopt -a -o o:,h -l auth:,mppe:,mtu:,mru:,echointerval:,echofailure:,dns:,wins:,custom:,help -- $@`
#[ $? -ne 0 ] && (help;exit 127)
if [ "$?" != "0" ]
then
help
exit 127
fi
eval set -- $argv
while true
do
case "$1" in
--auth)
if [ $2 == pap -o $2 == chap -o $2 == mschap -o $2 == mschap-v2 ]
then
AUTH=$2
else
help
exit 0
fi
shift 2
;;
--mppe)
if [ $2 == 40 -o $2 == 128 -o $2 == both ]
then
MPPE=$2
else
help
exit 0
fi
shift 2
;;
--mtu | --mru | --echointerval | --echofailure)
expr $2 + 1 &>/dev/null
if [ $? -eq 0 ]
#if [ `echo $2 |grep [0-9]` ]
then
case "$1" in
--mtu)
MTU=$2
;;
--mru)
MRU=$2
;;
--echointerval)
EchoInterval=$2
;;
--echofailure)
EchoFailure=$2
;;
--)
break
;;
esac
else
help
exit 0
fi
shift 2
;;
--dns)
DNS[$dnsnum]=$2
dnsnum=`expr $dnsnum + 1`
shift 2
;;
--wins)
WINS[$winsnum]=$2
winsnum=`expr $winsnum + 1`
shift 2
;;
--custom)
CUSTOM[customnum]=$2
customnum=`expr $customnum + 1`
shift 2
;;
-o)
PPPOESEROPT=$2
shift 2
;;
-h | --help)
help
exit 0
shift
;;
--)
break
;;
esac
done


echo -e ${green}auth=$AUTH
echo MPPE=$MPPE
echo MTU=$MTU
echo MRU=$MRU
echo EchoInterval=$EchoInterval
echo EchoFailure=$EchoFailure
for((i=0;i<$dnsnum;i++))
do
echo DNS[$i]=${DNS[$i]}
done
for((i=0;i<$winsnum;i++))
do
echo WINS[$i]=${WINS[$i]}
done
for((i=0;i<customnum;i++))
do
echo CUSTOM[$i]=${CUSTOM[$i]}
done
echo PPPOESEROPT=$PPPOESEROPT
echo -e ${normal}

#重新创建配置文件
OPTDIR=`expr ${PPPOESEROPT%/*}`
mkdir -p ${OPTDIR}
rm -rf $PPPOESEROPT
echo "# PPP options for the PPPoE server" > $PPPOESEROPT
echo "# LIC: GPL" >> $PPPOESEROPT
if [ $MPPE ]
then
echo "refuse-pap" >> $PPPOESEROPT
echo "refuse-chap" >> $PPPOESEROPT
echo "refuse-mschap" >> $PPPOESEROPT
echo "require-mschap-v2" >> $PPPOESEROPT

if [ $MPPE == "both" ]
then
echo "require-mppe" >> $PPPOESEROPT
else
echo "require-mppe-${MPPE}" >> $PPPOESEROPT
fi

else
case "$AUTH" in
pap)
echo "require-pap" >> $PPPOESEROPT
echo "refuse-chap" >> $PPPOESEROPT
echo "refuse-mschap" >> $PPPOESEROPT
echo "refuse-mschap-v2" >> $PPPOESEROPT
;;
chap)
echo "refuse-pap" >> $PPPOESEROPT
echo "require-chap" >> $PPPOESEROPT
echo "refuse-mschap" >> $PPPOESEROPT
echo "refuse-mschap-v2" >> $PPPOESEROPT
;;
mschap)
echo "refuse-pap" >> $PPPOESEROPT
echo "refuse-chap" >> $PPPOESEROPT
echo "require-mschap" >> $PPPOESEROPT
echo "refuse-mschap-v2" >> $PPPOESEROPT
;;
mschap-v2)
echo "refuse-pap" >> $PPPOESEROPT
echo "refuse-chap" >> $PPPOESEROPT
echo "refuse-mschap" >> $PPPOESEROPT
echo "require-mschap-v2" >> $PPPOESEROPT
;;
*)
echo "refuse-pap" >> $PPPOESEROPT
echo "require-chap" >> $PPPOESEROPT
echo "refuse-mschap" >> $PPPOESEROPT
echo "refuse-mschap-v2" >> $PPPOESEROPT
;;
esac
fi

if [ $MRU ]
then
if [ $MRU == 0 ]
then
echo "-mru" >> $PPPOESEROPT
else
echo "mru $MRU" >> $PPPOESEROPT
fi
fi
if [ $MTU ]
then
echo "mtu $MTU" >> $PPPOESEROPT
fi

echo "lcp-echo-interval $EchoInterval" >> $PPPOESEROPT
echo "lcp-echo-failure $EchoFailure" >> $PPPOESEROPT

for((i=0;i<$dnsnum;i++))
do
echo "ms-dns ${DNS[$i]}" >> $PPPOESEROPT
done

for((i=0;i<$winsnum;i++))
do
echo "ms-wins ${WINS[$i]}" >> $PPPOESEROPT
done

for((i=0;i<$customnum;i++))
do
echo "${CUSTOM[$i]}" >> $PPPOESEROPT
done
exit 0