`

bash1

 
阅读更多
Bourne again shell (bash) 基本编程

   
 

2000 年 3 月 01 日

通过学习如何使用 bash 脚本语言编程,将使 Linux 的日常交互更有趣和有生产力,同时还可以利用那些已熟悉和喜爱的标准 UNIX 概念(如管道和重定向)。在此三部分系列中,Daniel Robbins 将以示例指导您如何用 bash 编程。他将讲述非常基本的知识(这使此系列十分适合初学者),并在后续系列中逐步引入更高级特性。
您可能要问:为什么要学习 Bash 编程?好,以下是几条令人信服的理由:



如果查看一下,可能会发现:您现在正在运行 bash。因为 bash 是标准 Linux shell,并用于各种目的,所以,即使更改了缺省 shell,bash 可能 仍 在系统中某处运行。因为 bash 已在运行,以后运行的任何 bash 脚本都天生是有效利用内存的,因为它们与任何已运行的 bash 进程共享内存。如果正在运行的工具可以胜任工作,并且做得很好,为什么还要装入一个 500K 的解释器?



 





不仅在运行 bash,实际上,您每天还在与 bash 打交道。它总在那里,因此学习如何最大限度使用它是有意义的。这样做将使您的 bash 经验更有趣和有生产力。但是为什么要学习 bash 编程 ?很简单,因为您已在考虑如何运行命令、CPing 文件以及管道化和重定向输出。为什么不学习一种语言,以便使用和利用那些已熟悉和喜爱的强大省时的概念?命令 shell 开启了 UNIX 系统的潜能,而 bash 正是 这个 Linux shell。它是您和机器之间的高级纽带。增长 bash 知识吧,这将自动提高您在 Linux 和 UNIX 中的生产力 -- 就那么简单。



 







以错误方式学习 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 中定义环境变量的标准方法是:

C代码  $ myvar='This is my environment variable!'  $ myvar='This is my environment variable!'
 


以上命令定义了一个名为 "myvar" 的环境变量,并包含字符串 "This is my environment variable!"。以上有几点注意事项:第一,在等号 "=" 的两边没有空格,任何空格将导致错误(试一下看看)。第二个件要注意的事是:虽然在定义一个字时可以省略引号,但是当定义的环境变量值多于一个字时(包含空格或制表键),引号是必须的。

第三,虽然通常可以用双引号来替代单引号,但在上例中,这样做会导致错误。为什么呢?因为使用单引号禁用了称为扩展的 bash 特性,其中,特殊字符和字符系列由值替换。例如,"!" 字符是历史扩展字符,bash 通常将其替换为前面输入的命令。(本系列文章中将不讲述历史扩展,因为它在 bash 编程中不常用。有关历史扩展的详细信息,请参阅 bash 帮助页中的“历史扩展”一节。)尽管这个类似于宏的功能很便利,但我们现在只想在环境变量后面加上一个简单的感叹号,而不是宏。

现在,让我们看一下如何实际使用环境变量。这有一个例子:

$ echo $myvar
This is my environment variable!



通过在环境变量的前面加上一个 $,可以使 bash 用 myvar 的值替换它。这在 bash 术语中叫做“变量扩展”。但是,这样做将怎样:

$ echo foo$myvarbar
foo



我们希望回显 "fooThis is my environment variable!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 的角度理解环境变量:



#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)




 





截断字符串是将初始字符串截断成较小的独立块,它是一般 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 755 mytar.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" 语句。



 







与大多数语言一样,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 脚本设计选项。再见!






您可以参阅本文在 developerWorks 全球站点上的 英文原文.


访问 GNU's bash 主页


查看 bash online reference manual


分享到:
评论

相关推荐

    RHCE bash 丢失处理

    1. **查找bash安装包**:找到合适的bash安装包。通常可以在系统安装介质的`/mnt/Server/`目录下找到。 ```shell ls /cdrom/*/bash-* ``` 2. **安装bash**:使用`rpm`工具安装bash,并指定安装位置为`/mnt/...

    AIX系统上安装BASH程序

    进入Bash环境后,再次运行`rpm -qa | grep bash`,确认输出结果中包含`bash-3.2-1`。 #### 五、总结 通过本文的介绍,读者应该已经掌握了如何在AIX系统上安装和配置Bash shell的方法。这对于从Linux环境过渡到AIX...

    Bash Quick Reference bash命令快速指南

    ### Bash Quick Reference – bash命令快速指南 #### 一、简介 本快速指南旨在为用户提供一个简洁而实用的bash命令手册,适用于版本2.02.0。它由Arnold Robbins编写,并得到了bash维护者Chet Ramey的帮助。这份手册...

    gitbash1st

    "gitbash1st"可能是一个初学者教程或者项目,旨在帮助用户入门GitBash的使用。在Java开发中,GitBash常常作为版本控制和命令行操作的首选工具,因为它支持Git的所有功能,并且能与常见的Unix/Linux命令无缝对接。 ...

    Bash参考手册.pdf

    1. Bash简介 Bash是GNU项目的一部分,是自由软件基金会(Free Software Foundation)维护的一个 Unix shell。Bash是Unix shell的免费实现,兼容 Unix shell的所有功能,并且添加了一些新的功能。 2. Shell是什么? ...

    linux bash

    1. **广泛可用性**:Bash是大多数Linux发行版的标准组件,掌握Bash意味着可以轻松管理Linux系统。 2. **易学性**:Bash的学习曲线平缓,即使是编程新手也能迅速上手。 3. **理解系统配置**:许多Linux配置文件和脚本...

    bash官方手册/Bash Reference Manual

    "bash 官方手册/Bash Reference Manual" bash 官方手册是 GNU 项目的一部分,由 Chet Ramey 和 Brian Fox 编写,提供了 Bash shell 的详细参考手册。该手册涵盖了 Bash shell 的所有方面,包括基本语法、shell ...

    bash.acp&bash.stx

    在这个场景中,我们关注的是与Bash shell相关的配置文件——"bash.acp"和"bash.stx",这些文件是专门为EditPlus定制的,目的是增强在编辑Bash脚本时的用户体验。 `bash.acp` 文件是EditPlus的语法规则配置文件,...

    AIX 6.1 bash包及安装

    在AIX 6.1中,bash-4.2-1.aix6.1.ppc是一个重要的更新,因为Bash的版本升级通常会带来性能提升、新特性和安全性改进。PPC代表PowerPC,这是IBM AIX操作系统支持的一种处理器架构。安装这个包意味着你可以享受到Bash ...

    bash 1.05 by gcc-1.40 in minix-386 第二版

    在压缩包子文件的文件名称列表中只有一个文件:“bash1”。这通常意味着压缩包中包含的是Bash shell的可执行文件,可能是经过编译后的二进制形式,用于在Minix 386环境中直接运行。文件名没有明确指出版本号,但根据...

    bash-4.4版本升级手册带安装包

    1. 首先,从可靠来源下载Bash 4.4的源代码包,这里提供的是`bash-4.4.tar.gz`。可以使用`wget`命令下载,例如: ``` wget http://example.com/bash-4.4.tar.gz ``` 2. 使用`tar`命令解压下载的文件: ``` tar ...

    Linux-101-Hacks

    Bash是Linux中最常用的shell之一,本章将详细介绍Bash的基础用法及高级技巧。 ### Sed and Awk 101 Hacks Sed和Awk是两款强大的文本处理工具,它们能够帮助用户快速处理文本文件。 ### Nagios Core Nagios Core是...

    bash高级编程 advanced bash script

    1. **Bash基础知识**:理解Bash环境,包括Shell变量、命令替换、流程控制结构(如if语句、for循环、while循环)以及函数的创建和使用。 2. **输入/输出重定向**:学习如何改变程序的默认输入和输出,包括重定向到...

    bash v203- bash的windows本地实现

    1. **Bash Shell**:Bash是Bourne-Again SHell的缩写,是GNU项目的一部分,广泛用于Linux和Unix系统中的默认shell。它提供了命令行界面,允许用户通过输入命令来执行系统操作和程序。 2. **Windows本地实现**:在...

    bash.tar.gz

    1. **变量**:Bash 允许定义和使用变量来存储数据。 2. **命令替换**:通过反引号 ` 或 $( ) 将命令的输出作为字符串使用。 3. **重定向**:&gt;``, `&gt;&gt;`, `分别用于覆盖输出、追加输出和读取输入。 4. **条件表达式**...

    Bash使用 文档.doc

    Bash 使用文档 bash 是一种广泛使用的 shellcript 语言,主要应用于 Linux 操作系统中。下面是 bash 的一些重要知识点: 一、什么是 shell shell 是 Linux 系统中,用户和内核之间的交互程序。它翻译用户输入的...

    修复bash漏洞的bash-4.4.tar.gz源码包

    linux 服务器GNU Bash小于版本4.3有操作系统命令注入漏洞,需要对bash升级,下载解压 #tar zxvf bash-4.4.tar.gz #cd bash-4.4 #./configure (如果centos7编译失败,请先安装#yum install gcc) #make #make ...

    高级bash shell手册

    **高级Bash Shell手册** Bash(Bourne-Again SHell)是Unix/Linux系统中最常用的命令行解释器,它提供了丰富的功能,使得用户能够高效地进行系统管理和自动化任务执行。高级Bash Shell手册是一本深入讲解Bash特性和...

    详细的Bash介绍

    Bash中有许多特殊变量,比如`$0`表示脚本名称,`$1`到`$9`表示脚本接收到的参数,`$#`表示参数的总数,`$$`表示当前进程ID等。 4. **环境变量** 环境变量是全局的,对所有子进程可见。可以通过`export`命令来设置...

    Bash Reference Manual.pdf --- Bash手册

    1. Shell 语法:Bash 的语法类似于 C 语言,提供了多种控制结构和操作符。了解这些基本语法对于编写有效的脚本至关重要。 2. 引用:通过使用不同的引用方式,可以控制文本字符串的解析和输出。例如,单引号表示字符...

Global site tag (gtag.js) - Google Analytics