月归档:2014年08月

openwrt路由器宽带频繁掉线解决方案

一台OPENWRT系统的路由器在电脑大流量下载时候频繁掉线。很恼火。

查看日志

Aug 31 08:54:39 Home-Ant daemon.info pppd[2168]: No response to 5 echo-requests
Aug 31 08:54:39 Home-Ant daemon.notice pppd[2168]: Serial link appears to be disconnected.
Aug 31 08:54:39 Home-Ant daemon.info pppd[2168]: Connect time 345.5 minutes.
Aug 31 08:54:39 Home-Ant daemon.info pppd[2168]: Sent 8797017 bytes, received 83886313 bytes.
Aug 31 08:54:39 Home-Ant daemon.notice pppd[2168]: Connection terminated.

先说说 ADSL 的拨号网络。为了保证 pppoe 拨号连接的稳定,pppoe 拨号设定了两个值,用于测试系统的连接是否在线。openwrt这两个配置默认分别是: 

lcp-echo-failure 1 

lcp-echo-interval 5

上面两个配置信息是 对系统 ADSL 服务端进行 pppoe 连接拨号后,每隔 5秒进行adsl回执测试,如果 5s秒内无法收到 ADSL 服务器的回执,则算一次失败,经过 1 次失败后,确认为 系统 pppoe 拨号断开,也就是我们常说的 ADSL 连接断开。由于国内的网络不稳定,所以这些测试时间根本不足以用来测试 ADSL 的在线时间。 

分别找到 lcp-echo-failure 与 lcp-echo-interval,根据上面的理解,将这两个的值修改得大一点。 
我是将 lcp-echo-failure的值改大的,这样就可以在国内网络不会自动断开连接了。


继续阅读

留下评论

SPI驱动m25p80.c

./linux-2.6.21.x/drivers/mtd/devices/m25p80.c
./linux-2.6.36MT.x/drivers/mtd/devices/m25p80.c
./linux-2.6.36.x/drivers/mtd/devices/m25p80.c

继续阅读

留下评论

Linux 在一个命令行上执行多个命令

对于单个命令执行我想大多数人都是明了的,也就是在一个命令行上执行一条命令。那对于在一行上执行多个命令怎么办呢,其实也很简单,只需在各命令之间加上特殊命令符号,我们常规使用到的有3个特殊命令符号。
 
1. [ ; ]
如果被分号(;)所分隔的命令会连续的执行下去,就算是错误的命令也会继续执行后面的命令。
[root@localhost etc]# lld ; echo “ok” ; lok
-bash: lld: command not found
ok
-bash: lok: command not found
2. [ && ]
如果命令被 && 所分隔,那么命令也会一直执行下去,但是中间有错误的命令存在就不会执行后面的命令,没错就直行至完为止。
[root@localhost etc]# echo “ok” && lld && echo “ok”
ok
-bash: lld: command not found
3. [ || ]
如果每个命令被双竖线 || 所分隔,那么一遇到可以执行成功的命令就会停止执行后面的命令,而不管后面的命令是否正确与否。如果执行到错误的命令就是继续执行后一个命令,一直执行到遇到正确的命令为止。
[root@localhost etc]# echo “ok” || echo “haha”
ok
[root@localhost etc]# lld || echo “ok” || echo “haha”
-bash: lld: command not found
ok
继续阅读

发表在 Tips | 留下评论

linux命令后台运行

 有两种方式:

   1. command & : 后台运行,你关掉终端会停止运行
   2. nohup command & : 后台运行,你关掉终端也会继续运行

   

一、 简介 
    Linux/Unix 区别于微软平台最大的优点就是真正的多用户,多任务。因此在任务管理上也有别具特色的管理思想。
我们知道,在 Windows 上面,我们要么让一个程序作为服务在后台一直运行,要么停止这个服务。而不能让程序在前台后台之间切换。而 Linux 提供了 fg 和bg 命令,让你轻松调度正在运行的任务。假设你发现前台运行的一个程序需要很长的时间,但是需要干其他的事情,你就可以用 Ctrl-Z ,挂起这个程序,然后可以看到系统提示:
[1]+ Stopped /root/bin/rsync.sh
然后我们可以把程序调度到后台执行:(bg 后面的数字为作业号)
#bg 1
[1]+ /root/bin/rsync.sh &
用 jobs 命令查看正在运行的任务:
#jobs
[1]+ Running /root/bin/rsync.sh &
如果想把它调回到前台运行,可以用
#fg 1
/root/bin/rsync.sh
这样,你在控制台上就只能等待这个任务完成了。

& 将指令丢到后台中去执行
[ctrl]+z 將前台任务丟到后台中暂停
jobs 查看后台的工作状态
fg %jobnumber 将后台的任务拿到前台来处理
bg %jobnumber 将任务放到后台中去处理
kill 管理后台的任务

二、&

在Linux中,当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。可以使用&命令把作业放到后台执行。实际上,这样是将命令放入到一个作业队列中了:

$ ./test.sh &
[1] 17208

$ jobs -l
[1]+ 17208 Running                 ./test.sh &
    在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:
command >out.file 2>&1 &
在上面的例子中,2>&1表示所有的标准输出和错误输出都将被重定向到一个叫做out.file 的文件中。 当你成功地提交进程以后,就会显示出一个进程号,可以用它来监控该进程,或杀死它。 
例:查找名为“httpd.conf”的文件,并把所有标准输出和错误输出重定向到find.dt的文件中: 
# find /etc/httpd/ -name “httpd.conf” -print >find.dt 2>&1 & 
[2] 7832 
成功提交该命令之后,系统给出了它的进程号7832。 对于已经在前台执行的命令,也可以重新放到后台执行,首先按ctrl+z暂停已经运行的进程,然后使用bg命令将停止的作业放到后台运行,例如对正在前台执行的tesh.sh使用ctrl+z挂起它:
$ ./test.sh
[1]+ Stopped                 ./test.sh

$ bg %1
[1]+ ./test.sh &

$ jobs -l
[1]+ 22794 Running                 ./test.sh &

但是如上方到后台执行的进程,其父进程还是当前终端shell的进程,而一旦父进程退出,则会发送hangup信号给所有子进程,子进程收到hangup以后也会退出。如果我们要在退出shell的时候继续运行进程,则需要使用nohup忽略hangup信号,或者setsid将将父进程设为init进程(进程号为1)

$ echo $$
21734

$ nohup ./test.sh &
[1] 29016

$ ps -ef | grep test
515      29710 21734 0 11:47 pts/12   00:00:00 /bin/sh ./test.sh
515      29713 21734 0 11:47 pts/12   00:00:00 grep test
$ setsid ./test.sh &
[1] 409

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515        413 21734 0 11:49 pts/12   00:00:00 grep test
上面的试验演示了使用nohup/setsid加上&使进程在后台运行,同时不受当前shell退出的影响。那么对于已经在后台运行的进程,该怎么办呢?可以使用disown命令:

$ ./test.sh &
[1] 2539

$ jobs -l
[1]+ 2539 Running                 ./test.sh &

$ disown -h %1

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515       2542 21734 0 11:52 pts/12   00:00:00 grep test
另外还有一种方法,即使将进程在一个subshell中执行,其实这和setsid异曲同工。方法很简单,将命令用括号() 括起来即可:

$ (./test.sh &)

$ ps -ef | grep test
515        410     1 0 11:49 ?        00:00:00 /bin/sh ./test.sh
515      12483 21734 0 11:59 pts/12   00:00:00 grep test
注:本文试验环境为Red Hat Enterprise Linux AS release 4 (Nahant Update 5),shell为/bin/bash,不同的OS和shell可能命令有些不一样。例如AIX的ksh,没有disown,但是可以使用nohup -p PID来获得disown同样的效果。

还有一种更加强大的方式是使用screen,首先创建一个断开模式的虚拟终端,然后用-r选项重新连接这个虚拟终端,在其中执行的任何命令,都能达到nohup的效果,这在有多个命令需要在后台连续执行的时候比较方便:

$ screen -dmS screen_test

$ screen -list
There is a screen on:
        27963.screen_test       (Detached)
1 Socket in /tmp/uscreens/S-jiangfeng.

$ screen -r screen_test

三、 nohup 
    如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户之后继续运行相应的进程。nohup就是不挂起的意思( no hang up)。 该命令的一般形式为: 
nohup conmmand &
如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:
nohup command > myout.file 2>&1 
在上面的例子中,输出被重定向到myout.file文件中。

四、.*,?,[…],[!…]等 
下面就是这些特殊字符: 
* 匹配文件名中的任何字符串,包括空字符串。 
? 匹配文件名中的任何单个字符。 
[…] 匹配[ ]中所包含的任何字符。 
[!…] 匹配[ ]中非感叹号!之后的字符。 
当s h e l l遇到上述字符时,就会把它们当作特殊字符,而不是文件名中的普通字符,这样用户就可以用它们来匹配相应的文件名。

1)列出以i或o开头的文件名:     #ls [io]*
2)列出log.开头、后面跟随一个数字、然后可以是任意字符串的文件名: #ls log.[0-9]* 
3)与例二相反,列出log.开头、后面不跟随一个数字、然后可以是任意字符串的文件名 : #ls log.[!0-9]* 
4)列出所有以LPS开头、中间可以是任何两个字符,最后以1结尾的文件名:#ls LPS??1
5)列出所有以大写字母开头的文件名:$ ls [A-Z]* 6)列出所有以. 开头的文件名(隐含文件,例如. profile、.rhosts、.histo ry等): $ ls .*

其他相关命令:

jobs:查看当前有多少在后台运行的命令
fg:将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)
bg:将一个在后台暂停的命令,变成继续执行。如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)

杀死进程

杀死已经启动的程序和普通方式一样:

pkill -9 name
killall name
kill pid
继续阅读

发表在 Tips | 留下评论

更新NAS在UPS停电后自动关机脚本

貌似有的路由器定时任务程序有bug,设置后会不起作用。比如我的RG200E刷了某个版本的番茄固件就是这个情况。

于是把脚本写成自动循环执行,这样直接加到开机脚本就OK了。

废话不多说 直接贴代码

#!/bin/sh
while
ping -c 1 192.168.2.1 > /dev/null
[ $? -eq 0 ];
do
echo ‘ AC Power OK ! ‘ >> /jffs/ups.log;date >> /jffs/ups.log
sleep 180
done
echo ‘ AC Power maybe off, checking again after 3 minutes ! ‘ >> /jffs/ups.log;date >> /jffs/ups.log
sleep 60
ping -c 1 192.168.2.1 > /dev/null
if [ $? -eq 0 ]
then
echo ‘ Checkagain, AC Power OK ! ‘ >> /jffs/ups.log;date >> /jffs/ups.log
else
echo ‘ AC Power is already off, shut down NAS Now! ‘ >> /jffs/ups.log;date >> /jffs/ups.log
halt
fi

手动在/jffs/创建ups.log文件,权限设置为0777即可。这样等于给脚本加了个日志功能,你可以查看log文件来判断脚本是否在正常运行。

继续阅读

留下评论

写了个UPS断电自动关闭NAS的脚本

终于买了UPS,这货有USB接口与电脑进行数据传输,自带电脑端的监控软件,断电自动关闭电脑,非常好。这下电脑硬盘不用担心会被突然断电搞坏了。

果断把NAS也插在UPS上了,可惜NAS是MIPS芯片,UPS官方不提供源代码,只能自己写个脚本实现断电自动关闭NAS了。

废话不多说了,脚本如下,测试OK。

#!/bin/sh

ping -c 1 192.168.2.1 > /dev/null
if [ $? -eq 0 ]
then
echo ‘ AC Power OK ! ‘
else
echo ‘ AC Power maybe off, checking again after 3 minutes ! ‘
sleep 180
ping -c 1 192.168.2.1 > /dev/null
if [ $? -eq 0 ]
then
echo ‘ AC Power OK ! ‘
else
echo ‘ AC Power off, shut down NAS Now! ‘
halt
fi
fi

脚本加入到crontab,5分钟执行一次即可.


NAS没有shutdown指令,只能用halt指令了……

继续阅读

发表在 Tips | 留下评论

Unix系列shell程序编写(下)

Until语句

  While语句中,只要某条件为真,则重复执行循环代码,until语句正好同while相反,该语句使循环代码重复执行,直到遇到某一条件为真才停止。

Until语句的结构如下: 
until command 
   do 
     command 
     command 
     … … 
   done

  可以用until语句替换上面备份程序的while语句,完成同样的功能:

until [ \$ANS != Y -a \$ANS != y ]

for 循环 
   在介绍for循环之前,我们要学个非常有用的Unix命令:shift。我们知道,对于位置变量或命令行参数,其个数必须是确定的,或者当Shell程序不知道其个数时,可以把所有参数一起赋值给变量\$*。若用户要求Shell在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在\$1后为\$2,在\$2后面为\$3等。在 shift命令执行前变量\$1的值在shift命令执行后就不可用了。

示例如下:

#测试shift命令(x_shift.sh) 
until [ \$# -eq 0 ] 
do 
echo “第一个参数为: \$1 参数个数为: \$#” 
shift 
done 
执行以上程序x_shift.sh: 
\$./x_shift.sh 1 2 3 4

结果显示如下:

第一个参数为: 1 参数个数为: 3 
第一个参数为: 2 参数个数为: 2 
第一个参数为: 3 参数个数为: 1 
第一个参数为: 4 参数个数为: 0从上可知shift命令每执行一次,变量的个数(\$#)减一,而变量值提前一位,下面代码用until和shift命令计算所有命令行参数的和。

#shift上档命令的应用(x_shift2.sh) 
if [ \$# -eq 0 ] 
then 
echo “Usage:x_shift2.sh 参数” 
exit 1 
fi 
sum=0 
until [ \$# -eq 0 ] 
do 
sum=`expr \$sum + \$1` 
shift 
done 
echo “sum is: \$sum”

执行上述程序:

\$x_shift2.sh 10 20 15

其显示结果为:

45

  shift命令还有另外一个重要用途,Bsh定义了9个位置变量,从\$1到\$9,这并不意味着用户在命令行只能使用9个参数,借助shift命令可以访问多于9个的参数。

  Shift命令一次移动参数的个数由其所带的参数指定。例如当shell程序处理完前九个命令行参数后,可以使用shift 9命令把\$10移到\$1。

  在熟悉了shift命令后,我们一起看看,Bsh程序中非常有用的for循环语句,这种循环同上面说的while和until循环不同,for语句中的循环是否执行并不由某个条件的真和假来决定,决定for循环是否继续的条件是参数表中是否还有未处理的参数。

For语句的结构如下:

for variable in arg1 arg2 … argn 
do 
command 
command 
… … 
done

下面是for循环的简单例子:

for LETTER in a b c d 
do 
echo \$LETTER 
done

程序执行结果如下:




d

在上面计算参数和的例子中,我们可以用for循环,实现如下:

#测试 for 程序(x_for.sh)

if [ \$# -eq 0 ] 
then 
    echo “Usage:x_for.sh 参数… …” 
    exit 1 
fi 
sum=0 
for I in \$* 
do 
    sum=`expr \$sum + \$I` 
done 
echo “sum is: \$sum”

中断循环指令

  在程序循环语句中,我们有时候希望遇到某中情况时候结束本次循环执行下次循环或结束这个循环,这就涉及到两条语句:continue和break。continue命令可使程序忽略其后循环体中的其他指令,直接进行下次循环,而break命令则立刻结束循环,执行循环体后面的的语句。

#测试continue 
I=1 
while [ \$I -lt 10 ] 
do 
   if [ \$I -eq 3 ] 
   then 
     continue 
   fi 
   if [ \$I -eq 7 ] 
   then 
     break 
   fi 
   echo “\$Ic” 
done

执行上面程序,结果如下:

12456789

与或结构

使用与/或结构有条件的执行命令

  Shell程序中可以使用多种不同的方法完成相同的功能,例如until和while语句就可以完成相同的功能,同样,除了if-then-else结构可以使命令有条件的执行外,\$\$和||操作符也能完成上述功能。在C语言中这两个操作符分别表示逻辑与和逻辑或操作。在Bourne Shell中,用&&连接两条命令的含义只有前面一条命令成功执行了,后面的命令才会执行。

  &&操作的形式为:

    command && command

  例如语句:

    rm \$TEMPDIR/* && echo “Files successfully removed”

  只有rm命令成功执行以后,才会执行echo命令。若用if-then语句实现上述功能,形式为:

    if rm \$TEMPDIR/* 
     then 
       echo “Files successfully removed” 
     fi 
   相反,用||连接两条命令的含义为只有第一条命令执行失败才执行第二条命令,例如:

    rm \$TEMPDIR/* || echo “File were not removed”

  上面语句的等价形式为:

    if rm \$TEMPDIR/* 
     then 
       : 
     else 
       echo “Files were not removed” 
     fi 
   这两种操作符可以联合使用,如在下面的命令行中,只有command1和command2执行成功后,command3才会执行:

    command1 && command2 && command3

  下面的命令行表示只有command1成功执行,command2不成功执行时,才会执行command3。

  &&和||操作符可以简化命令条件执行的格式,但一般只用于一条命令的条件执行。如果许多命令都使用这两个操作符,那么整个程序的可读性将变的很差,所以在多条命令的条件执行时,最好采用可读性好的if语句。

函数

  现在我们介绍Shell程序中的函数部分,基本上任何高级语言都支持函数这个东西,能让我们胜好多事情的东西,至少省的频繁的敲击相同的东西,好了come on 
Shell程序中的函数

  函数又叫做子程序,可以在程序中的任何地方被调用,其格式如下:

  函数名字() 
   { 
     command 
     … … 
     command; 
   }

  Shell程序的任何地方都可以用命令 “函数名字” 调用,使用函数的好处有两点,一点是使用函数可以把一个复杂的程序化为多个模块,易于管理,符合结构化程序的设计思想,另一个好处是代码的重用。

  Shell函数和Shel程序比较相似,它们的区别在于Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此,在当前Shell中可以看到Shell函数对变量的修改。在任何Shell中都可以定义函数,包括交互式Shell。

  例如:

    \$dir() {ls -l;}

    结果是我们在\$后面打dir,其显示结果同ls -l的作用是相同的。该dir函数将一直保留到用户从系统退出,或执行了如下所示的unset命令: 
     \$unset dir 
     下面的例子说明了函数还可以接受位置参数:

    \$dir(){_ 
     >echo “permission    ln owner   group    file sz last access 
     >ls -l \$*; 
     >}

    运行 dir a* 看产生什么结果

    参数a*传递到dir函数中并且代替了\$*

    通常Shell程序将在子Shell中执行,该程序对变量的改变只在子Shell中有效而在当前Shell中无效。”.”命令可以使Shell程序在当前Shell中执行。用户可以在当前Shell中定义函数和对变量赋值。通常用下面命令来重新初使化.profile对Shell环境的设置。 
     \$ . .profile 
   由于看到这部分相对简单,我们还是顺便说说trap好了

使用trap命令进行例外处理

  用户编写程序在程序运行时可能会发生一些例外情况,比如执行该程序的用户按中断键或使用kill命令,或者控制终端突然与系统断开等。unix系统中的上述情况会使系统向进程发一个信号,通常情况下该信号使进程终止运行。有时侯用户希望进程在接到终止信号时进行一些特殊的操作。若进程在运行时产生一些临时文件,又因接受到的信号而终止。那么该进程产生的临时文件将保留下来。在bsh中,用户可以使用trap命令修改进程接收到终止信号时进行的默认操作。 
   trap命令格式如下:

     trap command_string signals

多数系统中共有15种发给进程的信号,默认情况下大多数信号都会使程序终止。用户最好查阅自己系统的文挡,看看本系统内使用的信号种类。除了信号为9(真正的kill信号)不能使用trap命令外,其他信号所带来的操作都可以用trap命令进行指定。下面是trap命令中经常使用的几种信号:

    信号   功能 
     1     挂起 
     2    操作中断 
     15    软终止(kill信号)

  若命令串中包含不只一条命令,必须使用引号将整个命令括起来,具体是单引号还是双引号,由用户是否需要变量替换决定。” “替换,’ ‘不替换。

  使用下面trap命令可以使程序在接收到挂起、中断或kill信号时,首先把临时文件删除,然后退出:

    trap “rm \$TEMPDIR/* \$\$;exit” 1 2 15

  在上面例子中,当Shell读取trap命令时,首先对\$TEMPDIR和\$\$进行变量替换,替换之后的命令串将被保存在trap表中,若上例中trap命令使用单引号时,trap命令执行时候,不进行变量替换,而把命令串 rm \$TEMPDIR/* \$\$;exit 放到trap表中,当检测到信号时,程序解释执行trap表中的命令串,此时进行变量替换。前面变量\$TEMPDIR和\$\$的值为执行trap指令时候的值,后一种情况中变量的值为程序接收到信号时候的值,所以 “、’一定要区分仔细。

  下面命令的含义为用户按二次中断键后,程序才终止:

    trap ‘trap 2’ 2

  一般trap命令中的命令串中几乎都包含exit语句,上面rm的例子若无exit语句,接收到信号rm命令执行完后程序将挂起。但有时用户也需要程序在接到信号后挂起,例如当终端和系统断开后,用户发出挂起信号,并执行空命令,如下:

    trap : 1

  若用户想取消前trap指令设置的命令串,可以再执行trap命令,在命令中不指定命令串表示接收到信号后进行默认的操作,命令如下: 
     trap 1

规范Shell

获取UNIX类型的选项:

  unix有一个优点就是标准UNIX命令在执行时都具有相同的命令行格式:

  command -options parameters

  如果在执行Shell程序也采用上述格式,Bourne Shell中提供了一条获取和处理命令行选项的语句,即getopts语句。该语句的格式为:

  getopts option_string variable

  其中option_string中包含一个有效的单字符选项。若getopts命令在命令行中发现了连字符,那么它将用连字符后面的字符同option_string相比较。若有匹配,则把变量variable的值设为该选项。若无匹配,则variable设为?。当getopts发现连字符后面没有字符,会返回一个非零的状态值。Shell程序中可以利用getopts的返回值建立一个循环。

  下面代码说明了date命令中怎么使用getopts命令处理各种选项,该程序除了完成unix的标准命令date的功能外,还增加了许多新的选项。 
   #新date程序 
   if [ \$# -lt 1 ] 
   then 
     date 
   else 
     while getopts mdyDHMSTJjwahr OPTION 
     do 
       case \$OPTION 
       in 
         m)date ‘+%m’;; 
         d)date ‘+%d’;; 
         y)date ‘+%y’;; 
         D)date ‘+%D’;; 
         H0date ‘+%H’;; 
         M)date ‘+%M’;; 
         S)date ‘+%S’;; 
         T)date ‘+%T’;; 
         j)date ‘+%j’;; 
         J)date ‘+%y%j’;; 
         w)date ‘+%w’;; 
         a)date ‘+%a’;; 
         h)date ‘+%h’;; 
         r)date ‘+%r’;; 
         ?)echo “无效的选项!\$OPTION”;; 
       esac 
     done 
   fi 
   有时侯选项中还带一个值,getopts命令同样也支持这一功能。这时需要在option_string中选项字母后加一个冒号。当getopts命令发现冒号后,会从命令行该选项后读取该值。若该值存在,那么将被存在一个特殊的变量OPTARG中。如果该值不存在,getopts命令将在OPTARG中存放一个问号,并且在标准错误输出上显示一条消息。

  下面的例子,实现拷贝一个文件,并给文件赋一个新的名字。-c选项指定程序拷贝的次数,-v选项要求显示新创建文件的文件名。

  #–拷贝程序

  COPIES=1 
   VERBOSE=N 
   while getopts vc:OPTION 
   do 
     case \$OPTION 
     in 
       c)COPIES=\$OPTARG;; 
       v)VERBOSE=Y;; 
       ?)echo “无效参数!” 
        exit 1;; 
     esac 
   done 
   if [ \$OPTIND -gt \$# ] 
   then 
     echo “No file name specified” 
     exit 2 
   fi 
   shift ‘expr \$OPTIND – 1’ 
   FILE=\$1 
   COPY=0 
   while [ \$COPIES -gt \$COPY ] 
   do 
     COPY=’expr \$COPY + 1′ 
     cp \$FILE \$ {FILE} \$ {COPY} 
     if [ VERBOSE = Y } 
     then 
       echo \${FILE} \$ {COPY} 
     fi 
   done

规范Shell:

  我们知道环境变量PS1是提示符,看下面程序chdir: 
   if [ ! -d “\$!” ] 
   then 
     echo “\$1 is not a Directory” 
     exit 1 
   fi 
   cd \$1 
   PS1=”‘pwd’>” 
   export PS1

  我们执行:

    \$chdir /usr/ice666

  结果提示符号变成/usr/ice666>了吗?没有,为什么?

  原因在于:chdir在子Shell中执行,变量PS1的修改在当前Shell中也不会起作用,若要chdir完成意想中的功能,必须在当前Shell中执行该命令。最好的方法就是把其改成一个函数并且在.profile文件中定义。但若要把函数放到单个文件中并在当前Shell中执行,则需要使用 . 命令,并将chdir重写成一个函数,把其中的exit改写成return。下面代码是 .ice_ps的内容:

  #–提示符 
   chdir() 
   { 
   if [ !-d “\$1” ] 
   then 
     echo ” \$1 is not a directory” 
     return 
   fi 
   cd \$1 
   PS1=”‘pwd’>” 
   export PS1; 
   }

  然后我们在.profile文件中加入下面语句

  .ice_ps

  然后在切换目录的时候,我们用chdir命令,结果是什么呢,自己实验好了!   
调试Shell程序

1>调试shell程序

  用户刚编写完Shell程序中,不可避免的会有错误,这时我们可以利用Bsh中提供的跟踪选项,该选项会显示刚刚执行的命令及参数。用户可以通过set命令打开-x选项或在启动Shell使用-x选项将Shell设置成跟踪模式。例如有下面代码ice_tx:

  if [ \$# -eq 0 ] 
   then 
     echo “usage:sumints integer list” 
     exit 1 
   fi 
   sum=0 
   until [ \$# -eq 0 ] 
   do 
     sum=’expr \$sum + \$1′ 
     shift 
   done 
   echo \$sum

  我们用跟踪模式运行:

  \$sh -x ice_tx 2 3 4 
   结果显示: 
   +[ 3 -eq 0 ] 
   +sum=0 
   +[ 3 -eq 0 ] 
   +expr 0+2 
   +sum=2 
   +shift 
   +[ 2 -eq 0 ] 
   +expr 2+3 
   +sum=5 
   +shift 
   +[ 1 -eq 0 ] 
   +expr 5+4 
   +sum=9 
   +[ 0 -eq 0 ] 
   +echo 9 
   9

  从上面可以看出,跟踪模式下Shell显示执行的每一条命令以及该命令使用的变量替换后的参数值。一些控制字如if、then、until等没显示。 
2>命令分组

  Shell中若干命令可以组成一个单元一起执行。为了标识一组命令,这些命令必须放到”()”或”{}”中。放在”()”中的命令将在子Shell中运行,而放在”{}”中的命令将在当前Shell中运行。子Shell中运行的命令不影响当前Shell的变量。当前Shell中运行的命令影响当前Shell的变量。

  \$NUMBER=2 
   \$(A=2;B=2;NUMBER=’expr \$A+\$B’;echo \$NUMBER) 
   结果为:4 
   \$echo \$NUMBER 
   结果为:2 
   如果把上面的()变成{},结果会是怎么样的呢?

3>使用Shell分层管理器shl

  UNIX是一个多道程序设计的操作系统,一些UNIX系统利用这一特性提供了Shell层次管理器shl。使用shl用户一次可以打开多个层次的Shell,其中活跃的Shell可以从终端上获得输入。但所有Shell的输出都可在终端上显示,除非显示被禁止。

  多个Shell中有一个为shl,当用户在某个Shell中工作时,可以通过使用特殊字符(一般为Ctrl+z)返回shl。为了同其他Shell区别,shl中提示符为”>>>”。当用户工作在Shell层次管理器中时,可以创建、激活和删除Shell,下面是shl中使用的命令。

  create name    产生名为name的层次 
   delete name    删除名为name的层次 
   block name     禁止名为name的层次的输出 
   unblock name    恢复名为name的层次的输出 
   resume name    激活名为name的层次 
   toggle       激活近来经常使用的层次 
   name        激活名为name的层次

  layers [-l] name  对于表中的每个层次,显示其正在运行的进程的进程号,-l选项要求显示详细信息。

  help        显示shl命令的帮助信息 
   quit        退出shl以及所有被激活的层次 
总结

  在前面我们主要介绍了sh的变量、基本语法、程序设计等。如果掌握了这些内容,在学习其他UNIX下编程语言的时候,相信有一定的好处,我们说了,在大多数的UNIX中都提供Bourn Shell,而且很少有象sh这样强大的脚本编辑语言了,是系统管理员和程序员的一笔财富,并且不需要额外的软件环境,对文件等处理借助unix命令,实现起来比c实现还要简单。

  继续阅读

发表在 Linux | 留下评论

Unix系列shell程序编写(上)

*Shell是什么? 



  任何发明都具有供用户使用的界面。UNIX供用户使用的界面就是Shell(DOS的command熟悉吧,但UNIX的要强大的多)。 

Shell为用户提供了输入命令和参数并可得到命令执行结果的环境。 



  为了不同的需要,UNIX提供了不同的Shell。现在的UNIX大部分都支持BourneShell,以下教程就以BourneShell(Bsh)为例,一步步的领略UNIX 

Shell的强大功能,占先其强大魅力,达到更方便灵活的管理、应用UNIX的目的。 



  1.UNIX内核和Shell的交互方法 



  启动UNIX时,程序UNIX(内核)将被调入计算机内存,并一直保留在内存中直到机器关闭。在引导过程中,程序 

init将进入后台运行一直到机器关闭。该程序查询文件/etc/inittab,该文件列出了连接终端的各个端口及其特征。当发现一个活动的终端 时,init程序调用getty程序在终端上显示login等登陆信息。(username和passwd),在输入密码后, 

getty调用login进程,该进程根据文件/etc/passwd的内容来验证用户的身份。若用户通过身份验证,login进程 

把用户的home目录设置成当前目录并把控制交给一系列setup程序。setup程序可以是指定的应用程序,通常setup程序 

为一个Shell程序,如:/bin/sh 即Bourne Shell(command出来了,呵呵)。 



  得到控制后,Shell程序读取并执行文件/etc/.profile以及.profile。这两个文件分别建立了系统范围内的和 

该用户自己的工作环境。最后Shell显示命令提示符,如$。(这是以bsh为例,若是csh,为.cshrc,ksh为.kshrc,bash为.bashrc等等) 

    



  注不妨把/etc/.profile和.profile看成DOS的autoexec.bat 或 

config.sys文件) 



  当shell退出时,内核把控制交给init程序,该程序重新启动自动登陆过程。有两种方法使shell退出,一是用户执行exit命令,二是 

内核(例如root用kill命令)发出一个kill命令结束shell进程。shell退出后,内核回收用户及程序使用的资源。 



  用户登陆后,用户命令同计算机交互的关系为:命令进程—>Shell程序—>UNIX内核—>计算机硬件。当用户输入一个命令,如$ls, 

Shell将定位其可执行文件/bin/ls并把其传递给内核执行。内核产生一个新的子进程调用并执行/bin/ls。当程序执行完毕后,内核取消 

该子进程并把控制交给其父进程,即Shell程序。例如执行: 



    $ps 



    该命令将会列出用户正在执行的进程,即Shell程序(下来详细说说,别急现在)和ps程序。若执行: 



    $sleep 10 & 

    $ps 



  其中第一条命令将产生一个在后台执行的sleep子进程。ps命令执行时会显示出该子进程。 



  每当用户执行一条命令时,就会产生一个子进程。该子进程的执行与其父进程或Shell完全无关,这样可以使Shell去做其他工作。(Shell只是把用户的意图告诉内核,然后该干嘛干嘛) 

现在windows有个计划任务(在固定的时间,日期自动执行某任务),其实UNIX很早就有这个功能了,也就是所谓的Shell的自动执行。一些UNIX 

资源,如cron可以自动执行Shell程序而无需用户的参与,(这个功能好象在/var/spool/crotab目录里)。 



Crontab 程序对于系统管理员来说是非常有用的。Cron 

服务用于计划程序在特定时间(月、日、周、时、分)运行。我们以root的crontab 为例。根用户的 

crontab 文件放在 /var/spool/crontab/root 中,其格式如下: 



  (1)  (2)  (3)  (4)  (5)  (6) 

   0   0   *   *   3   /usr/bin/updatedb 

      1. 分钟 (0-60) 

      2. 小时 (0-23) 

      3. 日 (1-31) 

      4. 月 (1-12) 

      5. 星期 (1-7) 

      6. 所要运行的程序 

  2.Shell的功能和特点 

  1>命令行解释 

  2>使用保留字 

  3>使用Shell元字符(通配符) 

  4>可处理程序命令 

  5>使用输入输出重定向和管道 

  6>维护一些变量 

  7>运行环境控制 

  8>支持Shell编程 



  对于”命令行解释”就不多说了,就是在shell提示符(例如:”$”,”%”,”#”等)后输入一行unix命令,Shell将接收用户的输入。 



  ”使用保留字”:Shell有一些具有特殊意义的字,例如在Shell脚本中,do,done,for等字用来控制循环操作,if,then等控制条件操作。 

保留字随Shell环境的不同而不同。 



  ”通配符”:* 匹配任何位置 

       ? 匹配单个字符 

       [] 匹配的字符范围或列表 例如: 

        

          $ls [a-c]* 

          

          将列出以a-c范围内字符开头的所有文件 

          $ls [a,m,t]* 

         将列出以e,m或t开头的所有文件 



  ”程序命令” 

:当用户输入命令后,Shell读取环境变量$path(一般在用户自己的.profile中设置),该变量包含了命令可执行文件可能存在的目录列表。 

shell从这些目录中寻找命令所对应的可执行文件,然后将该文件送给内核执行。 



  ”输入输出重定向及管道” :重定向的功能同DOS的重定向功能: 



     “>” 重定向输出 

     “<” 重定向输入 



  而管道符号,是unix功能强大的一个地方,符号是一条竖线:”|”,用法: 

command 1 | command 2 他的功能是把第一个命令command 1执行的结果作为command 

2的输入传给command 2,例如: 



    $ls -s|sort -nr|pg 



  该命令列出当前目录中的所有文件,并把输出送给sort命令作为输入,sort命令按数字递减的顺序把ls的输出排序。然后把排序后的 

内容传送给pg命令,pg命令在显示器上显示sort命令排序后的内容。 



  ”维护变量” 

:Shell可以维护一些变量。变量中存放一些数据供以后使用。用户可以用”=”给变量赋值,如: 



         $lookup=/usr/mydir 



该命令建立一个名为lookup的变量并给其赋值/usr/mydir,以后用户可以在命令行中使用lookup来代替/usr/mydir,例如: 

          

         $echo $lookup 

         结果显示:/usr/mydir 



         为了使变量能被子进程使用,可用exprot命令,例如: 



         $lookup=/usr/mydir 

         $export lookup 



  ”运行环境控制” 

:当用户登陆启动shell后,shell要为用户创建一个工作的环境,如下: 



  1>当login程序激活用户shell后,将为用户建立环境变量。从/etc/profile和.profile文件中读出,在这些文件中一般都用$TERM 

变量设置终端类型,用$PATH变量设置Shell寻找可执行文件的路径。 



  2>从/etc/passwd文件或命令行启动shell时,用户可以给shell程序指定一些参数,例如”-x”,可以在命令执行前显示该命令及其参数。后面详细介绍这些参数。 



  ”shell编程” :本文主要介绍的内容。 



  shell本身也是一种语言(*可以先理解为unix命令的组合,加上类C的条件,循环等程序控制语句,类似dos批处理,但要强大的多),用户可以 

通过shell编程(脚本,文本文件),完成特定的工作。 



SHELL变量 



  下面我们详细的介绍Bourne Shell的编程: 



  自从贝尔实验室设计了Bourne 

Shell。从那时起许多厂商根据不同的硬件平台设计了许多版本得unix。但在众多版本的unix中,Bourne Shell 

一直保持一致。 

  1>Bsh的启动:用户在登陆后,系统根据文件/etc/passwd中有关该用户的信息项启动Shell。例如某用户在passwd中 

的信息项为: 



    ice_walk:!:411:103:Imsnow ,ice_walk:/home/ice_walk:/bin/bsh 



  则表明,用户名是ice_walk等信息,在最后一项”/bin/bsh”表明用户的sh环境类型是bsh,于是系统启动之。在启动或执行(包括下面我们要讲 

的shell程序–脚本)过程中可以使用以下一些参数,我们一一说明: 



  -a 将所有变量输出 

  -c “string”从string中读取命令 

  -e 使用非交互式模式 

  -f 禁止shell文件名产生 

  -h 定义 

  -i 交互式模式 

  -k 为命令的执行设置选项 

  -n 读取命令但不执行 

  -r 受限模式 

  -s 命令从标准输入读取 

  -t 执行一命令,然后退出shell 

  -u 在替换时,使用未设置的变量将会出错 

  -v 显示shell的输入行 

  -x 跟踪模式,显示执行的命令 



许多模式可以组合起来用,您可以试试了,但-ei好象不行,你说why呢? 



  使用set可以设置或取消shell的选项来改变shell环境。打开选项用”-“,关闭选项用”+”,多数unix允许打开或关闭a、f、e、h、k、n、 

u、v和x选项。若显示Shell中已经设置的选项,执行: 



    $echo $- 



  Bsh中每个用户的home目录下都有一个.profile文件,可以修改该文件来修改shell环境。为了增加一个可执行文件的路径(例如/ice_walk/bin), 

可以把下面代码加入.profile中 



    PATH=$PATH:/ice_walk/bin;exprot PATH 



   .profile中shell的环境变量意思如下: 



    CDPATH 执行cd命令时使用的搜索路径 

    HOME 用户的home目录 

    IFS 内部的域分割符,一般为空格符、制表符、或换行符 

    MAIL 指定特定文件(信箱)的路径,有UNIX邮件系统使用 

    PATH 寻找命令的搜索路径(同dos的config.sys的 path) 

    PS1 主命令提示符,默认是”$” 

    PS2 从命令提示符,默认是”>” 

    TERM 使用终端类型 



  2>Bsh里特殊字符及其含义 



  在Bsh中有一组非字母字符。这些字符的用途分为四类:作为特殊变量名、产生文件名、数据或程序控制以及引用和逃逸字符控制。他们 

可以让用户在Shell中使用最少的代码完成复杂的任务。 



     *> Shell变量名使用的特殊字符 

        $# 传送给命令Shell的参数序号 

        $- 在Shell启动或使用set命令时提供选项 

        $? 上一条命令执行后返回的值 

        $$ 当前shell的进程号 

        $! 上一个子进程的进程号 

        $@ 所有的参数,每个都用双括号括起 

        $* 所有参数,用双括号括起 

        $n 位置参数值,n表示位置 

        $0 当前shell名 

     *>产生文件名的特殊字符 

        包括”*”,”?”,”[]”,上面讲过,不再多说。 

     *>数据或程序控制使用的特殊字符 

        >(file) 输出重定向到文件中(没有文件则创建,有则覆盖) 

        >>(file) 

输出重定向到文件中(没有则创建,有则追加到文件尾部) 

        <(file) 输入重定向到文件 

        ; 命令分割符 

        | 管道符 

        & 后台运行(例如:sleep 10 &) 

        ` ` 命令替换,重定向一条命令的输出作为另一命令的参数 

     *>对于引用或逃逸的特殊字符 
继续阅读

发表在 Linux | 留下评论