Shell学习笔记 四
时间:2008-05-29 来源:liyf0371
三.Linux后台运行命令详述 本次主要讲述后台运行命令(crontab,at,&,nohup)及(*,?,[])等 • 设置c r o n t a b文件,并用它来提交作业。 • 使用a t命令来提交作业。 • 在后台提交作业。 • 使用n o h u p命令提交作业。 名词解释: cron:系统调度进程。可以使用它在每天的非高峰负荷时间段运行作业,或在一周或一月中的不同时段运行。 at命令:使用它在一个特定的时间运行一些特殊的作业,或在晚一些的非负荷高峰时间段或高峰负荷时间段运行。 &:使用它在后台运行一个占用时间不长的进程。 nohup:用它在后台运行一个命令,即使在用户退出时也不受影响 1.cron and crontab c r o n是系统主要的调度进程,可以在无需人工干预的情况下运行作业。c r o n t a b命令允许用户提交、编辑或删除相应的作业。每一个用户都可以有一个c r o n t a b文件来保存调度信息。可以使用它运行任意一个s h e l l脚本或某个命令,每小时运行一次,或一周三次,这完全取决于你。每一个用户都可以有自己的c r o n t a b文件,但在一个较大的系统中,系统管理员一般会禁止这些文件,而只在整个系统保留一个这样的文件。系统管理员是通过c r o n . d e n y和c r o n . a l l o w这两个文件来禁止或允许用户拥有自己的c r o n t a b文件。 crontab的域 为了能够在特定的时间运行作业,需要了解c r o n t a b文件每个条目中各个域的意义和格式。 下面就是这些域:
代码:
第1列分钟1~5 9 第2列小时1~2 3(0表示子夜) 第3列日1~3 1 第4列月1~1 2 第5列星期0~6(0表示星期天) 第6列要运行的命令下面是c r o n t a b的格式:
代码:
分< >时< >日< >月< >星期< >要运行的命令其中< >表示空格。 c r o n t a b文件的一个条目是从左边读起的,第一列是分,最后一列是要运行的命令,它位于星期的后面。 可以用横杠-来表示一个时间范围,例如你希望星期一至星期五运行某个作业,那么可以在星期域使用1 - 5来表示。 还可以在这些域中使用逗号“,”,例如你希望星期一和星期四运行某个作业,只需要使用1 , 4来表示。 可以用星号*来表示连续的时间段。如果你对某个表示时间的域没有特别的限定,也应该在该域填入*。该文件的每一个条目必须含有5个时间域,而且每个域之间要用空格分隔。 该文件中所有的注释行要在行首用#来表示。 c r o n t a b文件例子:
代码:
30 21* * * /apps/bin/cleanup.sh上面的例子表示每晚的2 1 : 3 0运行/ a p p s / b i n目录下的c l e a n u p . s h。
代码:
45 4 1,10,22 * * /apps/bin/backup.sh上面的例子表示每月1、1 0、2 2日的4 : 4 5运行/ a p p s / b i n目录下的b a c k u p . s h。
代码:
10 1 * * 6,0 /bin/find -name "core" -exec rm {} \;上面的例子表示每周六、周日的1 : 1 0运行一个f i n d命令。
代码:
0,30 18-23 * * * /apps/bin/dbcheck.sh上面的例子表示在每天1 8 : 0 0至2 3 : 0 0之间每隔3 0分钟运行/ a p p s / b i n目录下的d b c h e c k . s h。
代码:
0 23 * * 6 /apps/bin/qtrend.sh上面的例子表示每星期六的11 : 0 0 p m运行/ a p p s / b i n目录下的q t r e n d . s h。 你可能已经注意到上面的例子中,每个命令都给出了绝对路径。当使用c r o n t a b运行s h e l l脚本时,要由用户来给出脚本的绝对路径,设置相应的环境变量。记住,既然是用户向c r o n提交了这些作业,就要向c r o n提供所需的全部环境。不要假定c r o n知道所需要的特殊环境,它其实并不知道。所以你要保证在s h e l l脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量。 如果c r o n不能运行相应的脚本,用户将会收到一个邮件说明其中的原因。 c r o n t a b命令的一般形式为:
代码:
crontab [-u user] -e -l -r其中: -u 用户名。 -e 编辑c r o n t a b文件。 -l 列出c r o n t a b文件中的内容。 -r 删除c r o n t a b文件。 如果使用自己的名字登录,就不用使用- u选项,因为在执行c r o n t a b命令时,该命令能够知道当前的用户。 创建一个新的crontab文件 在向c r o n进程提交一个c r o n t a b文件之前,要先设置环境变量E D I TO R.c r o n进程根据它来确定使用哪个编辑器编辑c r o n t a b文件。大部份的U N I X和L I N U X用户都使用v i,如果你也是这样,那么你就编辑$ H O M E目录下的. p r o f i l e文件,在其中加入这样一行:
代码:
EDITOR=vi; export EDITOR然后保存并退出。 创建一个名为< u s e r > c r o n的文件,其中< u s e r >是用户名,例如, samcron。在该文件中加入如下的内容。
代码:
#(put your own initials here) echo the date to the console every #15 minutes between 6pm and 6am 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console保存并退出。确信前面5个域用空格分隔。 在上面的例子中,系统将每隔1 5分钟向控制台输出一次当前时间。如果系统崩溃或挂起,从最后所显示的时间就可以一眼看出系统是什么时间停止工作的。在有些系统中,用t t y 1来表示控制台,可以根据实际情况对上面的例子进行相应的修改。 为了提交你刚刚创建的c r o n t a b文件,可以把这个新创建的文件作为c r o n命令的参数:
代码:
$su sam crontab samcron为了方便演示,切换到sam用户环境下,然后用crontab samcron提交给c r o n进程,它将每隔1 5分钟运行一次。 同时,新创建文件的一个副本已经被放在/ v a r / s p o o l / c r o n目录中,文件名就是用户名(即sam)。
代码:
#su # cat /var/spool/cron/sam # DO NOT EDIT THIS FILE - edit the master and reinstall. # (samcron installed on Wed Nov 10 21:41:55 2004) # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $) #(put your own initials here) echo the date to the console every #15 minutes between 6pm and 6am 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console回到root下,查看/var/spool/cron/sam 列出crontab文件 为了列出c r o n t a b文件,可以用:
代码:
$ crontab -l # DO NOT EDIT THIS FILE - edit the master and reinstall. # (samcron installed on Wed Nov 10 21:41:55 2004) # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $) #(put your own initials here) echo the date to the console every #15 minutes between 6pm and 6am 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console你将会看到和上面类似的内容。可以使用这种方法在$ H O M E目录中对c r o n t a b文件做一备份:
代码:
$ crontab -l > $HOME/mycron这样,一旦不小心误删了c r o n t a b文件,可以用上一节所讲述的方法迅速恢复。 编辑crontab文件 如果希望添加、删除或编辑c r o n t a b文件中的条目,而E D I TO R环境变量又设置为v i,那么就可以用v i来编辑c r o n t a b文件,相应的命令为:
代码:
$ crontab -e可以像使用v i编辑其他任何文件那样修改c r o n t a b文件并退出。如果修改了某些条目或添加了新的条目,那么在保存该文件时, c r o n会对其进行必要的完整性检查。如果其中的某个域出现了超出允许范围的值,它会提示你。 例如,加入下面的一条:
代码:
#DT:delete core files,at 3:30am on 1,7,14,21,26 days of each month 30 3 1,7,14,21,26 * * /bin/find -name "core" -exec rm {} \;现在保存并退出。最好在c r o n t a b文件的每一个条目之上加入一条注释,这样就可以知道它的功能、运行时间,更为重要的是,知道这是哪位用户的作业。 现在让我们使用前面讲过的crontab -l命令列出它的全部信息:
代码:
#(put your own initials here) echo the date to the console every #15 minutes between 6pm and 6am 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console #DT:delete core files,at 3:30am on 1,7,14,21,26 days of each month 30 3 1,7,14,21,26 * * /bin/find -name "core" -exec rm {} \; 删除crontab文件 为了删除c r o n t a b文件,可以用:
代码:
$ crontab -r恢复丢失的crontab文件 如果不小心误删了c r o n t a b文件,假设你在自己的$ H O M E目录下还有一个备份,那么可以将其拷贝到/ v a r / s p o o l / c r o n / < u s e r n a m e >,其中< u s e r n a m e >是用户名。如果由于权限问题无法完成拷贝,可以用:
代码:
$ crontab <filename>其中,< f i l e n a m e >是你在$ H O M E目录中副本的文件名。 建议在自己的$ H O M E目录中保存一个该文件的副本。编辑副本,然后重新提交新的文件。 有些c r o n t a b的变体有些怪异,所以在使用c r o n t a b命令时要格外小心。如果遗漏了任何选项,c r o n t a b可能会打开一个空文件,或者看起来像是个空文件。这时敲d e l e t e键退出,不要按< C t r l - D >,否则你将丢失c r o n t a b文件。 2.at a t命令允许用户向c r o n守护进程提交作业,使其在稍后的时间运行。一旦一个作业被提交, a t命令将会保留所有当前的环境变量,包括路径,不象c r o n t a b,只提供缺省的环境。该作业的所有输出都将以电子邮件的形式发送给用户,除非你对其输出进行了重定向,绝大多数情况下是重定向到某个文件中。 和c r o n t a b一样,根用户可以通过/ e t c目录下的a t . a l l o w和a t . d e n y文件来控制哪些用户可以使用a t命令,哪些用户不行。不过一般来说,对a t命令的使用不如对c r o n t a b的使用限制那么严格。 a t命令的基本形式为:
代码:
at [-f script] [-m -l -r] [time] [date]其中, -f:script 是所要提交的脚本或命令。 -l:列出当前所有等待运行的作业。a t q命令具有相同的作用。 -r:清除作业。为了清除某个作业,还要提供相应的作业标识( I D);有些U N I X变体只接受a t r m作为清除命令。 -m:作业完成后给用户发邮件。 time:at命令的时间格式非常灵活;可以是H、H H . H H M M、H H : M M或H : M,其中H和M分别是小时和分钟。还可以使用a . m .或p . m .。 date:日期格式可以是月份数或日期数,而且a t命令还能够识别诸如t o d a y、t o m o r r o w这样的词。 使用at命令提交命令或脚本 使用a t命令提交作业有几种不同的形式,可以通过命令行方式,也可以使用a t命令提示符。一般来说在提交若干行的系统命令时,使用a t命令提示符方式,在提交s h e l l脚本时,使用命令行方式。 提示符方式:
代码:
以在a t命令后面跟上日期/时间并回车。然后就进入了a t命令提示符,这时只需逐条输入相应的命令,然后按‘ < C T R L - D >’退出。命令行方式:
代码:
at [-f script] [-m -l -r] [time] [date] 例一:提示符方式
代码:
# su sam $ at 10:40 warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh at> find /etc -name "passwd" -print at> <EOT> job 1 at 2004-11-02 10:40其中, < E O T >就是< C T R L - D >。在10:40系统将执行一个简单的f i n d命令。提交的作业被分配了一个唯一标识job 1。该命令在完成以后会将全部结果以邮件的形式发送给我。 下面这些日期/时间格式都是a t命令可以接受的:
代码:
at 5.00am May23 at 11.20pm at now +2 hour at 9am tomorrow at 15:00 May24 at now + 10 minutes例二:命令行方式 如果希望向a t命令提交一个s h e l l脚本,使用其命令行方式即可。在提交脚本时使用- f选项。 如:
代码:
$ touch db_table.sh $ at 3:00pm tomorrow -f db_table.sh warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh job 3 at 2004-11-02 15:00在上面的例子中,一个叫做d b _ t a b l e . s h的脚本将在2004-11-02 15:00运行。 还可以使用e c h o命令向a t命令提交作业:
代码:
$ echo find /etc -name "passwd" -print | at now +1 minute warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh job 4 at 2004-11-01 19:07 列出所提交的作业 一个作业被提交后,可以使用at -l命令来列出所有的作业:
代码:
$ at -l 1 2004-11-02 10:40 a sam 3 2004-11-02 15:00 a sam 4 2004-11-01 19:07 a sam其中,第一行是作业标识,后面是作业运行的日期/时间。最后一列a代表a t。 还可以使用a t q命令来完成同样的功能,它是a t命令的一个链接。 直接>atq,相当于>at -l 当提交一个作业后,它就被拷贝到/ v a r / s p o o l / a t目录中,准备在要求的时间运行。
代码:
# pwd /var/spool/at # ls -l清除一个作业 清除作业的命令格式为:
代码:
atrm [job no] 或at -r [job no]要清除某个作业,首先要执行at -l命令,以获取相应的作业标识,然后对该作业标识使用at -r 命令,清除该作业。
代码:
$ at -l 1 2004-11-02 10:40 a sam 3 2004-11-02 15:00 a sam 4 2004-11-01 19:07 a sam $at -r 3 $at -l 1 2004-11-02 10:40 a sam 4 2004-11-01 19:07 a sam 有些系统使用at-r [job no]命令清除作业。 3.& 当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。可以使用&命令把作业放到后台执行。 该命令的一般形式为:
代码:
命令& 在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。 不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:
代码:
command >out.file 2>&1 &在上面的例子中,2>&1表示所有的标准输出和错误输出都将被重定向到一个叫做out.file 的文件中。 当你成功地提交进程以后,就会显示出一个进程号,可以用它来监控该进程,或杀死它。 例一: 查找名为“httpd.conf”的文件,并把所有标准输出和错误输出重定向到f i n d . d t的文件中:
代码:
# find /etc/httpd/ -name "httpd.conf" -print >find.dt 2>&1 & [2] 7832 [1] Done find /etc/ -name "httpd.conf" -print >find.dt 2>&1 &成功提交该命令之后,系统给出了它的进程号7832。
代码:
# cat find.dt /etc/httpd/conf/httpd.conf [2]+ Done find /etc/httpd/ -name "httpd.conf" -print >find.dt 2>&1 &查看find.dt,可以看到执行结果 例二: 在后台执行脚本,如:有一个叫psl的脚本
代码:
$ps psl & [7878]用ps命令查看进程 用提交命令时所得到的进程号来监控它的运行。用p s命令和g r e p命令列出这个进程:
代码:
# ps -x |grep 7832 7868 pts/0 S 0:00 grep 7832如果系统不支持ps x命令,可以用:
代码:
# ps -ef |grep 7832 root 7866 7790 0 23:40 pts/0 00:00:00 grep 7832在用p s命令列出进程时,它无法确定该进程是运行在前台还是后台。 杀死后台进程 杀死后台进程可以使用k i l l命令。当一个进程被放到后台运行时, s h e l l会给出一个进程号,我们可以根据这个进程号,用k i l l命令杀死该进程。该命令的基本形式为:
代码:
kill -signal [process_number]现在暂且不要考虑其中的各种不同信号。 在杀进程的时候,执行下面的命令(你的进程号可能会不同)并按回车键。系统将会给出相应的信息告诉用户进程已经被杀死。
代码:
$kill 7832如果系统没有给出任何信息,告诉你进程已经被杀死,那么不妨等一会儿,也许系统正在杀该进程,如果还没有回应,就再执行另外一个k i l l命令,这次带上一个信号选项:
代码:
$kill - 9 7868如果用上述方法提交了一个后台进程,那么在退出时该进程将会被终止。为了使后台进程能够在退出后继续运行,可以使用n o h u p命令。 4.nohug 如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用n o h u p命令。该命令可以在你退出帐户之后继续运行相应的进程。n o h u p就是不挂起的意思( no hang up)。 该命令的一般形式为:
代码:
nohup command &使用nohup命令提交作业 如果使用n o h u p命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为n o h u p . o u t的文件中,除非另外指定了输出文件:
代码:
nohup command > myout.file 2>&1在上面的例子中,输出被重定向到m y o u t . f i l e文件中。 让我们来看一个例子,验证一下在退出帐户后相应的作业是否能够继续运行。我们先提交一个名为p s 1的日志清除进程:
代码:
$nobup ps1 &现在退出该s h e l l,再重新登录,然后执行下面的命令:
代码:
$ps x |grep ps1我们看到,该脚本还在运行。如果系统不支持ps x命令,使用ps -ef|grep ps1命令。 5.一次提交几个作业 如果希望一次提交几个命令,最好能够把它们写入到一个s h e l l脚本文件中,并用n o h u p命令来执行它。 例如,下面的所有命令都用管道符号连接在一起;我们可以把这些命令存入一个文件,并使该文件可执行。
代码:
cat /home/accounts/qrt_0499 | /apps/bin/trials.awk | sort | lp $cat > quarterend cat /home/accounts/qtr_0499 | /apps/bin/trials.awk | sort | lp <ctrl-D>现在让它可执行:
代码:
$ chmod 744 quarterend我们还将该脚本的所有输出都重定向到一个名为q t r. o u t的文件中。
代码:
nobup ./quarterend > qtr.out 2>后台运行作业的: 有时我们必须要对大文件进行大量更改,或执行一些复杂的查找,这些工作最好能够在系统负荷较低时执行。 创建一个定时清理日志文件或完成其他特殊工作的脚本,这样只要提交一次,就可以每天晚上运行,而且无需你干预,只要看看相应的脚本日志就可以了。c r o n和其他工具可以使系统管理任务变得更轻松。 6.*,?,[...],[!...]等 • 匹配文件名中的任何字符串。 • 匹配文件名中的单个字符。 • 匹配文件名中的字母或数字字符。 下面就是这些特殊字符: * 匹配文件名中的任何字符串,包括空字符串。 ? 匹配文件名中的任何单个字符。 [...] 匹配[ ]中所包含的任何字符。 [!...] 匹配[ ]中非感叹号!之后的字符。 当s h e l l遇到上述字符时,就会把它们当作特殊字符,而不是文件名中的普通字符,这样用户就可以用它们来匹配相应的文件名。 a、*:使用星号*可以匹配文件名中的任何字符串。就不用多说了,和win下差不多 b、?:使用可以匹配文件名中的任何单个字符。和win差不多 c、[]:使用[ . . . ]可以用来匹配方括号[ ]中的任何字符。可以使用一个横杠-来连接两个字母或数字,以此来表示一个范围。 1)列出以i或o开头的文件名:
代码:
#ls [io]*2)列出log.开头、后面跟随一个数字、然后可以是任意字符串的文件名:
代码:
#ls log.[0-9]*3)与例二相反,列出log.开头、后面不跟随一个数字、然后可以是任意字符串的文件名
代码:
#ls log.[!0-9]*4)列出所有以LPS开头、中间可以是任何两个字符,最后以1结尾的文件名:
代码:
#ls LPS??15)列出所有以大写字母开头的文件名:
代码:
$ ls [A-Z]*6)列出所有以小写字母开头的文件名:
代码:
$ ls [a-z]*7)为了列出所有以数字开头的文件名:
代码:
$ ls [0-9]*8)列出所有以. 开头的文件名(隐含文件,例如. p r o f i l e、. r h o s t s、. h i s t o r y等):
代码:
$ ls .* |
||
zhy2111314 |
查看公开信息 |
访问 zhy2111314 的个人网站 |
查找 zhy2111314 发表的更多帖子 |
05-03-17, 08:44 | 第 8 帖 |
zhy2111314
版主 注册日期: Oct 2004 我的住址: China QD 帖子: 782 精华: 11 |
四.Linux输入和输出命令详述 1.echo 首先,在LINUX中,要使转义符生效,需加参数-e 从echo的变量开始说起 如:e c h o命令输出转义符以及变量。
代码:
# echo -e "\007your home is $HOME , you are connected on `tty`" your home is /root , you are connected on /dev/pts/1 # echo -e "\ayour home is $HOME , you are connected on `tty`" your home is /root , you are connected on /dev/pts/1 #本例中 \007或\a你可以让终端铃响一声 显示出$ H O M E目录, 并且可以让系统执行t t y命令(注意,该命令用键盘左上角的符号,法语中的抑音符引起来,不是单引号 )。 在e c h o命令输出之后附加换行,可以使用\ n选项:
代码:
$ cat echod #!/bin/sh echo -e "this echo's 3 new lines\n\n\n" echo "OK"编辑一个新echod,如上内容,然后运行输出如下:
代码:
$ ./echod this echo's 3 new lines OK $在e c h o语句中使用跳格符,记住别忘了加反斜杠\:
代码:
$ echo -e "here is a tab\there are two tabs\t\tok" here is a tab here are two tabs ok $把一个字符串输出到文件中,使用重定向符号>。 在下面的例子中一个字符串被重定向到一个名为m y f i l e的文件中:
代码:
$ echo "The log files have all been done"> myfile或者可以追加到一个文件的末尾,这意味着不覆盖原有的内容:
代码:
$ echo "$LOGNAME carried them out at `date`">>myfile现在让我们看一下m y f i l e文件中的内容:
代码:
The log files have all been done sam carried them out at 六 11月 13 12:54:32 CST 2004引号是一个特殊字符,所以必须要使用反斜杠\来使s h e l l忽略它的特殊含义。 假设你希望使用e c h o命令输出这样的字符串:“/ d e v / r m t 0”,那么我们只要在引号前面加上反斜杠\即可:
代码:
$ echo "\"/dev/rmt0"\" "/dev/rmt0" $上面说过,
代码:
在LINUX中,要使转义符生效,需加参数-e那么echo可以用的转义字符有下面这些:
代码:
\a 鸣叫beep \n 换行 \t 水平制表 \v 垂直制表 \b 退格 \r 回车 \f 换页 \\ \ \' ' \" " \ddd 一到三位八进制数ddd所代表的字符 \xhh 一到二位十六进制数hh所代表的字符 (这两个都是ASCII码) \c 取消末行换行符,等价于echo -n ...ps:注意其中\n和\r的区别,可以自己实验...... 补充: echo的特殊用法 a.把字符串输出到标准显示的指定位置:
代码:
r= #指定行 c= #指定列 echo -e "\033[${r};${c}H我在这里"b.隐藏光标:
代码:
echo -ne "\033[?25l"其中25后面是字母l c.ANSI控制码 例如:
代码:
echo -ne "\033[32m" #可以将字符的显示颜色改为绿色 echo -ne "\033[3;1H" #可以将光标移到第3行第1列处具体的摘抄一些如下:
代码:
\033[0m #关闭所有属性 \033[1m #设置高亮度 \033[4m #下划线 \033[5m #闪烁 \033[7m #反显 \033[8m #消隐 \033[30m -- \33[37m #设置前景色 \033[40m -- \33[47m #设置背景色 \033[nA #光标上移n行 \033[nB #光标下移n行 \033[nC #光标右移n行 \033[nD #光标左移n行 \033[y;xH #设置光标位置 \033[2J #清屏 \033[K #清除从光标到行尾的内容 \033[s #保存光标位置 \033[u #恢复光标位置 \033[?25l #隐藏光标 \033[?25h #显示光标下面是摘抄的shell中俄罗斯方块程序,我也没有具体研究,先放到这里,有兴趣可以研究一下 运行于GNU bash, version 2.05a.0(1)-release (i686-pc-linux-gnu)
代码:
#!/bin/bash # Tetris Game # 10.21.2003 xhchen<[email protected]> #颜色定义 cRed=1 cGreen=2 cYellow=3 cBlue=4 cFuchsia=5 cCyan=6 cWhite=7 colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite) #位置和大小 iLeft=3 iTop=2 ((iTrayLeft = iLeft + 2)) ((iTrayTop = iTop + 1)) ((iTrayWidth = 10)) ((iTrayHeight = 15)) #颜色设置 cBorder=$cGreen cScore=$cFuchsia cScoreValue=$cCyan #控制信号 #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面; #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。 sigRotate=25 sigLeft=26 sigRight=27 sigDown=28 sigAllDown=29 sigExit=30 #七中不同的方块的定义 #通过旋转,每种方块的显示的样式可能有几种 box0=(0 0 0 1 1 0 1 1) box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3) box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0) box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1) box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2) box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2) box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2) #所有其中方块的定义都放到box变量中 box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]}) #各种方块旋转后可能的样式数目 countBox=(1 2 2 2 4 4 4) #各种方块再box数组中的偏移 offsetBox=(0 1 3 5 7 11 15) #每提高一个速度级需要积累的分数 iScoreEachLevel=50 #be greater than 7 #运行时数据 sig=0 #接收到的signal iScore=0 #总分 iLevel=0 #速度级 boxNew=() #新下落的方块的位置定义 cBoxNew=0 #新下落的方块的颜色 iBoxNewType=0 #新下落的方块的种类 iBoxNewRotate=0 #新下落的方块的旋转角度 boxCur=() #当前方块的位置定义 cBoxCur=0 #当前方块的颜色 iBoxCurType=0 #当前方块的种类 iBoxCurRotate=0 #当前方块的旋转角度 boxCurX=-1 #当前方块的x坐标位置 boxCurY=-1 #当前方块的y坐标位置 iMap=() #背景方块图表 #初始化所有背景方块为-1, 表示没有方块 for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done #接收输入的进程的主函数 function RunAsKeyReceiver() { local pidDisplayer key aKey sig cESC sTTY pidDisplayer=$1 aKey=(0 0 0) cESC=`echo -ne "\33"` cSpace=`echo -ne "\40"` #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。 #如果在read -s时程序被不幸杀掉,可能会导致终端混乱, #需要在程序退出时恢复终端属性。 sTTY=`stty -g` #捕捉退出信号 trap "MyExit;" INT TERM trap "MyExitNoSub;" $sigExit #隐藏光标 echo -ne "\33[?25l" while (( 1 )) do #读取输入。注-s不回显,-n读到一个字符立即返回 read -s -n 1 key aKey[0]=${aKey[1]} aKey[1]=${aKey[2]} aKey[2]=$key sig=0 #判断输入了何种键 if [[ $key == $cESC && ${aKey[1]} == $cESC ]] then #ESC键 MyExit elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]] 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 != 0 ]] then #向另一进程发送消息 kill -$sig $pidDisplayer fi done } #退出前的恢复 function MyExitNoSub() { local y #恢复终端属性 stty $sTTY ((y = iTop + iTrayHeight + 4)) #显示光标 echo -e "\33[?25h\33[${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 (( 1 )) do #根据当前的速度级iLevel不同,设定相应的循环的次数 for ((i = 0; i < 21 - iLevel; i++)) do sleep 0.02 sigThis=$sig sig=0 #根据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=$1 xTest=$2 for ((j = 0; j < 8; j += 2)) do ((i = j + 1)) ((y = ${boxCur[$j]} + yTest)) ((x = ${boxCur[$i]} + xTest)) if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth)) then #撞到墙壁了 return 1 fi if ((${iMap[y * iTrayWidth + x]} != -1 )) then #撞到其他已经存在的方块了 return 1 fi done return 0; } #将当前移动中的方块放到背景方块中去, #并计算新的分数和速度级。(即一次方块落到底部) function Box2Map() { local j i x y xp yp line #将当前移动中的方块放到背景方块中去 for ((j = 0; j < 8; j += 2)) do ((i = j + 1)) ((y = ${boxCur[$j]} + boxCurY)) ((x = ${boxCur[$i]} + boxCurX)) ((i = y * iTrayWidth + x)) iMap[$i]=$cBoxCur done #消去可被消去的行 line=0 for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth)) do for ((i = j + iTrayWidth - 1; i >= j; i--)) do if ((${iMap[$i]} == -1)); then break; fi done if ((i >= j)); then continue; fi ((line++)) for ((i = j - 1; i >= 0; i--)) do ((x = i + iTrayWidth)) iMap[$x]=${iMap[$i]} done for ((i = 0; i < iTrayWidth; i++)) do iMap[$i]=-1 done done if ((line == 0)); then return; fi #根据消去的行数line计算分数和速度级 ((x = iLeft + iTrayWidth * 2 + 7)) ((y = iTop + 11)) ((iScore += line * 2 - 1)) #显示新的分数 echo -ne "\33[1m\33[3${cScoreValue}m\33[${y};${x}H${iScore} " if ((iScore % iScoreEachLevel < line * 2 - 1)) then if ((iLevel < 20)) then ((iLevel++)) ((y = iTop + 14)) #显示新的速度级 echo -ne "\33[3${cScoreValue}m\33[${y};${x}H${iLevel} " fi fi echo -ne "\33[0m" #重新显示背景方块 for ((y = 0; y < iTrayHeight; y++)) do ((yp = y + iTrayTop + 1)) ((xp = iTrayLeft + 1)) ((i = y * iTrayWidth)) echo -ne "\33[${yp};${xp}H" for ((x = 0; x < iTrayWidth; x++)) do ((j = i + x)) if ((${iMap[$j]} == -1)) then echo -ne " " else echo -ne "\33[1m\33[7m\33[3${iMap[$j]}m\33[4${iMap[$j]}m[]\33[0m" fi done done } #下落一行 function BoxDown() { local y s ((y = boxCurY + 1)) #新的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 - 1)) if BoxMove $boxCurY $x then s=`DrawCurBox 0` ((boxCurX = x)) s=$s`DrawCurBox 1` echo -ne $s fi } #右移一列 function BoxRight() { local x s ((x = boxCurX + 1)) if BoxMove $boxCurY $x then s=`DrawCurBox 0` ((boxCurX = x)) s=$s`DrawCurBox 1` echo -ne $s fi } #下落到底 function BoxAllDown() { local k j i x y iDown s iDown=$iTrayHeight #计算一共需要下落多少行 for ((j = 0; j < 8; j += 2)) do ((i = j + 1)) ((y = ${boxCur[$j]} + boxCurY)) ((x = ${boxCur[$i]} + boxCurX)) for ((k = y + 1; k < iTrayHeight; k++)) do ((i = k * iTrayWidth + x)) if (( ${iMap[$i]} != -1)); then break; fi done ((k -= y + 1)) if (( $iDown > $k )); then iDown=$k; fi done s=`DrawCurBox 0` #将旧的方块抹去 ((boxCurY += iDown)) s=$s`DrawCurBox 1` #显示新的下落后的方块 echo -ne $s Box2Map #将当前移动中的方块贴到背景方块中 RandomBox #产生新的方块 } #旋转方块 function BoxRotate() { local iCount iTestRotate boxTest j i s iCount=${countBox[$iBoxCurType]} #当前的方块经旋转可以产生的样式的数目 #计算旋转后的新的样式 ((iTestRotate = iBoxCurRotate + 1)) if ((iTestRotate >= iCount)) then ((iTestRotate = 0)) fi #更新到新的样式, 保存老的样式(但不显示) for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++)) do boxTest[$j]=${boxCur[$j]} boxCur[$j]=${box[$i]} done if BoxMove $boxCurY $boxCurX #测试旋转后是否有空间放的下 then #抹去旧的方块 for ((j = 0; j < 8; j++)) do boxCur[$j]=${boxTest[$j]} done s=`DrawCurBox 0` #画上新的方块 for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++)) do boxCur[$j]=${box[$i]} done s=$s`DrawCurBox 1` echo -ne $s iBoxCurRotate=$iTestRotate else #不能旋转,还是继续使用老的样式 for ((j = 0; j < 8; j++)) do boxCur[$j]=${boxTest[$j]} done fi } #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。 function DrawCurBox() { local i j t bDraw sBox s bDraw=$1 s="" if (( bDraw == 0 )) then sBox="\40\40" else sBox="[]" s=$s"\33[1m\33[7m\33[3${cBoxCur}m\33[4${cBoxCur}m" fi for ((j = 0; j < 8; j += 2)) do ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY)) ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]}))) #\33[y;xH, 光标到(x, y)处 s=$s"\33[${i};${t}H${sBox}" done s=$s"\33[0m" echo -n $s } #更新新的方块 function RandomBox() { local i j t #更新当前移动的方块 iBoxCurType=${iBoxNewType} iBoxCurRotate=${iBoxNewRotate} cBoxCur=${cBoxNew} for ((j = 0; j < ${#boxNew[@]}; j++)) do boxCur[$j]=${boxNew[$j]} done #显示当前移动的方块 if (( ${#boxCur[@]} == 8 )) then #计算当前方块该从顶端哪一行"冒"出来 for ((j = 0, t = 4; j < 8; j += 2)) do if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi done ((boxCurY = -t)) for ((j = 1, i = -4, t = 20; j < 8; j += 2)) do if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi done ((boxCurX = (iTrayWidth - 1 - i - t) / 2)) #显示当前移动的方块 echo -ne `DrawCurBox 1` #如果方块一出来就没处放,Game over! if ! BoxMove $boxCurY $boxCurX then kill -$sigExit ${PPID} ShowExit fi fi #清除右边预显示的方块 for ((j = 0; j < 4; j++)) do ((i = iTop + 1 + j)) ((t = iLeft + 2 * iTrayWidth + 7)) echo -ne "\33[${i};${t}H " done #随机产生新的方块 ((iBoxNewType = RANDOM % ${#offsetBox[@]})) ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]})) for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++)) do boxNew[$j]=${box[$i]}; done ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]})) #显示右边预显示的方块 echo -ne "\33[1m\33[7m\33[3${cBoxNew}m\33[4${cBoxNew}m" for ((j = 0; j < 8; j += 2)) do ((i = iTop + 1 + ${boxNew[$j]})) ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]})) echo -ne "\33[${i};${t}H[]" done echo -ne "\33[0m" } #初始绘制 function InitDraw() { clear RandomBox #随机产生方块,这时右边预显示窗口中有方快了 RandomBox #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落 local i t1 t2 t3 #显示边框 echo -ne "\33[1m" echo -ne "\33[3${cBorder}m\33[4${cBorder}m" ((t2 = iLeft + 1)) ((t3 = iLeft + iTrayWidth * 2 + 3)) for ((i = 0; i < iTrayHeight; i++)) do ((t1 = i + iTop + 2)) echo -ne "\33[${t1};${t2}H||" echo -ne "\33[${t1};${t3}H||" done ((t2 = iTop + iTrayHeight + 2)) for ((i = 0; i < iTrayWidth + 2; i++)) do ((t1 = i * 2 + iLeft + 1)) echo -ne "\33[${iTrayTop};${t1}H==" echo -ne "\33[${t2};${t1}H==" done echo -ne "\33[0m" #显示"Score"和"Level"字样 echo -ne "\33[1m" ((t1 = iLeft + iTrayWidth * 2 + 7)) ((t2 = iTop + 10)) echo -ne "\33[3${cScore}m\33[${t2};${t1}HScore" ((t2 = iTop + 11)) echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iScore}" ((t2 = iTop + 13)) echo -ne "\33[3${cScore}m\33[${t2};${t1}HLevel" ((t2 = iTop + 14)) echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iLevel}" echo -ne "\33[0m" } #退出时显示GameOVer! function ShowExit() { local y ((y = iTrayHeight + iTrayTop + 3)) echo -e "\33[${y};0HGameOver!\33[0m" exit } #游戏主程序在这儿开始. if [[ $1 != "--show" ]] then bash $0 --show& #以参数--show将本程序再运行一遍 RunAsKeyReceiver $! #以上一行产生的进程的进程号作为参数 exit else #当发现具有参数--show时,运行显示函数 RunAsDisplayer exit fi2.read 格式:
代码:
read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]当read命令没有参数[name ...]时,直接设在REPLY上。 可以用$REPLY引用. 其他命令参数看下面运行实例:
代码:
zhyfly: ~/1$ read what zhyfly: ~/1$ echo $REPLY what zhyfly: ~/1$ read name zhyfly zhyfly: ~/1$ echo $name zhyfly zhyfly: ~/1$ read name veb name1 i love you zhyfly: ~/1$ echo $name $veb $name1 i love you zhyfly: ~/1$ read name veb name1 i love zhyfly: ~/1$ echo "$name,$veb,$name1" i,love, zhyfly: ~/1$ read name veb i love you zhyfly: ~/1$ echo "$name,$veb" i,love you zhyfly: ~/1$ read -p "how old r u?" age how old r u?23 zhyfly: ~/1$ echo $age 23 zhyfly: ~/1$ read -p "some words?" -a words some words?i love u! zhyfly: ~/1$ echo ${words[*]} i love u! zhyfly: ~/1$ read -p "passwd:" -s passwd passwd:#输入密码zhyfly: ~/1$ echo $passwd zhyfly zhyfly: ~/1$ read -p "type the string end with n characters:" -n 5 type the string end with n characters:34352zhyfly: ~/1$ zhyfly: ~/1$ read -p "type the string end with the letter you set:" -dp type the string end with the letter you set:hahaw^?^?pzhyfly: ~/1$ zhyfly: ~/1$ read -p "type the string in the seconds you set:" -t 5 test type the string in the seconds you set:zhyfly: ~/1$ #5秒钟不反应自动退出 zhyfly: ~/1$ read -p 'the \ will work as a metacharater!' a the \ will work as a metacharater!bc\$d zhyfly: ~/1$ echo $a bc$d zhyfly: ~/1$ read -r -p 'the \ will not work as a metacharater!' A the \ will not work as a metacharater!bc\$d zhyfly: ~/1$ echo $A bc\$d一个echo和read的例子:
代码:
zhyfly: ~/1$ cat test #!/bin/bash echo -n "First name:" read firstname echo -n "Middle name:" read middlename echo -e "Last name:\c" read lastname echo "$firstname,$middlename,$lastname" zhyfly: ~/1$ sudo chmod +x test zhyfly: ~/1$ ./test First name:zhy Middle name:2 Last name:fly zhy,2,fly3.cat cat:显示文件内容,创建文件,还可以用它来显示控制字符。 注意:在文件分页符处不会停下来;会一下显示完整个文件。因此,可以使用m o r e命令或把c a t命令的输出通过管道传递到另外一个具有分页功能的命令中,使用命令less file可实现相同的功能。 如下形式
代码:
$cat myfile | more或
代码:
$cat myfile | pg或
代码:
$cat myfile | lessc a t命令的一般形式为:
代码:
cat [options] filename1 ... filename2 ...a、显示名为m y f i l e的文件:
代码:
$ cat myfileb、显示m y f i l e 1、m y f i l e 2、m y f i l e 3这三个文件,可以用:
代码:
$ cat myfile1 myfile2 myfile3c、创建一个包含上述三个文件的内容,名为b i g f i l e的文件,可以用输出重定向到新文件中:
代码:
$ cat myfile1 myfile2 myfile3 > bigfiled、如果cat的命令行中没有参数,输入的每一行都立刻被cat命令输出到屏幕上,输入完毕后按< C T R L - D >结束
代码:
$ cat Hello world Hello world <ctrl+d> $e、新建文件
代码:
$cat >myfile This is great <ctrl-d> $cat myfile This is great cat:参数选项 使用方式:
代码:
cat [-AbeEnstTuv] [--help] [--version] fileName说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案) 参数: -n 或 --number 由 1 开始对所有输出的行数编号 -b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号 -s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行 -v 或 --show-nonprinting 显示非打印字符 例: 显示时加上行号
代码:
$cp /etc/httpd/conf/httpd /usr/sam $ cat -n httpd.conf把 httpd.conf 的内容加上行号后输入 httpd1.conf 这个文件里
代码:
$cat -n httpd.conf > httpd1.conf对文件httpd.conf加上行号(空白不加)后显示
代码:
$ cat -b httpd.conf把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。
代码:
$ cat -b textfile1 textfile2 >> textfile3清空/etc/test.txt档案内容
代码:
$cat /dev/null > /etc/test.txt使用 sed 与 cat 除去空白行
代码:
$ cat -s /etc/X11/XF86Config | sed '/^[[:space:]]*$/d'cat 还可以在您查看包含如制表符这样的非打印字符的文件时起帮助作用。您可以用以下选项来显示制表符: * -T 将制表符显示为 ^I * -v 显示非打印字符,除了换行符和制表符,它们使用各自效果相当的“控制序列”。例如,当您处理一个在 Windows 系统中生成的文件时,这个文件将使用 Control-M(^M)来标记行的结束。对于代码大于 127 的字符,它们的前面将会被加上 M-(表示“meta”),这与其它系统中在字符前面加上 Alt- 相当。 * -E 在每一行的结束处添加美元符($)。 显示非打印字符
代码:
$ cat -t /etc/X11/XF86Config ... # Multiple FontPath entries are allowed (they are concatenated together) # By default, Red Hat 6.0 and later now use a font server independent of # the X server to render fonts. ^IFontPath^I"/usr/X11R6/lib/X11/fonts/TrueType" ^IFontPath^I"unix/:7100" EndSection ...
代码:
$ cat -E /etc/X11/XF86Config ... # Multiple FontPath entries are allowed (they are concatenated together)$ # By default, Red Hat 6.0 and later now use a font server independent of$ # the X server to render fonts.$ $ FontPath "/usr/X11R6/lib/X11/fonts/TrueType"$ FontPath "unix/:7100"$ $ EndSection$ ...
代码:
$ cat -v /etc/X11/XF86Config ... ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-|M-8^X^@^@^@ P^@^O"M-X^O M-@^M^@^@^@M-^@^O"M-@M-k^@M-8*^@ @M-^H$M-@M-9|A(M-@)M-yM-|M-sM-*M-hW^A^@^@j^@ M-|M-sM-%1M-@M-9^@^B^@^@M-sM-+fM-^A= ^@ ^@ F^@^@ ^@M-9^@^H^@^@M-sM-$M-G^E(l!M-@M-^? ^IM-A5^@^@^D^@PM-^]M-^\X1M-H%^@^@^D^@tyM-G ...4.tee tee:读取标准输入的数据,并将其内容输出成文件。 语 法:tee [-ai][--help][--version][文件…] 补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。我们可利用tee把管道导入的数据存成文件,甚至一次保存数份文件。 参 数:-a 附加到既有文件的面,而非覆盖它。如果给予tee指令的文件名称已经存在,预设会覆盖该文件的内容。加上此参数,数据会新增在该文件内容的最面,而不会删除原先之内容。 -i 忽略中断信号 --help 在线帮助 --version 显示版本信息 例一: 列出文本文件slayers.story的内容,同时复制3份副本,文件名称分别为ss-copy1、ss-copy2、ss-copy3:
代码:
$ cat slayers.story |tee ss-copy1 ss-copy2 ss-copy3例二: 把列出当前目录,并把结果结到myfile里
代码:
$ls -l |tee myfile 管道:可以通过管道把一个命令的输出传递给另一个命令作为输入。管道用竖杠|表示。它的一般形式为: 命令1 |命令2 其中|是管道符号。 |
相关阅读 更多 +
排行榜 更多 +
Copyright 2006-2020 (phpxiu.com) All Rights Reserved.
本站为非盈利性网站,不接受任何广告。php爱好者