闇の光

读书笔记 经验感受

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Commands(1)

在shell脚本中,你可以运行两类命令:一种是命令提示符运行的普通命令,也叫外部命令;还有一种是内置命令,也叫内部命令。内置命令是在shell的内部实现,所以不能像外部命令那样被调用。不过,大多数内部命令都可以作为独立程序被提供,而这也是POSIX规范要求的一部分。通常,命令都无所谓外部的还是内部的,除非内部命令的运行效果更高效。

在此,我们主要了解主要的几个命令,既有外部命令又有内部命令。

break

break用来在for、while或者until循环控制条件满足前跳出该循环。我们可以给break一个附加的数值参数用来跳出循环的次数,不过这会使得脚本变的难以阅读,因此不建议这样使用。默认状况下,break只能逃出一次循环级别。

示例:
#!/bin/sh

rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4

for file in fred*
do
   if [ -d "$file" ]; then
       break
   fi
done

echo first directory starting fred was $file

rm -rf fred*
exit 0

The : Command

冒号命令是一个空命令。它偶尔用来简化条件的逻辑,相当于true的别名。由于它是内置命令,所以:比true运行要快的多,即使它的输出相对而言更难以阅读一些。我们可能会碰到它作为while循环的一个条件出现(如: while :),作为一个无限循环来代替更普遍的while true。另外,冒号命令同样运用于变量的设定上,比如:

: ${var:=value}
如果没有:,shell程序将为$var赋一个值。

在一些比较旧的shell脚本里,我们可能还会看到冒号命令用在一行的开头,用来标注注解。不过在现代shell脚本里,通常是使用#来开始一段注解,毕竟这样运行起来更高效一些。

示例:
#!/bin/sh

rm -rf fred
if [ -f fred ]; then
   :
else
   echo file fred did not exist
fi

exit 0

continue

同C语言中continue一样,此命令的作用就是在for,whie或者until循环的下一次迭代继续运行。举例如下所示:

#!/bin/sh

rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4

for file in fred*
do
    if [ -d "$file" ]; then
        echo "skipping directory $file"
        continue
    fi
done

rm -rf fred*
exit 0

另外,continue可以将得到的循环号码作为一个可选择的参数而重新开始,从而跳出嵌套的循环。这种方法很少使用,因为它使得shell脚本变得难以理解。举例如下:

for x in 1 2 3
do
    echo before $x
    continue 1
    echo after $x
done
结果显示如下:
before 1
before 2
before 3

The . Command

点命令可以使得命令运行在当前的shell下,如:

$ . ./shell_script

通常情况下,当我们运行一个外部命令或者脚本时,都会重新生成一个新的环境(子shell),之后命令就在这个新环境中运行,直到命令运行完毕之后这个新的环境就自动分离遗弃。而通过点命令我们就可以在当前shell环境中运行脚本和命令,这样做有什么用处呢?原来在通常情况下,shell脚本在新生成的环境中运行对环境变量所做的改变都将被遗弃,而用了点命令之后,我们就可以运行shell脚本来改变当前的环境。这是很有用的,比如说你可以通过脚本来为后面的命令运行设置想要的环境。我们可以这样想,在shell脚本中,点命令就类似于C或者C++中的#include一样。举个例子说明,假设你有两个文件,它们用来为两个不同的开发环境设置系统环境。一个用来设置旧的环境,文件名为:classic_set;另一个用来设置新的环境,文件名为:latest_set。文件内容分别如下所示:

classic_set
#!/bin/sh

version=classic
PATH=/usr/local/old_bin:/usr/bin:/bin:.
PS1="classic> "
latest_set
#!/bin/sh

version=lateset
PATH=/usr/local/new_bin:/usr/bin:/bin:.
PS1="latest version> "
运行状况如下:
$ . ./classic_set
classic> echo $version
classic
classic> ./latest_set
latest version> echo $version
latest
lastest version>

echo

尽管X/Open提倡大家在shell中使用printf这个命令,不过在linux里面基本上用来在下一行输出一条字符串的命令,非echo莫属。在不同的unix版本里面有不同的方法来实现在当前输出字符串而产生新行,而linux下的实现方法是:
echo -n "string to output"
不过你也可能碰到这样的写法:
echo -e "string to output\c"

在第二种方法的选项echo -e,它的功能就是解释\后面的转义字符,比如\c是删除新行,\t是输出为tab键。在旧的bash版本中,这是默认的设置,不过在一些新的版本中,解释转义字符的功能不再是默认设置。因此,具体情况请看你所安装的系统自带的manual详情。

注:如果你要使得移除新行的功能可移植的话,可以使用外部命令tr来实现,不过执行的速度就稍微慢一些。如果你是要移植到Unix系统上面,使用printf会更好一些。如果你只是工作在linux和bash上面的话,echo -n是你最好的选择,最好就是在脚本的第一行就以#!/bin/bash开始,以确保脚本是运行于bash模式下。

eval

eval的功能是用来给参数重新赋值。它是shell的内建命令,并且作为一个独立命令它都是非常规的存在。下面是来自于X/Open规范的一段小示例:

foo=10
x=foo
y='$'$x
echo $y
输出为:$foo。而如果是这样,
foo=10
x=foo
eval y='$'$x
echo $y
输出为:10。因此,eval就像一个附加的$:一样,让我们得到变量的值的值。

exec

exec有两种用法。典型的用法是用不同的程序来代替当前的shell。例如:

exec wall "Thanks for all the fish"
在脚本里面,wall命令将代替当前的shell。如果exec后面没有任何语句,将直接通过,因为当脚本运行过后,新生成的shell环境就不再存在。

第二种用法是使用exec来改变当前的文件描述符:

exec 3< afile
这将使得在读取文件afile时文件描述符为3,这种方法很少使用。

exit n

exit命令使得脚本通过exit code来退出。如果你使用它在任何一种人机交互shell会话中,它将使得你退出该shell会话。如果我们在脚本退出时不指定一个退出状态的话,那么脚本中最后一个指令的退出状态将作为返回值被使用。通常指定返回值是一个很好的做法。
在Shell编程中,返回代码0为成功,1到125是错误代码,这些错误代码可以为脚本所使用。保留值所具有的含义:

Exit Code描述
126文件为非可执行的
127命令没有找到
128及以上发生信号

使用零来作为运行成功的状态,对多数C或者C++程序员来说,也许看起来有些与众不同。不过,它最大的好处就是能够让我们使用125种用户定义的错误代码而不需要一个全局错误代码变量。

下面是一个简单的示例,如果当前文件下存在.profile文件则返回成功:

#!/bin/sh

if [ -f .profile ]; then
    exit 0
fi

exit 1

如果你是一个甘受折磨或者需要简洁的脚本的人,可以通过AND和OR列表来重写我们的脚本,而将所有的内容放在一行:

[ -f .profile ] && exit 0 || exit 1
posted on 2008-03-10 13:35  taizi  阅读(288)  评论(0编辑  收藏  举报