Chinaunix首页 | 论坛 | 博客
  • 博客访问: 503158
  • 博文数量: 119
  • 博客积分: 5054
  • 博客等级: 大校
  • 技术积分: 1305
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-03 13:13
文章分类

全部博文(119)

文章存档

2011年(4)

2010年(115)

我的朋友

分类:

2010-01-03 22:21:52

摘自《高级bash脚本编程指南》
(注:Bash是”Bourne-Again shell”首字母的缩写,对于所有UNIX上的shell脚本来说, Bash已经成为了事实上的标准了. )

shell是什么

shell是一个命令解释器. 是介于操作系统内核与用户之间的一个绝缘层. 准确地说,它也是能力很强的计算机语言, 一种shell程序, 同时也被称为一种脚本语言. 它是非常容易使用的工具, 它可以通过将系统调用, 公共程序, 工具, 和编译过的二进制程序“粘合”在一起来建立应用. 事实上, 所有的UNIX命令和工具再加上公共程序, 对于shell脚本来说,都是可调用的. 如果这些你还觉得不够,那么shell内建命令, 比如条件测试与循环结构, 也会给脚本添加强力的支持和增加灵活性. Shell脚本对于管理系统任务和其它的重复工作的例程来说, 表现的非常好, 根本不需要那些华而不实的成熟紧凑的程序语言.

对于任何想适当精通一些系统管理知识的人来说, 掌握shell脚本知识都是最基本的, 即使这些人可能并不打算真正的编写一些脚本. 想一下Linux机器的启动过程, 在这个过程中, 必将运行/etc/rc.d目录下的脚本来存储系统配置和建立服务. 详细的理解这些启动脚本对于分析系统的行为是非常重要的, 并且有时候可能必须修改它.

学习如何编写shell脚本并不是一件很困难的事, 因为脚本可以分为很小的块, 并且相对于shell特性的操作和选项部分,只需要学习很小的一部分就可以了. 语法是简单并且直观的, 编写脚本很像是在命令行上把一些相关命令和工具连接起来, 并且只有很少的一部分”规则”需要学习. 绝大部分脚本第一次就可以正常的工作, 而且即使调试一个长一些的脚本也是很直观的.

一个shell脚本是一个类似于”小吃店的(quick and dirty)”方法, 在你使用原型设计一个复杂的应用的时候. 在工程开发的第一阶段, 即使从功能中取得很有限的一个子集放到shell脚本中来完成往往都是非常有用的. 使用这种方法, 程序的结果可以被测试和尝试运行, 并且在处理使用诸如C/C++, Java或者Perl语言编写的最终代码前, 主要的缺陷和陷阱往往就被发现了.

Shell脚本遵循典型的UNIX哲学, 就是把大的复杂的工程分成小规模的子任务, 并且把这些部件和工具组合起来. 许多人认为这种办法更好一些, 至少这种办法比使用那种高\大\全的语言更美, 更愉悦, 更适合解决问题. 比如Perl就是这种能干任何事能适合任何人的语言, 但是代价就是你需要强迫自己使用这种语言来思考解决问题的办法.

带着一个Sha-Bang出发(Sha-Bang指的是#!)

在一个最简单的例子中, 一个shell脚本其实就是将一堆系统命令列在一个文件中. 它的最基本的用处就是, 在你每次输入这些特定顺序的命令时可以少敲一些字.

例子 2-1. 清除: 清除/var/log下的log文件

  1 # 清除
  2 # 当然要使用root身份来运行这个脚本.
  3
  4 cd /var/log
  5 cat /dev/null > messages
  6 cat /dev/null > wtmp
  7 echo "Logs cleaned up."

这根本就没什么稀奇的, 只不过是命令的堆积, 来让从console或者xterm中一个一个的输入命令更方便一些. 好处就是把所有命令都放在一个脚本中,不用每次都敲它们. 这样的话, 这个脚本就成为了一个工具, 对于特定的应用来说,这个脚本就很容易被修改或定制.
例子 2-2. 清除:一个改良的清除脚本

  1 #!/bin/bash
  2 # 一个Bash脚本的正确的开头部分.
  3
  4 # Cleanup, 版本 2
  5
  6 # 当然要使用root身份来运行.
  7 # 在此处插入代码,来打印错误消息,并且在不是root身份的时候退出.
  8
  9 LOG_DIR=/var/log
 10 # 如果使用变量,当然比把代码写死的好.
 11 cd $LOG_DIR
 12
 13 cat /dev/null > messages
 14 cat /dev/null > wtmp
 15
 16
 17 echo "Logs cleaned up."
 18
 19 exit # 这个命令是一种正确并且合适的退出脚本的方法.

要注意,在每个脚本的开头都使用 sha-bang ( #!), 这意味着告诉你的系统这个文件的执行需要指定一个解释器. #! 实际上是一个2字节的魔法数字(注:那些具有UNIX味道的脚本(基于4.2BSD)需要一个4字节的魔法数字, 在!后边需要一个空格 — #! /bin/sh), 这是指定一个文件类型的特殊标记, 换句话说, 在这种情况下, 指的就是一个可执行的脚本(键入man magic来获得关于这个迷人话题的更多详细信息). 在sha-bang之后接着是一个路径名. 这个路径名就是解释脚本中命令的解释程序所在的路径, 可能是一个shell, 也可能是一个程序语言, 也可能是一个工具包中的命令程序. 这个解释程序从头开始解释并且执行脚本中的命令(从sha-bang行下边的一行开始), 忽略注释. (注:脚本中的#!所在的行的最重要的任务就是告诉系统本脚本是使用哪种命令解释器. (sh或者bash). 因为这行是以#作为行的开头, 当命令解释器执行这个脚本的时候,会把它作为一个注释行. 当然, 在这之前, 这行语句已经完成了它的任务, 就是调用命令解释器. 如果脚本中还包含有其他的#!行, 那么bash将会把它看成是一个一般的注释行.)

  1 #!/bin/sh
  2 #!/bin/bash
  3 #!/usr/bin/perl
  4 #!/usr/bin/tcl
  5 #!/bin/sed -f
  6 #!/usr/awk -f

上边每一个脚本头的行都指定了一个不同的命令解释器, 如果是/bin/sh, 那么就是默认shell (在Linux系统上默认就是bash), 否则的话就是其他解释器。 使用#!/bin/sh, 因为大多数的商业UNIX系统上都是以Bourne shell作为默认shell, 这样可以使脚本移植到non-Linux的机器上, 虽然这将会牺牲Bash一些独特的特征. 但是脚本将与POSIX(注:Portable Operating System Interface(可移植的操作系统接口), 标准化类UNIX操作系统的一种尝试.)的sh标准相一致

注意”sha-bang”后边给出的路径名必须是正确的, 否则将会出现一个错误消息 — 通常是”Command not found” — 这将是你运行这个脚本时所得到的唯一结果.

当然#!也可以被忽略, 不过这样你的脚本文件就只能是一些命令的集合, 不能够使用shell内建的指令了. 再次提醒你#!/bin/sh将会调用默认的shell解释器, 在Linux机器上默认是/bin/bash.

大多数情况下,你需要编写一个脚本来执行一个特定的任务, 在本章中第一个脚本就是一个这样的例子, 然后你会修改它来完成一个不同的, 但比较相似的任务. 使用变量来代替写死(“硬编码(hard-wired)”)的常量, 就是一个很好的习惯, 将重复的代码放到一个函数中,也是一种好习惯.

:这里可以玩一些小技巧.

  1 #!/bin/rm
  2 # 自删除脚本.
  3
  4 # 当你运行这个脚本时, 基本上什么都不会发生. . . 当然这个文件消失不见了.
  5
  6 WHATEVER=65
  7
  8 echo "This line will never print (betcha!)."
  9
 10 exit $WHATEVER  # 不要紧, 脚本是不会在这退出的.

当然,你还可以试试在一个README文件的开头加上一个#!/bin/more, 并让它具有执行权限(让它自运行,不能以 sh filename 或 bash filename 的方式运行,可能会出现意想不到的错误). 结果将是文档自动列出自己的内容.

调用一个脚本

编写完脚本之后,你可以使用 sh scriptname (小心: 使用 sh scriptname 来调用脚本的时候将会关闭一些Bash特定的扩展, 脚本可能因此而调用失败. ) ,或者 bash scriptname 来调用这个脚本. (不推荐使用sh < scriptname, 因为这禁用了脚本从stdin中读数据的功能. ) 更方便的方法是让脚本本身就具有可执行权限(上面“小技巧”中的“自删除脚本”必须以自身执行的方式运行才会有“自删除”的效果), 通过chmod命令可以修改.

比如:

chmod 555 scriptname (允许任何人都具有可读和执行权限。注:脚本需要读和可执行的权限, 因为shell需要读这个脚本. ) 

或者

    chmod +rx scriptname (允许任何人都具有可读和执行权限)
    chmod u+rx scriptname (只给脚本的所有者可读和执行权限)

既然脚本已经具有了可执行权限,现在你可以使用 ./scriptname(为什么不直接使用scriptname来调用脚本? 如果你当前的目录下($PWD) 正好是 scriptname所在的目录, 为什么它运行不了呢? 失败的原因是出于安全考虑, 当前目录并没有被加在用户的 $PATH环境变量中. 因此,在当前目录下调用脚本必须使用 ./scriptname 这种形式.)来测试这个脚本了. 如果这个脚本以一个”sha-bang”行开头, 那么脚本将会调用合适的命令解释器来运行.

最后一步, 在脚本被测试和debug之后, 你可能想把它移动到/usr/local/bin下, (当然是以root身份), 来让你的脚本对所有用户都有用. 这样以来, 用户就可以在命令行上简单的输入scriptname [ENTER]就可以运行这个脚本了.

阅读(686) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~