第一章 简单编程实现花生壳的ddns功能
第二章 让花生壳ddns脚本自动工作
第三章 同时解析多个花生壳域名的脚本
第四章 具有通用性的花生壳ddns脚本
用折腾路由的兴趣,顺便入门shell编程。
显然本专栏的文章与作者以前的风格不同,这个专栏是以让读者看明白为目的写的,不像以前只是简单记录重点,作者或有相关经验的人才能看明白。本专栏会详细引导您入门Linux shell编程,如果对您有一定帮助,希望读者能订阅,以鼓励作者创作更多更好的文章。
咱讲了二大章一万多字的花生壳域名,总得让它起点作用是吧,作者你这花生壳ddns还不如系统自带的呢?你折腾啥啊~
原计划是没有这一篇的,为了让前两章的内容有点作用而特设的,另外作者感觉进度太快,而且函数很重要,这一章就重点讲函数。
不知道oray官方是怎么想的,你在它那注册一个壳域名,它会自带一个没意义的很难记的(子域名随机由一串数字和多个字母组成)域名给你,并且有一段时间你只能用这个很难记的域名,现在怎么样我也不是很清楚哈。反正以前是这样的,那就造成了每个人至少有两个域名,像作者这种有4个以上的人也应该不少见。而系统内置的花生壳域名ddns只能解析一个域名,聪明人就会想到我到花生壳官网把其它域名设成cname解析不就行了。
上述方法自然是可行的(作者就这么干的),虽然cname解析会慢上那么几分钟(具体多久很难说),实际上花生壳的机制比较复杂,难记域名是和你注册的域名是绑定的,你改了一个另一个也就改了,就是会慢几分钟,但经常出错,出错成只解析一个域名,绑定的另一个不起作用。我们这里以需要解析难记的二个域名为例,就是你实际有4个以上的域名(两个难记的,自己注册的两个),当然你把4个都解析一下也是可以的。
本章开始引入函数的概念,在正经编程书籍中,这已经是一本书的中间部分内容了,咱这学习速度那是杠杠的。虽然咱连命令也才接触了十来个常用不常用的… 嗯,流程控制更是一个没说~ 别慌本章就开始有了!只要你能看明白前面的内容,那就说明作者这一套实践出真知的写法是有用效的,用到啥咱再来现学。
函数读者可以简单的认为它就是我们自己写的命令,它和系统提供的命令可以同样使用。简单说吧,就是你自己为实现某个功能而写的一段代码集合。比如程序界的经典例子,hello world! 前一章已经说过echo hello world!
一句代码就实现了,咱给它包装成函数:
print() {
echo 'hello world!'
}
print
这是直接写在命令行里了,看起来不太清爽。这种编程方法叫“交互式程序设计”,就是我们给出一行代码程序立即执行出结果,或显示>
等待继续输入,写成一个文件让计算机执行就叫脚本。当然我们也没必要给它弄成文件哈~ 读者看了更懵了… 一行代码的事,你给整成了四行才做到!这还有意义吗?当然是有意义的,这么写它有个很大的好处,它可以重复使用!我们改一下,让他能显示其它文本:
print() {
echo $1
}
print 'hello world!!'
这个示例不是很好(因为系统有echo
命令提供更强大的功能)地演示了函数的作用,主要是为了由浅入深的理解函数而写的。原本系统是没有print
这个命令的,我们自己造了一个,虽然它只是显示我们输入的字符到屏幕。但在退出ssh之前,我们都可以用上print
命令了,当然也可以给它固化进系统,只是没意义而已。想要显示什么到屏幕我们直接print
“内容” 就行了。
函数的结构是这样的:print(){ }
,print是自己取的函数名,()表示这是一个函数,{ }内是函数体,就是具体功能实现的代码集合。
示例中的$1
叫做位置参数,也就是在print
之后那一段 ‘hello world!!’ 这是在调用print
函数时传给函数的,print函数收到的第一个参数就是$1。参数可以有很多个,按位置分就是$1
、 $2
、 $3
等,这是shell最常用的参数传递方法之一。{}
花括号之间的echo $1
就是这个函数的主体功能,叫做函数体。这个函数也不是很难理解吧~ 但很重要哦,读者一定要理解并掌握才有写出较复杂代码的可能。
既然前面的例子不是很好,我们再来一个例子,这个例子演示如何把二段文字加起来,中间加上逗号显示出来:
# 这是在Mac OSX中的示例,路由也一样的,主要为说明shell都是差不多的
# function>是Mac系统的等待提示符
# 以#开头的行为注释,是给人看的,程序并不执行#开头的行
mac@macdeAir ~ % print(){
function> echo "$1,$2"
function> }
mac@macdeAir ~ % print hello world
hello,world
# 下例演示单引号与上例的双引号的区别
mac@macdeAir ~ % print(){
echo '$1,$2'
}
mac@macdeAir ~ % print hello world
$1,$2
看明白了吧,这个函数给我们加上了逗号,理解一下其中几个关键:
$
是引用符号,这个符号常用于引用变量(下一个例子说明变量),这里的作用是引用参数1($1:hello)和参数2($2:world),输入的多个参数要用空格分开哦~echo "$1,$2"
换成echo ‘$1,$2’
将显示$1,$2),字符串可以理解成文字,中文、日文、英文什么的都是字符串,例子中输入的两个参数是两个字符串 ‘hello’ 和 ‘world’ 。print
输出的是一个字符串 ‘hello,world’ ,也就是说这个函数的功能是把二个字符串加起来,且中间用逗号分隔。我们再来一个函数,用系统没有提供的功能来演示一下函数的作用。假设我们要计算加法,任意给两个整数。让函数给出结果,一般情况如果没有函数,我们要这样写:
# 这会是在openwrt里演示了,别老用梅林嘛,作者可是准备了四个路由的
root@OpenWrt:~# let res=1+2
root@OpenWrt:~# echo $res
3
# 这里写成”&&“也行,&&表示中文”和“的意思,这里只是把两行代码写到一行而已
root@OpenWrt:~# let c=1+2 && echo $c
3
LInux的shell中数学计算是比较麻烦的,不像python一样写个1+2,它就告诉你是3。
python中是这样的,这是在梅林路由器中运行的python:
# 这里的时间是UTC标准时间,读者别好奇作者为什么起床这么早~不存在的, >>> 是程序等待提示符
admin@RT-AC3100-88B0:/tmp/home/root# python
Python 3.11.4 (main, Sep 5 2023, 06:29:01) [GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1+2
3
>>>
我们忽略python,来看命令let res=1+2
, let
是Linux用来计算整数的,只能计算整数!
root@OpenWrt:~# let res=1+2 && echo $res
3
# 下例演示输入小数会报错 syntax error是语法错误
root@OpenWrt:~# let res=1.5+2 && echo $res
-ash: let: arithmetic syntax error
# 下例演示除法产生的小数会被丢弃
root@OpenWrt:~# let res=10/3 && echo $res
3
写上小数它就报错了哦~,shell本身是没有办法计算小数的,如果是除法产生的小数会被丢弃。(后面有需要再说正经linux是怎么解决小数计算的)我们写一个加法计算函数:
root@OpenWrt:~# add() {
> let res=$1+$2
> echo $res
> }
root@OpenWrt:~# add 1 2
3
root@OpenWrt:~# add 3786 234
4020
解读一下代码,这里引入了几个新概念:
res
是一个变量,变量故名思意就是可以变化的量,它是用于存储数据的,它是存储数据的内存地址的别名。let res=1+2
这句代码就是把1+2的结果存在res这个变量中,let
是用于整数计算的,上例中的字符相加并不需要。=
等于符号在这里是赋值符号,就是把1+2的结果交给res存储起来。以后要用的时候就可以引用res,这里=
赋值号两边不要有空格,不然会报错。echo $res
的$res
就是例二中所说的引用变量了。# 下例演示不加$引用符的情况
mac@macdeAir ~ % let res=1+2
mac@macdeAir ~ % echo $res
3
mac@macdeAir ~ % echo res
res
上面用了很多篇幅来说函数,作者其实觉得还是不够,不过咱这专栏的目的是学以致用,咱先解决多个花生壳域名要ddns解析的问题:
估计很多聪明人会说,你折腾啥!咱多写几个curl
来提交域名解析不就行了嘛~以下是多个curl的写法
#!/bin/sh
curl -s "http://用户:密码@ddns.oray.com/ph
/update?hostnam=域名1"
sleep 5
curl -s "http://用户:密码@ddns.oray.com/ph
/update?hostnam=域名2"
sleep 5
curl -s "http://用户:密码@ddns.oray.com/ph
/update?hostnam=域名3"
sleep 5
curl -s "http://用户:密码@ddns.oray.com/ph
/update?hostnam=域名4"
这样写肯定是可行的哈,咱还在每个域名提交后等待5秒钟呢~ sleep
是睡觉的意思,这个命令用于等待(5的单位是秒),免得速度太快被服务器当成攻击拒绝访问。实际sleep机制会释放出一定的资源给其它进程用,只保留让自己醒来的必要守护进程。
就是这种写法太low,没点程序设计的美感。
#!/bin/sh
user="用户名"
pass="密码"
# 写一个resolve函数来处理解析的事,传入三个参数
resolve() {
curl -s "http://$1:$2@ddns.oray.com/ph/update?hostnam=$3"
}
host1=a.oray.com
host2=b.oray.com
host3=c.oray.com
host4=d.oray.com
# 循环调用resolve函数工作,注意函数resolve三个参数的位置关系
# 作者这里故意写成三个参数的,用以说明多参数的位置关系
for host in $host1 $host2 $host3 $host4
do
resolve $user $pass $host
sleep 5
done
这里使用了for
循环,依次将列表中的元素赋值给变量"host”; 每次赋值后即执行一次循环体do
和done
之间是循环体; 直到in
后面列表中的元素耗尽,循环结束。咱变通的运行一下看看:
admin@RT-AC3100-88B0:/tmp/home/root# host1=a.oray.com
admin@RT-AC3100-88B0:/tmp/home/root# host2=b.oray.com
admin@RT-AC3100-88B0:/tmp/home/root# host3=c.oray.com
admin@RT-AC3100-88B0:/tmp/home/root# host4=d.oray.com
admin@RT-AC3100-88B0:/tmp/home/root# for host in $host1 $host2 $host3 $host4
> do
> echo $host
> done
a.oray.com
b.oray.com
c.oray.com
d.oray.com
我们这里用echo $host
来代替了resolve
函数,便于看清这个for循环的过程。for host in $host1 $host2 $host3 $host4
这一句中的host是一个新定义的变量,之前没有定义过,host1-4是之前已经定义过的变量。在for循环中每次都把host变量值改变成了in 之后的每一个值。请读者仔细理解,这是程序流程控制的第一步!
我们再来一个简单点的for循环理解一下:
admin@RT-AC3100-88B0:/tmp/home/root# for i in 1 2 3 4 5
> do
> echo $i
> done
1
2
3
4
5
最后再简化一下写法,就更好看了:
#!/bin/sh
user="用户名"
pass="密码"
# 写一个resolve函数来处理解析的事,只传入1个参数,用户和密码直接引用
resolve() {
curl -s "http://$user:$pass@ddns.oray.com/ph/update?hostnam=$1"
}
# 循环调用resolve函数工作
for host in 'a.oray.com' 'b.oray.com' 'c.oray.com' 'd.oray.com'
do
resolve $host
sleep 5
done
# do和done是用来界定边界的,shell中还有好几种类似的边界写法,和函数的花括号作用一样
聪明的读者又要问了,为什么用户名和密码不写进curl那一行呢,这样不是又省了两行吗? 这个问题下一章再说。
Okay,读者们理解了这一章的内容以后,并且能够独立的仿写出一个for 循环利用函数的代码,就可以大声的宣告:”Linux shell编程已经一只脚跨入门槛了!“,本章的重点内容就是函数的编写和for循环的利用。下一章我们将讲解选择分支并将这个脚本再度改进成具有一定的通用性,公开发布也不寒碜的那种。