日期:2006-10-25 作者:Daniel Robbins 来自:IBM DW 中国
您可能要问:为什么要学习 Bash 编程?好,以下是几条令人信服的理由:
曾经在运行它
如果查看一下,可能会发现:您当初正在运行 bash。因为 bash 是规范 Linux shell,并用于各种目标,所以,即便更改了缺省 shell,bash 可能 仍 在零碎中某处运行。因为 bash 已在运行,当前运行的任何 bash 脚本都天生是无效利用内存的,因为它们与任何已运行的 bash 过程共享内存。如果正在运行的工具能够胜任工作,并且做得很好,为什么还要装入一个 500K 的解释器?
曾经在应用它
不仅在运行 bash,实际上,您每天还在与 bash 打交道。它总在那里,因而学习如何最大限度应用它是有意义的。这样做将使您的 bash 教训更乏味和有生产力。然而为什么要学习 bash 编程 ?很简略,因为您已在思考如何运行命令、CPing 文件以及管道化和重定向输入。为什么不学习一种语言,以便应用和利用那些已相熟和青睐的弱小省时的概念?命令 shell 开启了 UNIX 零碎的潜能,而 bash 正是 这个 Linuxshell。它是您和机器之间的高级纽带。增长 bash 常识吧,这将主动进步您在 Linux 和 UNIX 中的生产力 – 就那么简略。
Bash 困惑
以谬误形式学习 bash 令人非常困惑。许多老手输出 “man bash” 来查看 bash 帮忙页,但只失去非常简单和技术方面的 shell 功能性形容。还有人输出 “info bash”(来查看 GNU 信息文档),只能失去从新显示的帮忙页,或者(如果侥幸)略为敌对的信息文档。
只管这可能使初学者有些悲观,但规范 bash 文档无奈满足所有人的要求,它只适宜那些已大体相熟 shell 编程的人。帮忙页中的确有很多极好的技术信息,但对初学者的帮忙却无限。
这就是本系列的目标所在。在本系列中,我将讲述如何理论应用 bash 编程概念,以便编写本人的脚本。与技术形容不同,我将以简略的语言为您解释,使您不仅晓得事件做什么,还晓得应在何时应用。在此三局部系列开端,您将能够本人编写简单的 bash 脚本,并能够自若地应用 bash 以及通过浏览(和了解)规范 bash 文档来补充常识。让咱们开始吧。
环境变量
在 bash 和简直所有其它 shell 中,用户能够定义环境变量,这些环境变量在以 ASCII 字符串存储。环境变量的最便当之处在于:它们是 UNIX 过程模型的规范局部。这意味着:环境变量不仅由 shell 脚本独用,而且还能够由编译过的规范程序应用。当在 bash 中“导出”环境变量时,当前运行的任何程序,不论是不是 shell 脚本,都能够读取设置。一个很好的例子是 vipw 命令,它通常容许 root 用户编辑零碎口令文件。通过将 EDITOR 环境变量设置成青睐的文本编辑器名称,能够配置 vipw,使其应用该编辑器,而不应用 vi,如果习惯于 xemacs 而的确不喜爱 vi,那么这是很便当的。
在 bash 中定义环境变量的规范办法是:
$ myvar='This is my environment variable!'
以上命令定义了一个名为 “myvar” 的环境变量,并蕴含字符串 “This ismy environment variable!”。以上有几点注意事项:第一,在等号 “=” 的两边没有空格,任何空格将导致谬误(试一下看看)。第二个件要留神的事是:尽管在定义一个字时能够省略引号,然而当定义的环境变量值多于一个字时(蕴含空格或制表键),引号是必须的。
援用细节
无关如何在 bash 中应用引号的十分详尽的信息,请参阅 bash 帮忙页面中的“援用”一节。特殊字符序列由其它值“扩大”(替换)的确使 bash 中字符串的解决变得复杂。本系列将只讲述最罕用的援用性能。
第三,尽管通常能够用双引号来代替单引号,但在上例中,这样做会导致谬误。为什么呢?因为应用单引号禁用了称为扩大的 bash 个性,其中,特殊字符和字符系列由值替换。例如,”!” 字符是历史扩大字符,bash 通常将其替换为后面输出的命令。(本系列文章中将不讲述历史扩大,因为它在 bash 编程中不罕用。无关历史扩大的详细信息,请参阅 bash 帮忙页中的“历史扩大”一节。)只管这个相似于宏的性能很便当,但咱们当初只想在环境变量前面加上一个简略的感叹号,而不是宏。
当初,让咱们看一下如何理论应用环境变量。这有一个例子:
$ echo $myvar
This is my environment variable!
通过在环境变量的后面加上一个 $,能够使 bash 用 myvar 的值替换它。这在 bash 术语中叫做“变量扩大”。然而,这样做将怎么:
$ echo foo$myvarbar
foo
咱们心愿回显 “fooThis is my environmentvariable!bar”,但却不是这样。错在哪里?简略地说,bash 变量扩大设施陷入了困惑。它无奈辨认要扩大哪一个变量:$m、$my、$myvar、$myvarbar 等等。如何更明确分明地告述 bash 援用哪一个变量?试一下这个:
$ echo foo${myvar}bar
fooThis is my environment variable!bar
如您所见,当环境变量没有与四周文本显著离开时,能够用花括号将它括起。尽管 $myvar 能够更快输出,并且在大多数状况下正确工作,但 ${myvar}却能在简直所有状况下正确通过语法分析。除此之外,二者雷同,将在本系列的余下局部看到变量扩大的两种模式。请记住:当环境变量没有用空白(空格或制表键)与四周文本离开时,请应用更明确的花括号模式。
回忆一下,咱们还提到过能够“导出”变量。当导出环境变量时,它能够主动地由当前运行的任何脚本或可执行程序环境应用。shell 脚本能够应用 shell 的内置环境变量反对“达到”环境变量,而 C 程序能够应用 getenv() 函数调用。这里有一些 C 代码示例,输出并编译它们 — 它将帮忙咱们从 C 的角度了解环境变量:
myvar.c — 样本环境变量 C 程序
#include <stdio.h>
#include <stdlib.h>
int main(void) {char *myenvvar=getenv("EDITOR");
printf("The editor environment variable is set to %s\n",myenvvar);
}
将下面的代码保留到文件 myenv.c 中,而后收回以下命令进行编译:
$ gcc myenv.c -o myenv
当初,目录中将有一个可执行程序,它在运行时将打印 EDITOR 环境变量的值(如果有值的话)。这是在我机器上运行时的状况:
$ ./myenv
The editor environment variable is set to (null)
啊 … 因为没有将 EDITOR 环境变量设置成任何值,所以 C 程序失去一个空字符串。让咱们试着将它设置成特定值:
$ EDITOR=xemacs
$ ./myenv
The editor environment variable is set to (null)
尽管心愿 myenv 打印值 ”xemacs”,然而因为还没有导出环境变量,所以它却没有很好地工作。这次让它正确工作:
$ export EDITOR
$ ./myenv
The editor environment variable is set to xemacs
当初,如您亲眼所见:不导出环境变量,另一个过程(在本例中是示例 C 程序)就看不到环境变量。顺便提一句,如果违心,能够在一行定义并导出环境变量,如下所示:
$ export EDITOR=xemacs
这与两行版本的成果雷同。当初该演示如何应用 unset 来除去环境变量:
$ unset EDITOR
$ ./myenv
The editor environment variable is set to (null)
dirname 和 basename
请留神:dirname 和 basename 不是磁盘上的文件或目录,它们只是字符串操作命令。
截断字符串概述
截断字符串是将初始字符串截断成较小的独立块,它是个别 shell 脚本每天执行的工作之一。很多时候,shell 脚本须要采纳全限定门路,并找到完结的文件或目录。尽管能够用 bash 编码实现(而且乏味),但规范 basename UNIX 可执行程序能够极好地实现此工作:
$ basename /usr/local/share/doc/foo/foo.txt
foo.txt
$ basename /usr/home/drobbins
drobbins
Basename 是一个截断字符串的极简便工具。它的相干命令 dirname 返回 basename 抛弃的“另”一部分门路。
$ dirname /usr/local/share/doc/foo/foo.txt
/usr/local/share/doc/foo
$ dirname /usr/home/drobbins/
/usr/home
命令替换
须要晓得一个简便操作:如何创立一个蕴含可执行命令后果的环境变量。这很容易:
$ MYDIR=`dirname /usr/local/share/doc/foo/foo.txt`
$ echo $MYDIR
/usr/local/share/doc/foo
下面所做的称为“命令替换”。此例中有几点须要指出。在第一行,简略地将要执行的命令以 反引号 括起。那不是规范的单引号,而是键盘中通常位于 Tab 键之上的单引号。能够用 bash 备用命令替换语法来做同样的事:
$ MYDIR=$(dirname /usr/local/share/doc/foo/foo.txt)
$ echo $MYDIR
/usr/local/share/doc/foo
如您所见,bash 提供多种办法来执行齐全一样的操作。应用命令替换能够将任何命令或命令管道放在
或 $() 之间,并将其调配给环境变量。真不便!上面是一个例子,演示如何在命令替换中应用管道:
MYFILES=$(ls /etc | grep pa)
bash-2.03$ echo $MYFILES
pam.d passwd
象业余人员那样截断字符串
只管 basename 和 dirname 是很好的工具,但有时可能须要执行更高级的字符串“截断”,而不只是规范的路径名操作。当须要更强的说服力时,能够利用 bash 内置的变量扩大性能。曾经应用了相似于 ${MYVAR}的规范类型的变量扩大。然而 bash 本身也能够执行一些便当的字符串截断。看一下这些例子:
$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg
在第一个例子中,输出了 ${MYVAR##*fo}。它的确切含意是什么?基本上,在 ${}中输出环境变量名称,两个 ##,而后是通配符 (“fo”)。而后,bash 获得 MYVAR,找到从字符串 “foodforthought.jpg” 开始处开始、且匹配通配符 ”fo” 的 最长 子字符串,而后将其从字符串的开始处截去。刚开始了解时会有些艰难,为了感受一下这个非凡的 ”##” 选项如何工作,让咱们一步步地看看 bash 如何实现这个扩大。首先,它从 “foodforthought.jpg” 的开始处搜寻与 ”*fo” 通配符匹配的子字符串。以下是查看到的子字符串:
f
fo MATCHES *fo
foo
food
foodf
foodfo MATCHES *fo
foodfor
foodfort
foodforth
foodfortho
foodforthou
foodforthoug
foodforthought
foodforthought.j
foodforthought.jp
foodforthought.jpg
在搜寻了匹配的字符串之后,能够看到 bash 找到两个匹配。它抉择最长的匹配,从初始字符串的开始处除去,而后返回后果。
下面所示的第二个变量扩大模式看起来与第一个雷同,然而它只应用一个 ”#” — 并且 bash 执行 简直 同样的过程。它查看与第一个例子雷同的子字符串系列,然而 bash 从初始字符串除去 最短 的匹配,而后返回后果。所以,一查到 ”fo” 子字符串,它就从字符串中除去 “fo”,而后返回 ”odforthought.jpg”。
这样说可能会令人非常困惑,上面以一简略形式记住这个性能。当搜寻最长匹配时,应用 ##(因为 ## 比 # 长)。当搜寻最短匹配时,应用 #。看,不难记吧!等一下,怎么记住应该应用 ‘#’ 字符来从字符串开始局部除去?很简略!留神到了吗:在美国键盘上,shift- 4 是 “$”,它是 bash 变量扩大字符。在键盘上,紧靠 “$” 右边的是 ”#”。这样,能够看到:”#” 位于 “$” 的“开始处”,因而(依据咱们的记忆法),”#” 从字符串的开始处除去字符。您可能要问:如何从字符串开端除去字符。如果猜到咱们应用美国键盘上紧靠 ”$” 左边 的字符(“%),那就猜对了。这里有一些简略的例子,解释如何截去字符串的开端局部:
$ MYFOO="chickensoup.tar.gz"
$ echo ${MYFOO%%.*}
chickensoup
$ echo ${MYFOO%.*}
chickensoup.tar
正如您所见,除了将匹配通配符从字符串开端除去之外,% 和 %% 变量扩大选项与 # 和 ## 的工作形式雷同。请留神:如果要从开端除去特定子字符串,不用应用 “*” 字符:
MYFOOD="chickensoup"
$ echo ${MYFOOD%%soup}
chicken
在此例中,应用 “%%” 或 “%” 并不重要,因为只能有一个匹配。还要记住:如果遗记了应该应用 “#” 还是 ”%”,则看一下键盘上的 3、4 和 5 键,而后猜出来。
能够依据特定字符偏移和长度,应用另一种模式的变量扩大,来抉择特定子字符串。试着在 bash 中输出以下行:
$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga
这种模式的字符串截断十分简便,只需用冒号离开来指定起始字符和子字符串长度。
利用字符串截断
当初咱们曾经学习了所有截断字符串的常识,上面写一个简略短小的 shell 脚本。咱们的脚本将承受一个文件作为自变量,而后打印:该文件是否是一个 tar 文件。要确定它是否是 tar 文件,将在文件开端查找模式 ”.tar”。如下所示:
mytar.sh — 一个简略的脚本
#!/bin/bash
if ["${1##*.}" = "tar" ]
then
echo This appears to be a tarball.
else
echo At first glance, this does not appear to be a tarball.
fi
要运行此脚本,将它输出到文件 mytar.sh 中,而后输出 “chmod 755mytar.sh”,生成可执行文件。而后,如下做一下 tar 文件试验:
$ ./mytar.sh thisfile.tar
This appears to be a tarball.
$ ./mytar.sh thatfile.gz
At first glance, this does not appear to be a tarball.
好,胜利运行,然而不太实用。在使它更实用之前,先看一下下面应用的 ”if” 语句。语句中应用了一个布尔表达式。在 bash 中,”=” 比拟运算符查看字符串是否相等。在 bash 中,所有布尔表达式都用方括号括起。然而布尔表达式实际上测试什么?让咱们看一下右边。依据后面所学的字符串截断常识,”${1##.}” 将从环境变量 “1” 蕴含的字符串开始局部除去最长的 “.” 匹配,并返回后果。这将返回文件中最初一个 “.” 之后的所有局部。显然,如果文件以 “.tar” 完结,后果将是 ”tar”,条件也为真。
您可能会想:开始处的 “1” 环境变量是什么。很简略 — $1 是传给脚本的第一个命令行自变量,$2 是第二个,以此类推。好,曾经回顾了性能,上面来初探 “if” 语句。
If 语句
与大多数语言一样,bash 有本人的条件模式。在应用时,要遵循以上格局;即,将 “if” 和 “then” 放在不同行,并使 “else” 和完结处必须的 “fi” 与它们程度对齐。这将使代码易于浏览和调试。除了 “if,else” 模式之外,还有其它模式的 “if” 语句:
if [condition]
then
action
fi
只有当 condition
为真时,该语句才执行操作,否则不执行操作,并继续执行 “fi” 之后的任何行。
if [condition]
then
action
elif [condition2]
then
action2
.
.
.
elif [condition3]
then
else
actionx
fi
以上 “elif” 模式将间断测试每个条件,并执行合乎第一个 真 条件的操作。如果没有条件为真,则将执行 ”else” 操作,如果有一个条件为真,则继续执行整个 “if,elif,else” 语句之后的行。
下一次
咱们曾经学习了最根本的 bash 性能,当初要加快脚步,筹备编写一些理论脚本。在下一篇中,将讲述循环概念、函数、名称空间和其它重要主题。而后,将筹备好编写一些更简单的脚本。在第三篇中,将重点讲述一些非常复杂的脚本和性能,以及几个 bash 脚本设计选项。再见!
原文链接:http://www.ibm.com/developerw…