`
keep
  • 浏览: 102956 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Bash 实例,第一部分

阅读更多



Bourne again shell (bash) 基本编程
developerWorks



级别: 初级

Daniel Robbins (drobbins@gentoo.org), 总裁兼 CEO, Gentoo Technologies, Inc.

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 困惑

以错误方式学习 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 is my 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 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 的角度理解环境变量:

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


这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。

 

分享到:
评论

相关推荐

    Linux高级bash编程

    第一部分. 热身 1. 为什么使用shell编程 2. 带着一个Sha-Bang出发(Sha-Bang指的是#!) 2.1. 调用一个脚本 2.2. 初步的练习 第二部分. 基本 3. 特殊字符 4. 变量和参数的介绍 4.1. 变量替换 4.2. 变量...

    Advanced Bash-Scripting Guide <>

    第一部分. 热身 1. 为什么使用shell 编程 2. 带着一个Sha-Bang 出发(Sha-Bang 指的是#!) 2.1. 调用一个脚本 2.2. 初步的练习 第二部分. 基本 3. 特殊字符 4. 变量和参数的介绍 4.1. 变量替换 4.2. 变量赋值 4.3. ...

    shell基础编程实例

    大量的shell实例,前面都是非常之基础的,例子肯定够你需求,后面的相对加深,是对前面的实际应用,有300来页,是我在本网找不到而在网上找来的资源,下面只给出第一页例子 Example 2-1 清除:清除/var/log下的log...

    comp-init:用于将 Google Compute Engine Debian 实例配置为开发人员环境的 bash 启动脚本

    ###Software 该脚本在第一次使用时安装以下软件(和依赖项): 压缩 吉特 MySQL客户端 打开 JDK 7 Gradle 2.3(版本可在脚本中配置) Google App Engine Java 和 Python SDK 点子 派林特 pep8 pyopenssl +...

    linux shell 实例精解

    本书重点讲述了当下最流行的两个Linux Shell——Borne Again Shell(bash)和TC Shell...通过对一个个实例的讲解,循序渐进地介绍了Linux Shell中的各个知识点,使读者逐步具备读写Shell程序的能力。这是第二个压缩包。

    入门学习Linux常用必会60个命令实例详解doc/txt

    hda1中的“1”代表hda的第一个硬盘分区 (partition),hda2代表hda的第二主分区,第一个逻辑分区从hda5开始,依此类推。此外,可以直接检查 /var/log/messages文件,在该文件中可以找到计算机开机后系统已辨认出来的...

    Bash Shell中Shift用法分享

    Shell的名字 $0第一个参数 $1第二个参数 $2第n个参数 $n所有参数 $@ 或 $*参数个数 $# shift默认是shift 1以下边为例: 代码如下:cat shift.sh#—————————-输出文字-开始—————————-#!/bin/...

    Flask REST API项目实例,包含JWT认证、rq异步任务、Swagger文档、Redoc、Docker部署、uwsg等

    在CHANGELOG.md中记录版本日志,重新执行第一步发布镜像 部署说明 创建flask网络:docker network create flask 准备docker-compose.yml, 注释源码挂载:- "./:/work" 启动服务:docker-compose up -d 数据库迁移...

    边干边学Linux__第二版_doc格式

    第一部分 Linux 操作环境 第1章 Linux基础 1.1 登录Linux系统 1.2 Linx的shell 1.3 shell的一些基本命令 第2章 文本编辑 2.1 vi文本编辑器 2.2 emacs文本编辑器 第3章 Linux文件系统操作 3.1 文件类型 3.2 文件系统...

    Shell脚本专家指南

    第1部分 基本脚本编程技术 第1章 shell脚本的错误检测 1.1 shell跟踪选项 1.2 简单输出语句 1.3 根据调试层次控制输出 1.4 用函数简化错误检查 1.5 手动单步执行 第2章 标准函数库 2.1 库文件 2.2 一些有用的函数 ...

    Linux Shell编程从初学到精通(第2版)[2015.1].zip

    Shell是用户与内核进行交互操作的一种接口,是Linux最重要的软件之一。目前最流行的Shell称为bash Shell,bash Shell脚本编程以其简洁、高效而著称,多年来成为Linux程序员和系统管理员解决实际问题的利器。 本书...

    BASH 学习笔记小结

    1. Linux 脚本编写基础 1.1 语法基本介绍 1.1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序...

    Linux、Unix编程指南(第三版)

    一个系统管理员或用户学好怎样使用Shell编写程序是最起码的skills之一,Linux下普遍使用Bash shell,Korn Shell是Unix系统下标准的一个脚本语言,称之为ksh。Korn shell中的许多特性使它成为系统管理员创建脚本的一...

    jenkins-configuration:一组用于配置Jenkins实例及其插件的实用程序

    詹金斯配置Groovy脚本集合,用于自动配置Jenkins和第三方插件诸如Jenkins Job DSL之类的工具允许您以编程方式创建作业和其他资源。 但是,Jenkins本身的配置仍然是手动过程,容易出错。 这些脚本可用于创建可重现...

    Shell 传递参数

    n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 实例 以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名: #!/bin/bash # author:软件开发网 # url:...

    Express-Postgres-Nginx-Docker:完整的现成示例api和文档,在Nginx负载均衡器后面有3个Express worker,跨工作人员共享Postgres数据库,所有这些都使用Docker进行了容器化

    第一次: 生成应用并启动所有容器 $ bash run.sh build 运行: 运行所有容器 $ bash run.sh run 重新创建Apidocs 从/routes下的控制器重新创建apidocs $ bash run.sh apidoc 运行开发服务器 运行数据库容器和一个...

    零基础学习Linux第十七章 数据库定期备份实例

    /bin/bash /usr/local/mysql/bin/mysqldump -uroot -p123456 -B test &gt; /data/test.sql cd /data tar zcf test.sql.tar.gz test.sql mv test.sql.tar.gz bak/`date -d '-1 day' +%Y%m%d`.tar.gz old=`date -d '-1 ...

    Shell编程中的特殊变量之位置变量介绍

    2、$n 获取当前执行脚本的第n个参数,n=1..9,$0,为当前脚本名。如果n大于9,使用${10} 代码如下: echo’echo ‘$(seq-s ” $”1 5|sed’s/1/$1/’) &gt; test_n.sh cattest_n.sh #内容如下 #echo $1 $2 $3 $4 $5 ...

    通过Shell脚本批量创建服务器上的MySQL数据库账号

    输入参数是两个,第一个是数据库所在的IPs,即多个Server IP构成的字符串,IP间用逗号隔开。第二个参数是 端口(3306 或 3307) ##MySQL程序所在路径--mysql bin 文件所在路径;如果由建立软连接,可直

Global site tag (gtag.js) - Google Analytics