1) 命令行使用技巧
一个重要的增强功能是 shell提供的命令行编辑功能,这包括能够使用上次执行过的指令(命令回溯),对已经输入的部分进行编辑等等。
注意: shell的这些增强功能是在最新版本(4.2之后)的系统中才加以添加的,因此不再需要额外安装tcsh或bash。新版的FreeBSD使用tcsh作为基本的csh,而sh的功能也得到了增强。如果是老版本的系统,则需要安装tcsh作为shell,或者安装bash,来获得这些增强功能。
然而,最新的 sh缺省情况下并没有打开这个功能,因此必须首先打开这个功能,当打开这个功能之后,就能使用方向键来回溯执行过的命令,并进行修改。
$ set -o emacs
$ set -o vi
按照习惯的不同,可以选择 vi风格的编辑方式或emacs风格的方式,而vi风格还能支持vi的键盘指令,即在按下Esc键之后,使用h、j、k、l四个键来移动光标和回溯命令,它的好处是不使用基本键盘之外的控制键,因而可以适用于任何终端设备,并且进行编辑时手不需离开基本键盘,熟练操作之后最为快捷。
最新版本的 csh,即tcsh,缺省就支持命令回溯,但也能使用不同的命令切换编辑风格:
% bind emacs
% bind vi
另一个重要的功能是可以为常用的命令设置别名,简化用户输入,例如:
$ alias ec= “ echo This is a alias ”
$ ec
This is a alias
csh用于方便用户操作的另一项能力是自动补全命令或文件名的功能,因为FreeBSD下的文件名可能很长,将它们全部输入比较麻烦。事实上可以输入部分名字,然后按Tab键(在vi风格下是连续两次按Esc键),shell将自动补全文件名的剩余部分。如果已经输入的这部分名字不能确定具体的命令或文件,那么shell只将能确定的部分补上,然后响铃通知使用者继续输入以明确具体的文件。
事实上即使在基本的 sh或csh下,也可以使用 “ * ” 等特殊字符,用模式匹配的方式来简化输入。
$ cd /usr/loca*
$ pwd
/usr/local
Unix中的多数程序都具备模式匹配的处理能力,而shell的模式匹配功能最为常用。shell可以使用这些特殊模式来配置多个文件,达到简化操作的目的。如果要熟练掌握Unix,必须掌握模式匹配。
2) 控制功能
Unix的shell不仅仅简单的接受输入指令并执行,它更强大的能力是能够根据条件解释执行输入指令。当然,sh和csh对于输入的解释语法有所不同,由于在系统中sh最为基本,以下以sh为例,简单介绍shell的控制功能。
最重要的功能之一是根据条件来判断是否需要完成某项工作。最简单的情况下,如果某个程序存在并可以执行,则执行执行这个程序,这种用法在启动脚本中十分常见。这需要使用逻辑“与”判断:
$ [ -x /usr/bin/echo ] && /usr/bin/echo “ the program echo is running! ”
the program echo is running!
与此相反的方式是,当条件不成立的时候执行程序,这需要逻辑“或”判断:
$ [ -f /tmp/somefiles ] || echo “ somefiles is missing! ”
somefiles is missing!
当然,无论哪一种逻辑判断形式,关键点是逻辑判断本身的语法。一般来讲,可以根据文件本身的属性进行判断,如上面例子中判断文件 ” -f ” 是否存在, ” -x ” 判断文件是否是执行程序,还可以判断是否为目录 ” -d ” ,等等。此外,还可以根据字符串来进行判断,判断两个字符串是否相同、甚至比较大小,以及进行数学判断等等,下面将在介绍其他控制方式时给出一些例子。
更详细的判断条件,可以 man test。事实上,判断语句中的括号[本身就是一个程序,就是test,这个程序根据后面的条件返回一定的结果。因此,完全可以直接根据一个程序的返回结果进行判断。
使用逻辑与和逻辑或进行逻辑判断,是逻辑判断的一种缩略形式,它的好处是能将几个命令放在一行中。更为标准的方法当然还是使用 if判断语句。
$ if [ -d /home/user ] ; then echo “ user directory is exist! ” fi
user directory is exist!
此时由于有控制部分,将所有语句都写在一行上显然不是好主意, sh允许将这样一个语句分开完成的机制。
$ if [ ! -d /home/user1 ]
> then
> echo “ user1 directory is not exist! ”
> fi
注意, ” > “ 为 shell的提示符号而不是语句本身,当语句还没有完成的时候,sh使用提示符 ” > “ ,而不是标准的 ” $ ” ,这个提示符是由环境变量 PS2决定的。这个语句中增加了逻辑非的判断。
除了根据文件的属性进行判断之后,更多的情况下需要判断字符串是否相同:
引用:$ if [ “ X$TERM ” = “ Xansi ” ]
> then
> echo “ ansi terminal is not supported in FreeBSD! ”
> elif [ “ X$TERM ” > “ Xvt0 ” && “ X$TERM ” < “ Xvt9 ” ]
> then
> echo “ vtXXX serial terminal is ok! ”
> else
> echo “ terminal maybe ok! ”
> fi
上面例子中使用了 if,elif,else等多重判断形式,甚至还判断了字符串的大小,判断$TERM是否是vt系列的终端。
这里应用了一个小技巧,就是不直接判断 $TERM本身,而是添加上一个额外的字符 ' X ' ,这是因为 $TERM可能为空变量,这种情况下判断语句本身就会出现语法错误,添加上一个额外的字符就可以避免这个问题。
当然,如果是判断结果可能是多个字符串之一,那么就需要使用 case语句:
引用:$ case “ $gateway_enable ” in
> [Yy][Ee][Ss])
> echo “ $gateway is yes ”
> ;;
> *)
> ;;
> esac
这种形式在系统启动脚本中也非常常见,注意,这里使用了中括号来忽略变量中的大小写,事实上这是一种模式匹配的方法,方括号内的任一字符都匹配对应字符。
了解了逻辑判断语句之后,循环语句也非常自然了,因为循环原则上也是一个逻辑判断,加上循环的主体而已。
$ COUNT=1; export COUNT
$ while [ $COUNT -lt 10 ]
> do
> echo “ count is $COUNT ”
> COUNT=`expr $COUNT + 1`
> done
这个例子中,我们首先设置了一个环境变量,然后在这个变量小于 10的时候执行循环。当然,循环内部使用了反引号,调用外部程序expr,将该变量自动加一。这就是shell的一个弱点,处理数学计算的时候必须使用外部程序,shell在处理字符串的时候相对简单,有很多内部功能可以利用,比如直接将两个变量放在一起,就能完成字符串的合并任务。
如果循环次数比较少,也可以使用这种字符串判断的方法:
$ while [ “ X$loopflag ” < “ X1111111111 ” ]
> do
> echo “ flag is $loopflag ”
> loopflag=${loopflag}1
> done
这里每个循环向循环变量后面附加上一个字符,直到循环变量大于某个值为止。
其实,另一种循环方式更为直接,也更方便,就是使用 for语句。
$ for COUNT in 0 1 2 3 4 5 6 7 8 9
> do
> echo “ count is $COUNT ”
> done
通过枚举的方式把所有循环可能性列出,更为简单、可靠,而不需要任何其他的外部程序。 这种方法的缺点是如果循环的数量较多就比较麻烦了,如果是这种情况,最好是使用多级循环的方式来解决。
其实 for循环后面的枚举完全可以和sh的其他功能组合起来,充分发挥功能。
$ for cfile in *c
> do
> echo “ in current directory, there is $cfile ”
> done
上例可以对当前目录下的所有 c文件进行处理,这就是sh自动将*c这个带有模式的输入自动展开为所有匹配的文件名的列表。
$ for cfile in `ls`
> do
> echo “ in current directory, there is $cfile ”
> done
这是另一种形式,使用反引号,让 sh启动ls命令,而ls命令的结果作为for的参数。