SHELL脚本编程基础知识
SHELL脚本编程基础知识
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
Linux之父Linus有一句话很经典:"Talk is cheap, show me the code",虽然我们是一枚小小的运维工程师,但工作中确实是有一些任务是需要写成脚本方式来实现的。在招聘面试过程中,要求运维人员会shell编程是必须的,甚至有的公司得要求运维会Java,Python或者Golang。
程序:
算法+数据结构
数据:
是程序的核心
数据结构:
数据在计算机中的类型和组织方式
算法:
处理数据的方式
过程式:
以指令为中心,数据服务于指令
对象式:
以数据为中心,指令服务于数据
计算机只能识别二进制,因此运行程序其实就是运行二进制指令。
编程语言是人与计算机之间交互的语言。
机器语言:
二进制的0和1的序列,称为机器指令。与自然语言差异太大,难懂、难写。
汇编:
用一些助记符号替代机器指令,称为汇编语言。
如:ADD A,B 将寄存器A的数与寄存器B的数相加得到的数放到寄存器A中
汇编语言写好的程序需要汇编程序转换成机器指令
汇编语言稍微好理解,即机器指令对应的助记符,助记符更接近自然语言
编译型语言执行过程:高级语言-->编译器-->机器代码-->执行
典型代表:C,C++,等
特点:开发效率低,执行效率高 解释型语言执行过程:高级语言-->执行-->解释器-->机器代码
典型代表:shell,python,php,JavaScript,perl,Scala等
特点:开发效率高,执行效率低
无论是编译型语言还是解释型语言在外面生活中都能找到类似吃菜的场景:
炒菜:
上菜后就直接可用吃啦,但是我们得等炒菜的过程,这就很像我们计算机编译型语言的执行过程。 火锅:
不需要提前把菜弄熟,而是想吃哪个就先煮哪个菜,边吃火锅里煮着,这就很像我们计算机解释性语言的执行过程。
6>.编程逻辑处理方式
顺序执行
循环执行
选择执行
编程语言的基本结构:
各种系统命令的组合
数据存储:变量、数组
表达式:如:"a + b"
语句:if...else,case,while do...done等
它是网页程序,也是脚本语言,更专注于web页面的开发,例如:dedecms,discuz。也可以处理系统日志,配置文件等。
perl脚本语言,比shell强大的多,2010年前很火,语法灵活,复杂,实现方法很多,不易读,团队协作困难。
近几年很火的语言,可以做脚本开发,也可以实现web开发(但并不是和做电商网站开发哟~),中等以上的公司都要求会python。
最容易上手的脚本,shell的优势在于处理操作系统底层的业务,因为有大量的系统命令为它做支撑,2000多个命令都是shell编程的有力支撑,特别是grep,awk,sed等。例如:一件软件安装,优化,监控报警脚本,常规的业务应用,shell开发更简单快速。以下是解释器支持的shell类型。
Golang语言,一个很适合做自动化运维的编程语言
它是编译型语言,Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程,很适合做系统开发或自动化运维。
Golang语言,一个很适合做自动化运维的编程语言
Bourne Shell:
从1979年起Unix就开始使用Bourne Shell,Bourne Shell的主文件名为sh,我们最常见的"/bin/sh"。 C Shell:
C Shell主要在BSD版的Unix系统中使用,其语法和C语言相类似而得名,我们最常见的就是“/bin/csh”。 Bash:
Bash与sh兼容,现在使用的Linux就是使用Bash作为用户的基本Shell。
温馨提示:
Shell的两种主要语法类型有Bourne和C,这两种语法彼此不兼容。
Bourne家族主要包括sh,ksh,Bash,psh,zsh;
C家族主要包括:csh,tcsh。 常用操作系统的默认Shell
linux操作系统:
Bourne Again shell(bash)。
Solaris和FreeBSD操作系统:
Bourne shell(sh)。
AIX操作系统:
Korn Shell(ksh)。
HP-UX操作系统:
POSIX shell(sh)。
Centos linux操作系统:
默认的shell是bash。
[root@node101.yinzhengjie.org.cn ~]# cat /etc/shells #这个文件保存着当前操作系统支持的Shell版本
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# cat /etc/shells #该文件保存着当前操作系统支持的Shell版本
包含一些命令或声明,并符合一定格式的文本文件
编写脚本的时候我们通常会在第一行指定当前脚本所用的解释权,我们称之为"首行shebang机制",这里的"shebang"其实就是"#!"的读音翻译。 我们写脚本的时候一般都是调用bash,所以第一行我们要写上这么一行:"#!/bin/bash",要注意的是这可不是注释行哟~而是告诉内核我们用的是哪种解释器,下面的所有行,如果在出现类似的内核都会认为是注释行。 常见解释性语言的开头标识内容如下:
#!/bin/bash
#!/bin/sh
#!/usr/bin/awk
#!/bin/sed
#!/usr/bin/tcl
#!/usr/bin/expect
#!/usr/bin/perl
#!/usr/bin/env python
自动化常用命令
执行系统管理和故障排除
创建简单的应用程序
处理文本或文件
[root@node101.yinzhengjie.org.cn ~]# mkdir -pv /data/script
mkdir: created directory ‘/data’
mkdir: created directory ‘/data/script’
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# cd /data/script/
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# vim hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# cat hello.sh
#!/bin/bash
#@author:yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie
#Description: This is the first script echo "hello world"
echo "My hostname is `hostname`"
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rw-r--r-- root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# chmod +x hello.sh #给予执行权限,在命令行上指定脚本的绝对或相对路径
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rwxr-xr-x root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ./hello.sh #有执行权限后,可用用相对路径调用执行
hello world
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# /data/script/hello.sh #有执行权限后,也可以直接使用绝对路径调用执行
hello world
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn /data/script]#
给予执行权限,在命令行上指定脚本的绝对或相对路径
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rwxr-xr-x root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# chmod -x hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rw-r--r-- root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# sh hello.sh #直接运行解释器,将脚本作为解释器程序的参数运行
hello world
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]#
直接运行解释器,将脚本作为解释器程序的参数运行
[root@node101.yinzhengjie.org.cn /data/script]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/softwares/jdk1..0_201/bin:/root/bin:/home/softwares/mysql/bin/
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# vim /etc/profile.d/env.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# cat /etc/profile.d/env.sh
PATH=/data/script:$PATH
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# source /etc/profile.d/env.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# echo $PATH
/data/script:/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/softwares/jdk1..0_201/bin:/root/bin:/home/softwares/mysql/bin/
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# he
head help hexdump
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rw-r--r-- root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# chmod +x hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rwxr-xr-x root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# cd
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# hello.sh
hello world
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]#
将编写脚本路径添加到"$PATH"环境变量中,只要有执行权限的脚本文件均可被调用执行
"source"和"."的功能是一样的,可以调用脚本,并将脚本里的函数也传递到当前的脚本或者解释器中,即不会开启新的bash而是在当前bash中运行。 "sh"后面跟脚本名称,则不会将该脚本的函数传递进来,即需要开启新的bash,"sh"实际上是执行一个脚本,最后执行完毕会将内存释放掉,不会保存变量。 而"."和"source"则不会新的bash进程,这就是为什么在/etc/init.d/这个目录下有很多的脚本都会用"."去调用脚本。 综上所述:
生产环境中编写脚本一般会使用sh命令去执行脚本,因为使用sh命令执行的脚本执行完毕后会自动释放内存并不会影响当前进程中的变量。除非你明确直到想要脚本中的变量要在当前bash中生效(比如重新读取配置文件)则可以使用"source"或者"."
[root@node101.yinzhengjie.org.cn ~]# vim shell/test.sh
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# cat shell/test.sh
#!/bin/bash
#
#********************************************************************
#Author: yinzhengjie
#QQ:
#Date: --
#FileName: test.sh
#URL: http://www.cnblogs.com/yinzhengjie
#Description: The test script
#Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated.
#******************************************************************** NAME="尹正杰" #定义一个变量
echo $NAME #打印咱们的上面定义的变量对应的值
echo $BASHPID #打印当前脚本执行的bash的pid编号
sleep #让脚本晚一点结束,咱们可以使用pstree命令观察是否有新的进程生成。
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# NAME="Jason Yin"
[root@node101.yinzhengjie.org.cn ~]# echo $NAME #这是打印的在当前shell中定义的变量
Jason Yin
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# bash shell/test.sh #不难发现我们在脚本中定义了变量被打印出来了,
尹正杰 [root@node101.yinzhengjie.org.cn ~]# echo $NAME #我们发现执行完脚本后当前shell的变量并没有被覆盖哟~
Jason Yin
[root@node101.yinzhengjie.org.cn ~]# 上面代码进入阻塞状态时,咱们开启新的终端执行如下命令:
[root@node101.yinzhengjie.org.cn ~]# pstree -p | grep sshd #可以看到有一个sleep进程id为14814,而其父进程则为14813哟,结合上面的输出进行对比。
|-sshd()-+-sshd()---bash()-+-grep()
| `-sshd()---bash()---bash()---sleep()
[root@node101.yinzhengjie.org.cn ~]#
sh案例(在CentOS操作系统sh其实就是bash命令的软连接)
[root@node101.yinzhengjie.org.cn ~]# vim shell/test.sh
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# cat shell/test.sh
#!/bin/bash
#
#********************************************************************
#Author: yinzhengjie
#QQ:
#Date: --
#FileName: test.sh
#URL: http://www.cnblogs.com/yinzhengjie
#Description: The test script
#Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated.
#******************************************************************** NAME="尹正杰" #定义一个变量
echo $NAME #打印咱们的上面定义的变量对应的值
echo $BASHPID #打印当前脚本执行的bash的pid编号
sleep #让脚本晚一点结束,咱们可以使用pstree命令观察是否有新的进程生成。
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# NAME="Jason Yin"
[root@node101.yinzhengjie.org.cn ~]# echo $NAME
Jason Yin
[root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# chmod +x shell/test.sh
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# ./shell/test.sh
尹正杰 当执行上面的脚本进入阻塞状态时,在其它终端执行以下命令(发现有执行权限的脚本在执行代码时也开启了新的bash):
[root@node101.yinzhengjie.org.cn ~]# pstree -p | grep sshd
|-sshd()-+-sshd()---bash()-+-grep()
| `-sshd()---bash()---test.sh()---sleep()
[root@node101.yinzhengjie.org.cn ~]#
发现有执行权限的脚本在执行代码时也开启了新的bash
1>.脚本开头约定
开头执行脚本解释器 开头加版权等信息 脚本中尽量不要用中文注释,尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰
脚本以".sh"为扩展名 代码书写优秀习惯
().成对内容的一次写出来,防止漏写。例如:{},[],'',``,"".
().[]中括号两端都要有空格,书写时即可流出空格[ ],然后退格书写内容。
().流程控制语句一次书写完,在添加内容。
2>.脚本的基本结构
#!SHEBANG
CONFIGURATION_VARIABLES
FUNCTION_DEFINITIONS
MAIN_CODE
3>.shell脚本示例
[root@node101.yinzhengjie.org.cn /data/script]# cat hello.sh
#!/bin/bash
#
#********************************************************************
#Author: yinzhengjie
#Email: y1053419035@qq.com
#Date: --
#FileName: hello.sh
#URL: http://www.cnblogs.com/yinzhengjie
#Description: This is the first script
#Copyright (C): Original works, no reprint! Otherwise, legal liability will be investigated
#******************************************************************** echo -e "hello world"
echo -e "My hostname is `hostname`"
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]#
4>.可配置vim编辑文件时自动加上以上信息版权信息(方法是修改~/.vimrc配置文件)
[root@node101.yinzhengjie.org.cn ~]# cat ~/.vimrc #下面附有对每行的中文解释,使用时需要将这些中文字符提前删除哟~
set ignorecase #忽略大小写
set cursorline #移动光标时添加下划线
set autoindent #自动进行行对其
autocmd BufNewFile *.sh exec ":call SetTitle()" #当打开是以"*.sh"的文件名称时,就会自动调用下面我们定义的函数啦! func SetTitle()
if expand("%:e") == 'sh'
call setline(,"#!/bin/bash")
call setline(,"#")
call setline(,"#********************************************************************")
call setline(,"#Author: yinzhengjie")
call setline(,"#QQ: 1053419035")
call setline(,"#Date: ".strftime("%Y-%m-%d"))
call setline(,"#FileName: ".expand("%"))
call setline(,"#URL: http://www.cnblogs.com/yinzhengjie")
call setline(,"#Description: The test script")
call setline(,"#Copyright (C): ".strftime("%Y")." All rights reserved")
call setline(,"#********************************************************************")
call setline(,"")
endif
endfunc
autocmd BufNewFile * normal G
[root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn /data/script]# ll
total
-rwxr-xr-x root root Nov : hello.sh
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# bash -n hello.sh #检查脚本的语法并不会执行,若有语法错误就会抛出异常,只能检查语法错误不能检查命令是否错误。
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# bash hello.sh #调用脚本并执行
hello world
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# bash -n hello.sh #检查脚本的语法并不会执行
[root@node101.yinzhengjie.org.cn /data/script]# bash -x hello.sh #查看脚本的执行过程,尤其时代表出错时,可用看到是具体哪行代码出错啦~
+ echo 'hello world'
hello world
++ hostname
+ echo 'My hostname is node101.yinzhengjie.org.cn'
My hostname is node101.yinzhengjie.org.cn
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]#
[root@node101.yinzhengjie.org.cn /data/script]# bash -x hello.sh #查看脚本的执行过程
网络安全专家警告称,开源软件Linux中一个频繁使用的片段“Bash”,发现存在安全漏洞,其对计算机用户造成的威胁可能要超过2014年4月爆出的"心脏出血"(Heartbleed)漏洞。
bash是用于控制Linux计算机命令提示符的软件。网络安全专家表示,黑客可以利用Bash中的一个安全漏洞,对目标计算机系统进行完全控制。 网络安全公司Trail of Bits的首席执行官丹·吉多(Dan Guido)指出:与"Heartbleed"相比,后者只允许黑客窥探计算机,但不会让黑客获得计算机的控制权。"他说:"利用Bash漏洞的方法也简单得多,你可以直接剪切和粘贴一行软件代码,就能取得很好的效果。"吉多还表示,他正考虑将自己公司非必要的服务器断网,以保护他们不会受到Bash漏洞的攻击,直到他能够修补这一漏洞为止。 网络安全公司Rapid7的工程经理托德·比尔兹利(Tod Beardsley)则警告称,Bash漏洞的严重程度被评为10级,意味着它具有最大的影响力,而其利用的难度被评为"低"级,意味着黑客比较容易地利用其发动网络攻击。 比尔兹利称:"利用这个漏洞,攻击者可能会接管计算机的整个操作系统,得以访问机密信息,并对系统进行更改等等。任何人的计算机系统,如果使用了Bash软件,都需要立即打上补丁。"
centos系统(如果是centos系统只要运行下面简单的命令就可以)
yum clean all
yum makecache
yum -y update bash Ubuntu系统
apt-get update
apt-get -y install --only-upgrade bash Debian系统
如果是7. 64位 && 32位环境运行
apt-get update
apt-get -y install --only-upgrade bash
#!/bin/bash #APP declaration
APP_NAME="${0##*[\\/]}"
APP_VERSION="1.0" #颜色定义
cRed=
cGreen=
cYellow=
cBlue=
cFuchsia=
cCyan=
cWhite=
colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite) #位置和大小
iLeft=
iTop=
((iTrayLeft = iLeft + ))
((iTrayTop = iTop + ))
((iTrayWidth = ))
((iTrayHeight = )) #颜色设置
cBorder=$cGreen
cScore=$cFuchsia
cScoreValue=$cCyan #控制信号
#改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
#当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
sigRotate=
sigLeft=
sigRight=
sigDown=
sigAllDown=
sigExit= #七中不同的方块的定义
#通过旋转,每种方块的显示的样式可能有几种
box0=( )
box1=( )
box2=( )
box3=( )
box4=( )
box5=( )
box6=( )
#所有其中方块的定义都放到box变量中
box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
#各种方块旋转后可能的样式数目
countBox=( )
#各种方块再box数组中的偏移
offsetBox=( ) #每提高一个速度级需要积累的分数
iScoreEachLevel= #be greater than #运行时数据
sig= #接收到的signal
iScore= #总分
iLevel= #速度级
boxNew=() #新下落的方块的位置定义
cBoxNew= #新下落的方块的颜色
iBoxNewType= #新下落的方块的种类
iBoxNewRotate= #新下落的方块的旋转角度
boxCur=() #当前方块的位置定义
cBoxCur= #当前方块的颜色
iBoxCurType= #当前方块的种类
iBoxCurRotate= #当前方块的旋转角度
boxCurX=- #当前方块的x坐标位置
boxCurY=- #当前方块的y坐标位置
iMap=() #背景方块图表 #初始化所有背景方块为-, 表示没有方块
for ((i = ; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-; done #接收输入的进程的主函数
function RunAsKeyReceiver()
{
local pidDisplayer key aKey sig cESC sTTY pidDisplayer=$
aKey=( ) cESC=`echo -ne "\033"`
cSpace=`echo -ne "\040"` #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
#如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
#需要在程序退出时恢复终端属性。
sTTY=`stty -g` #捕捉退出信号
trap "MyExit;" INT TERM
trap "MyExitNoSub;" $sigExit #隐藏光标
echo -ne "\033[?25l" while :
do
#读取输入。注-s不回显,-n读到一个字符立即返回
read -s -n key aKey[]=${aKey[]}
aKey[]=${aKey[]}
aKey[]=$key
sig= #判断输入了何种键
if [[ $key == $cESC && ${aKey[]} == $cESC ]]
then
#ESC键
MyExit
elif [[ ${aKey[]} == $cESC && ${aKey[]} == "[" ]]
then
if [[ $key == "A" ]]; then sig=$sigRotate #<向上键>
elif [[ $key == "B" ]]; then sig=$sigDown #<向下键>
elif [[ $key == "D" ]]; then sig=$sigLeft #<向左键>
elif [[ $key == "C" ]]; then sig=$sigRight #<向右键>
fi
elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate #W, w
elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown #S, s
elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft #A, a
elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight #D, d
elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown #空格键
elif [[ $key == "Q" || $key == "q" ]] #Q, q
then
MyExit
fi if [[ $sig != ]]
then
#向另一进程发送消息
kill -$sig $pidDisplayer
fi
done
} #退出前的恢复
function MyExitNoSub()
{
local y #恢复终端属性
stty $sTTY
((y = iTop + iTrayHeight + )) #显示光标
echo -e "\033[?25h\033[${y};0H"
exit
} function MyExit()
{
#通知显示进程需要退出
kill -$sigExit $pidDisplayer MyExitNoSub
} #处理显示和游戏流程的主函数
function RunAsDisplayer()
{
local sigThis
InitDraw #挂载各种信号的处理函数
trap "sig=$sigRotate;" $sigRotate
trap "sig=$sigLeft;" $sigLeft
trap "sig=$sigRight;" $sigRight
trap "sig=$sigDown;" $sigDown
trap "sig=$sigAllDown;" $sigAllDown
trap "ShowExit;" $sigExit while :
do
#根据当前的速度级iLevel不同,设定相应的循环的次数
for ((i = ; i < - iLevel; i++))
do
sleep 0.02
sigThis=$sig
sig= #根据sig变量判断是否接受到相应的信号
if ((sigThis == sigRotate)); then BoxRotate; #旋转
elif ((sigThis == sigLeft)); then BoxLeft; #左移一列
elif ((sigThis == sigRight)); then BoxRight; #右移一列
elif ((sigThis == sigDown)); then BoxDown; #下落一行
elif ((sigThis == sigAllDown)); then BoxAllDown; #下落到底
fi
done
#kill -$sigDown $$
BoxDown #下落一行
done
} #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
function BoxMove()
{
local j i x y xTest yTest
yTest=$
xTest=$
for ((j = ; j < ; j += ))
do
((i = j + ))
((y = ${boxCur[$j]} + yTest))
((x = ${boxCur[$i]} + xTest))
if (( y < || y >= iTrayHeight || x < || x >= iTrayWidth))
then
#撞到墙壁了
return
fi
if ((${iMap[y * iTrayWidth + x]} != - ))
then
#撞到其他已经存在的方块了
return
fi
done
return ;
} #将当前移动中的方块放到背景方块中去,
#并计算新的分数和速度级。(即一次方块落到底部)
function Box2Map()
{
local j i x y xp yp line #将当前移动中的方块放到背景方块中去
for ((j = ; j < ; j += ))
do
((i = j + ))
((y = ${boxCur[$j]} + boxCurY))
((x = ${boxCur[$i]} + boxCurX))
((i = y * iTrayWidth + x))
iMap[$i]=$cBoxCur
done #消去可被消去的行
line=
for ((j = ; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
do
for ((i = j + iTrayWidth - ; i >= j; i--))
do
if ((${iMap[$i]} == -)); then break; fi
done
if ((i >= j)); then continue; fi ((line++))
for ((i = j - ; i >= ; i--))
do
((x = i + iTrayWidth))
iMap[$x]=${iMap[$i]}
done
for ((i = ; i < iTrayWidth; i++))
do
iMap[$i]=-
done
done if ((line == )); then return; fi #根据消去的行数line计算分数和速度级
((x = iLeft + iTrayWidth * + ))
((y = iTop + ))
((iScore += line * - ))
#显示新的分数
echo -ne "\033[1m\033[3${cScoreValue}m\033[${y};${x}H${iScore} "
if ((iScore % iScoreEachLevel < line * - ))
then
if ((iLevel < ))
then
((iLevel++))
((y = iTop + ))
#显示新的速度级
echo -ne "\033[3${cScoreValue}m\033[${y};${x}H${iLevel} "
fi
fi
echo -ne "\033[0m" #重新显示背景方块
for ((y = ; y < iTrayHeight; y++))
do
((yp = y + iTrayTop + ))
((xp = iTrayLeft + ))
((i = y * iTrayWidth))
echo -ne "\033[${yp};${xp}H"
for ((x = ; x < iTrayWidth; x++))
do
((j = i + x))
if ((${iMap[$j]} == -))
then
echo -ne " "
else
echo -ne "\033[1m\033[7m\033[3${iMap[$j]}m\033[4${iMap[$j]}m[]\033[0m"
fi
done
done
} #下落一行
function BoxDown()
{
local y s
((y = boxCurY + )) #新的y坐标
if BoxMove $y $boxCurX #测试是否可以下落一行
then
s="`DrawCurBox 0`" #将旧的方块抹去
((boxCurY = y))
s="$s`DrawCurBox 1`" #显示新的下落后方块
echo -ne $s
else
#走到这儿, 如果不能下落了
Box2Map #将当前移动中的方块贴到背景方块中
RandomBox #产生新的方块
fi
} #左移一列
function BoxLeft()
{
local x s
((x = boxCurX - ))
if BoxMove $boxCurY $x
then
s=`DrawCurBox `
((boxCurX = x))
s=$s`DrawCurBox `
echo -ne $s
fi
} #右移一列
function BoxRight()
{
local x s
((x = boxCurX + ))
if BoxMove $boxCurY $x
then
s=`DrawCurBox `
((boxCurX = x))
s=$s`DrawCurBox `
echo -ne $s
fi
} #下落到底
function BoxAllDown()
{
local k j i x y iDown s
iDown=$iTrayHeight #计算一共需要下落多少行
for ((j = ; j < ; j += ))
do
((i = j + ))
((y = ${boxCur[$j]} + boxCurY))
((x = ${boxCur[$i]} + boxCurX))
for ((k = y + ; k < iTrayHeight; k++))
do
((i = k * iTrayWidth + x))
if (( ${iMap[$i]} != -)); then break; fi
done
((k -= y + ))
if (( $iDown > $k )); then iDown=$k; fi
done s=`DrawCurBox ` #将旧的方块抹去
((boxCurY += iDown))
s=$s`DrawCurBox ` #显示新的下落后的方块
echo -ne $s
Box2Map #将当前移动中的方块贴到背景方块中
RandomBox #产生新的方块
} #旋转方块
function BoxRotate()
{
local iCount iTestRotate boxTest j i s
iCount=${countBox[$iBoxCurType]} #当前的方块经旋转可以产生的样式的数目 #计算旋转后的新的样式
((iTestRotate = iBoxCurRotate + ))
if ((iTestRotate >= iCount))
then
((iTestRotate = ))
fi #更新到新的样式, 保存老的样式(但不显示)
for ((j = , i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * ; j < ; j++, i++))
do
boxTest[$j]=${boxCur[$j]}
boxCur[$j]=${box[$i]}
done if BoxMove $boxCurY $boxCurX #测试旋转后是否有空间放的下
then
#抹去旧的方块
for ((j = ; j < ; j++))
do
boxCur[$j]=${boxTest[$j]}
done
s=`DrawCurBox ` #画上新的方块
for ((j = , i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * ; j < ; j++, i++))
do
boxCur[$j]=${box[$i]}
done
s=$s`DrawCurBox `
echo -ne $s
iBoxCurRotate=$iTestRotate
else
#不能旋转,还是继续使用老的样式
for ((j = ; j < ; j++))
do
boxCur[$j]=${boxTest[$j]}
done
fi
} #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
function DrawCurBox()
{
local i j t bDraw sBox s
bDraw=$ s=""
if (( bDraw == ))
then
sBox="\040\040"
else
sBox="[]"
s=$s"\033[1m\033[7m\033[3${cBoxCur}m\033[4${cBoxCur}m"
fi for ((j = ; j < ; j += ))
do
((i = iTrayTop + + ${boxCur[$j]} + boxCurY))
((t = iTrayLeft + + * (boxCurX + ${boxCur[$j + ]})))
#\[y;xH, 光标到(x, y)处
s=$s"\033[${i};${t}H${sBox}"
done
s=$s"\033[0m"
echo -n $s
} #更新新的方块
function RandomBox()
{
local i j t #更新当前移动的方块
iBoxCurType=${iBoxNewType}
iBoxCurRotate=${iBoxNewRotate}
cBoxCur=${cBoxNew}
for ((j = ; j < ${#boxNew[@]}; j++))
do
boxCur[$j]=${boxNew[$j]}
done #显示当前移动的方块
if (( ${#boxCur[@]} == ))
then
#计算当前方块该从顶端哪一行"冒"出来
for ((j = , t = ; j < ; j += ))
do
if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
done
((boxCurY = -t))
for ((j = , i = -, t = ; j < ; j += ))
do
if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
done
((boxCurX = (iTrayWidth - - i - t) / )) #显示当前移动的方块
echo -ne `DrawCurBox ` #如果方块一出来就没处放,Game over!
if ! BoxMove $boxCurY $boxCurX
then
kill -$sigExit ${PPID}
ShowExit
fi
fi #清除右边预显示的方块
for ((j = ; j < ; j++))
do
((i = iTop + + j))
((t = iLeft + * iTrayWidth + ))
echo -ne "\033[${i};${t}H "
done #随机产生新的方块
((iBoxNewType = RANDOM % ${#offsetBox[@]}))
((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
for ((j = , i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * ; j < ; j++, i++))
do
boxNew[$j]=${box[$i]};
done ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]})) #显示右边预显示的方块
echo -ne "\033[1m\033[7m\033[3${cBoxNew}m\033[4${cBoxNew}m"
for ((j = ; j < ; j += ))
do
((i = iTop + + ${boxNew[$j]}))
((t = iLeft + * iTrayWidth + + * ${boxNew[$j + ]}))
echo -ne "\033[${i};${t}H[]"
done
echo -ne "\033[0m"
} #初始绘制
function InitDraw()
{
clear
RandomBox #随机产生方块,这时右边预显示窗口中有方快了
RandomBox #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
local i t1 t2 t3 #显示边框
echo -ne "\033[1m"
echo -ne "\033[3${cBorder}m\033[4${cBorder}m" ((t2 = iLeft + ))
((t3 = iLeft + iTrayWidth * + ))
for ((i = ; i < iTrayHeight; i++))
do
((t1 = i + iTop + ))
echo -ne "\033[${t1};${t2}H||"
echo -ne "\033[${t1};${t3}H||"
done ((t2 = iTop + iTrayHeight + ))
for ((i = ; i < iTrayWidth + ; i++))
do
((t1 = i * + iLeft + ))
echo -ne "\033[${iTrayTop};${t1}H=="
echo -ne "\033[${t2};${t1}H=="
done
echo -ne "\033[0m" #显示"Score"和"Level"字样
echo -ne "\033[1m"
((t1 = iLeft + iTrayWidth * + ))
((t2 = iTop + ))
echo -ne "\033[3${cScore}m\033[${t2};${t1}HScore"
((t2 = iTop + ))
echo -ne "\033[3${cScoreValue}m\033[${t2};${t1}H${iScore}"
((t2 = iTop + ))
echo -ne "\033[3${cScore}m\033[${t2};${t1}HLevel"
((t2 = iTop + ))
echo -ne "\033[3${cScoreValue}m\033[${t2};${t1}H${iLevel}"
echo -ne "\033[0m"
} #退出时显示GameOVer!
function ShowExit()
{
local y
((y = iTrayHeight + iTrayTop + ))
echo -e "\033[${y};0HGameOver!\033[0m"
exit
} #显示用法.
function Usage
{
cat << EOF
Usage: $APP_NAME
Start tetris game. -h, --help display this help and exit
--version output version information and exit
EOF
} #游戏主程序在这儿开始.
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
Usage
elif [[ "$1" == "--version" ]]; then
echo "$APP_NAME $APP_VERSION"
elif [[ "$1" == "--show" ]]; then
#当发现具有参数--show时,运行显示函数
RunAsDisplayer
else
bash $ --show& #以参数--show将本程序再运行一遍
RunAsKeyReceiver $! #以上一行产生的进程的进程号作为参数
fi
"俄罗斯方块"使用shell编写的源码(这是我从网络上找到的代码,不得不说写着程序的哥们也太有才了~)
SHELL脚本编程基础知识的更多相关文章
- Linux shell脚本编程基础之练习篇
shell脚本编程基础之练习篇. 1.编写一个脚本使我们在写一个脚本时自动生成”#!/bin/bash”这一行和注释信息. #!/bin/bash ] then echo "请输入一个参数& ...
- linux的基本操作(shell 脚本的基础知识)
shell 脚本的基础知识 日常的linux系统管理工作中必不可少的就是shell脚本,如果不会写shell脚本,那么你就不算一个合格的管理员.目前很多单位在招聘linux系统管理员时,shell脚本 ...
- shell脚本编程基础介绍
Linux系统——shell脚本编程基础介绍 1.什么是shell 它是一个命令解释器,在linux/unix操作系统的最外层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种操作输出的结果 ...
- linux基础—课堂随笔_03 SHELL脚本编程基础
shell脚本编程基础 条件选择:if语句 选择执行: 注意:if语句可嵌套 单分支 if(开头)判断条件:then条件为真的分支代码 fi(结尾) 双分支 if(开头)判断条件:then条件为真的分 ...
- shell脚本编程基础
最近学习了shell脚本编程,感觉自己的脚本写的不太好,所以想把shell脚本相关的知识系统的整理一下,便于以后的学习和使用. 一.shell脚本基础 shell脚本是利用shell的功能 ...
- Linux Shell脚本编程-基础1
概述: shell脚本在Linux系统管理员的运维工作中非常重要.shell脚本能够帮助我们很方便的管理服务器,因为我们可以指定一个任务计划,定时的去执行某一个脚本以满足我们的需求.本篇将从编程基础 ...
- liunx shell 脚本的基础知识
Shell脚本编程30分钟入门====================## 什么是Shell脚本### 示例看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell ...
- 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---57
以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:
- Linux基础篇–shell脚本编程基础
本章内容概要 编程基础 脚本基本格式 变量 运算 条件测试 配置用户环境 7.1 编程基础程序:指令+数据程序编程风格: 过程式:以指令为中心,数据服务于指令 对象式:以数据为中心 ...
随机推荐
- OpenShift上的OpenvSwitch入门
前段时间参加openshift培训,通过产品部门的讲解,刷新了我对OpenShift一些的认识,今天先从最弱的环节网络做一些了解吧. Openvswitch是openshift sdn的核心组件,进入 ...
- [LeetCode] 314. Binary Tree Vertical Order Traversal 二叉树的垂直遍历
Given a binary tree, return the vertical order traversal of its nodes' values. (ie, from top to bott ...
- Jenkins - 以Docker方式安装启动Jenkins
1 - 官网信息 操作步骤:https://jenkins.io/zh/doc/book/installing/#docker Docker映像地址:https://hub.docker.com/r/ ...
- 如何杀死处于进程状态D的进程
D状态的就是 uninterruptible sleep ,此时进程不能被信号唤醒,GDB等调试工具也不能对它调试,因为GDB也是用到了信号,也杀不死它 D状态的形成 如何分析D状态 cat /pro ...
- Linux下用dd命令测试硬盘的读写速度
一.测试写速度: time dd if=/dev/zero of=/tmp/test bs=8k count=1000000 测试结果:565 MB/s 二.测试读速度: time dd if ...
- Kubernetes 配置管理 ConfigMap(十二)
目录 一.背景 二.创建 ConfigMap 2.1.通过 --from-literal 2.2.通过 --from-file 2.3.通过--from-env-file 2.4.YAML 配置文件 ...
- SecureCRT 使用密钥登录 Ubuntu
记录 SecureCRT 通过 SSH 使用密钥登录 Ubuntu. 具体步骤如下: 1. 使用 SecureCRT 生成密钥对: 工具 -> 创建公钥 -> 密钥类型 RSA -> ...
- Appium UiWatchers 监听解决各种非期待弹窗,弹层,弹弹弹等问题
app自动化时,各种不期待的弹层弹窗,升级广告等时有飞出,由于弹窗具有不定时,不定页面等很多不确定性.有的弹窗很不友好,不×掉,很难进行下一步操作,造成 测试用例失败.而判断是否有弹窗,弹层很麻烦.研 ...
- MinGW ,GNU 是什么
MinGW : Minimalist GNU for Windows MinGW(Minimalist GNU For Windows)是个精简的Windows平台下的 C/C++.ADA及Fortr ...
- Java开发笔记(一百一十)GET方式的HTTP调用
所谓术业有专攻,一个程序单靠自身难以吃成大胖子,要想让程序变得血肉丰满,势必令其与外界多加交流,汲取天地之精华,方能练就盖世功夫.那么程序应当如何与外部网络进行通信呢?计算机网络的通信标准主要采取TC ...