shell脚本编程 linux shell编程实例

您好,欢迎来到“C语言中文网-->Shell专题”,您将享受到免费的Shell教程和学习资料!

《Linux命令行与Shell脚本编程大全》第2版 PDF中文版

Shell 诞生于 Unix,是与 Unix/Linux 交互的工具,单独地学习 Shell 是没有意义的,请先参考Unix/Linux入门教程,了解 Unix/Lunix 基础。

近几年来,Shell一直被忽略,是一个不受重视的脚本语言。Shell虽然是Unix的第一个脚本语言,但它是相当优秀的。它结合了延展性与效率,持续保有独具的特色,并不断的被改良,使它多年来能与那些花招很多的脚本语言保持抗衡。

Shell需要依赖其他程序才能完成大部分的工作,这或许是它的缺陷,但它不容置疑的长处是:简洁的脚本语言标记方式,而且比C语言编写的程序执行更快、更有效率。

C语言中文网以通俗易懂的语言向您讲解Shell编程,让您在最短的时间内快速掌握Shell,编写出实用的程序和代码。

为了让您尽快体验最新技术,我们的教程将不断更新,保持与时俱进,请及时关注。您的支持是我们前进的动力!

说明:C语言中文网的文章由团队成员翻译、编辑和整理,难免有错误和纰漏,请在文章底部留言向我们指出,谢谢。

Shell脚本编程的常识

 

现在我们使用的操作系统(Windows、Mac OS、Android、iOS 等)都是带图形界面的,简单直观,容易上手,对专业用户(程序员、网管等)和普通用户(家庭主妇、老年人等)都非常适用;计算机的普及离不开图形界面。

然而在计算机的早期并没有图形界面,我们只能通过一个一个地命令来控制计算机,这些命令有成百上千之多,且不说记住这些命令非常困难,每天面对没有任何色彩的“黑屏”本身就是一件枯燥的事情;这个时候的计算机还远远谈不上炫酷和普及,只有专业人员才能使用。

图:早期的电脑,都是“黑纸白字”

猛击《带你逛西雅图活电脑博物馆》可以欣赏更多早期的计算机。

对于图形界面,用户点击某个图标就能启动某个程序;对于命令行,用户输入某个程序的名字(可以看做一个命令)就能启动某个程序。这两者的基本过程都是类似的,都需要查找程序在硬盘上的安装位置,然后将它们加载到内存运行。

关于程序的运行原理,请猛击《载入内存,让程序运行起来》。

换句话说,图形界面和命令行要达到的目的是一样的,都是让用户控制计算机。

然而,真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁。

由于安全、复杂、繁琐等原因,用户不能直接接触内核(也没有必要),需要另外再开发一个程序,让用户直接使用这个程序;该程序的作用就是接收用户的操作(点击图标、输入命令),并进行简单的处理,然后再传递给内核。如此一来,用户和内核之间就多了一层“代理”,这层“代理”既简化了用户的操作,也保护了内核。

用户界面和命令行就是这个另外开发的程序,就是这层“代理”。在Linux下,这个命令行程序叫做 Shell

Shell 除了能解释用户输入的命令,将它传递给内核,还可以:

  • 调用其他程序,给其他程序传递数据或参数,并获取程序的处理结果;
  • 在多个程序之间传递数据,把一个程序的输出作为另一个程序的输入;
  • Shell 本身也可以被其他程序调用。


由此可见,Shell 是将内核、程序和用户连接了起来。

Shell 本身支持的命令并不多,但是它可以调用其他的程序,每个程序就是一个命令,这使得 Shell 命令的数量可以无限扩展,其结果就是 Shell 的功能非常强大,完全能够胜任 Linux 的日常管理工作,如文本或字符串检索、文件的查找或创建、大规模软件的自动部署、更改系统设置、监控服务器性能、发送报警邮件、抓取网页内容、压缩文件等。

Shell 并不是简单的堆砌命令,我们还可以在 Shell 中编程,这和使用 C/C++、Java、Python 等常见的编程语言并没有什么两样。

Shell 虽然没有 C/C++、Java、Python 等强大,但也支持了基本的编程元素,例如:

  • if...else 选择结构,switch...case 开关语句,for、while、until 循环;
  • 变量、数组、字符串、注释、加减乘除、逻辑运算等概念;
  • 函数,包括用户自定义的函数和内置函数(例如 printf、export、eval 等)。


站在这个角度讲,Shell 也是一种编程语言,它的编译器(解释器)是 Shell 这个程序。我们平时所说的 Shell,有时候是指连接用户和内核的这个程序,有时候又是指 Shell 编程。

Shell 主要用来开发一些实用的、自动化的小工具,而不是用来开发具有复杂业务逻辑的中大型软件,例如检测计算机的硬件参数、一键搭建Web开发环境、日志分析等,Shell 都非常合适。

使用 Shell 的熟练程度反映了用户对 Linux 的掌握程度,运维工程师、网络管理员、程序员都应该学习 Shell。

尤其是 Linux 运维工程师,Shell 更是必不可少的,是必须掌握的技能,它使得我们能够自动化地管理服务器集群,否则你就得一个一个地登录所有的服务器,对每一台服务器都进行相同的设置,而这些服务器可能有成百上千之多,会浪费大量的时间在重复性的工作上。

Shell 是一种脚本语言

任何代码最终都要被“翻译”成二进制的形式才能在计算机中执行。

有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。

这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。

而有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。

这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。

编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。

脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合Web开发以及小工具的制作。

Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。

(这些往往是经常用到,但是各种网络上的材料都语焉不详的东西,个人认为比较有用)

七种文件类型

d            目录                                                       l             符号链接

s             套接字文件                                           b            块设备文件

c            字符设备文件                                       p            命名管道文件

-             普通文件

正则表达式

从一个文件或命令输出中抽取或过滤文本时。可使用正则表达式(RE),正则表达式是一些特殊或不很特殊的字符串模式的集合。

基本的元字符集:

^                   只匹配行首。

$                   只匹配行尾。

*                   一个单字符后紧跟*,匹配0个或多个此单字符。

[]                   匹配[]内字符,可以是一个单字符,也可以是字符序列。可以使

用-来表示[]内范围,如[1-5]等价于[1,2,3,4,5]。

\                    屏蔽一个元字符的特殊含义,如\$表示字符$,而不表示匹配行

尾。

.                 匹配任意单字符。

pattern\{n\}   匹配pattern出现的次数n

pattern\{n,\}m匹配pattern出现的次数,但表示次数最少为n

pattern\{n,m\} 匹配pattern出现的次数在n与m之间(n,m为0-255)

几个常见的例子:

显示可执行的文件:ls –l | grep …x...x..x

只显示文件夹:ls –l | grep  ^d

匹配所有的空行:^$

匹配所有的单词:[A-Z a-z]*

匹配任一非字母型字符:[^A-Z a-z]

包含八个字符的行:^……..$(8个.)

字符类描述

以下是可用字符类的相当完整的列表:

[:alnum:] 字母数字 [a-z A-Z 0-9]

[:alpha:] 字母 [a-z A-Z]

[:blank:] 空格或制表键

[:cntrl:] 任何控制字符

[:digit:] 数字 [0-9]

[:graph:] 任何可视字符(无空格)

[:lower:] 小写 [a-z]

[:print:] 非控制字符

[:punct:] 标点字符

[:space:] 空格

[:upper:] 大写 [A-Z]

[:xdigit:] 十六进制数字 [0-9 a-f A-F]

尽可能使用字符类是很有利的,因为它们可以更好地适应非英语 locale(包括某些必需的重音字符等等).

shell的引号类型

shell共有四种引用类型:

“ ”          双引号

‘ ’           单引号

` `         反引号

\            反斜线

l        “ ” 可引用除$、` 、\ 、外的任意字符或字符串,“ ”中的变量能够正常显示变量值。

l        ‘ ’与“ ”类似,不同在于shell会忽略任何的引用值。

例如: GIRL=‘girl’

echo “The ‘$GIRL’ did well”

则打印:The ‘girl’ did well

l        ` `用于设置系统命令的输出到变量,shell会将` `中的内容作为一个系统命令并执行质。

例如:echo `date` 则打印当前的系统时间。

l        \ 用来屏蔽特殊含义的字符:&  *  +  ^  $  `  “  |  ?

例如:expr 12 \* 12 将输出144

变量设置时的不同模式:

valiable_name=value           设置实际值到 variable_name中

valiable_name+value           如果设置了variable_name,则重设其值

valiable_name:?value           如果未设置variable_name,则先显示未定义用户错误信息

valiable_name?value           如果未设置variable_name,则显示系统错误信息

valiable_name:=value   如果未设置variable_name,则设置其值

valiable_name-value            同上,但取值并不设置到variable_name

条件测试

test命令用于测试字符串、文件状态和数字,expr测试和执行数值输出。

Test格式:test condition 或 [ condition ](需要特别注意的是condition的两边都要有一个空格,否则会报错),test命令返回0表示成功。

l        下面将分别描述test的三种测试:

n        文件状态测试(常用的)

-d           测试是否文件夹

-f            测试是否一般文件

-L          测试是否链接文件

-r           测试文件是否可读

-w         测试文件是否可写

-x           测试文件是否可执行

-s           测试文件是否非空

n        字符串测试

五种格式: test  “string”

test  string_operator  “string”

test  “string”  string_operator  “string”

[ string_operator  “string” ]

[ “string”  string_operator  “string” ]

其中string_operator可以为:       =     两字符串相等

!=    两字符串不等

-z   空串

-n   非空串

n        数值测试

两种格式: “number”  number_operator  “number”

[ “number”  number_operator  “number” ]

其中:number_operator 可以为:-eq  、-ne、-gt、-lt、-ge

例如:  NUMBER=130

[ “990”  –le  “995”  –a  “NUMBER”  -gt  “133” ]

(其中-a表示前后结果相“与”)

l        expr命令一般用于整数值,但也可以用于字符串。

n        格式:  expr srgument operator operator argument

例如:  expr 10 + 10

expr 10 ^ 2 (10的平方)

expr $value + 10

n        增量计数――expr在循环中最基本的用法

例如:  LOOP=0

LOOP=`expr $LOOP + 1`

n        模式匹配:通过指定的冒号选项计算字符串中的字符数

例如:  value=account.doc

expr $value : `\(.*\).doc`

输出 account

每一个在UNIX/Linux上工作的程序员可能都擅长shell脚本编程。但大家解决问题的方式却不尽相同,这要取决于对专业知识的掌握程度、使用命令的种类、看待问题的方式等等。对于那些处在shell脚本编程初级阶段的程序员来说,遵循一些恰当的做法可以帮助你更快、更好的学习这些编程技巧。下面,我们就来讨论这些能帮助你学习shell脚本编程的方法吧。

0、多动手

你想学习shell脚本编程,这很不错。于是你拿了一本书开始学习。一些人会首先通读整本教材后再上机练习。这种方法可能适用于一些人,但我却不太看好它。我的建议是,仅仅学一些最基础的能够让你开始编码的知识就可以了。之后,动手写一些简单的程序吧。一旦你由于知识上的欠缺而不得不停止时,再回到书本上去读你想要了解的那部分,然后继续做你的项目。如此周而复始,不断提高你的水平。这种边学边做的方法曾让我受益良多。

1、善用命令提示符

有时候,我们写的脚本中有一些错误。我们修改错误,运行脚本,但系统再次报错。并且这个改错报错的过程可能会发生很多次。碰到这些情况,首先需要找到有问题的行或命令,这可以通过一些调试语句来轻松做到。一旦发现这条语句,尝试在命令提示符下执行相同的语句。如果它在命令提示符下开始正常运行,你就可以容易的推断出它不能正常运行的原因了。可能是由于某些错误输入的命令,或者是某些环境变量不匹配,或者是从不同的地方引用了某个二进制文件等等。这种方法会让调试变得简单易行。

关于 shell 脚本编程的10 个最佳实践

2、考虑问题要全面

现在我们来看个问题。你想到了关于某个问题的解决方案,但这个解决方案只适用于处理小型文件。可是当处理比较大的文件时,你该怎么办?举个例子,我们想要得到一个文件的第一行内容:

这条语句当然会给出你想要的第一行内容。可是如果处理的文件包含上百万条记录呢?尽管上面的那条sed命令可以输出文件的第一行内容,但是想要处理大型文件一定会带来性能上的问题。

解决办法:

这条命令将只输出第一行,同时退出程序。

3、经常尝试不同的方法

你在写脚本时碰到一个问题,然后你找到了一种独特的解决方法。下一次你偶然又碰到类似的问题,这时,不要再用以前你用过的方法来解决。试试另外一种方法吧。如果某一天再次遇到这种情况,再试试其它方法。

例如:

另一种方法:

现在你可能会明白这个博客里会有那么多以“……的不同解决方法”为题的文章了吧。所有这些文章的目的都是用来帮助订阅这个博客的开发者开阔视野,打开思路。

4、快速编码

脚本可以节省我们的时间,提高生产力。可是,难道我们花在写脚本和测试上的时间还少吗?我们想写一个脚本,于是打开一个文件,写下代码,保存文件,之后运行脚本,系统报错,我们再打开文件修改、保存、运行……在这个过程中会花费很多时间。在此前的一篇题为《如何快速写shell脚本》的文章里,你可以学会如何编写脚本和测试正在运行中的脚本,而不用再回顾命令提示符。这些方法可以加快编码的速度。当我写脚本的时候,我总是使用这些方法。而且我可以很肯定的说,它们帮我节约了不少时间。

5、经常使用内部命令

无论碰到哪种情况,请尽量考虑使用内部命令而不是外部命令。在此前的一篇题为《内部命令和外部命令》的文章里,我们可以看到二者间的差异。用内部命令对你永远都有好处。根据正在处理的输入文件的大小,内部命令可以在性能方面为你节省很多。虽然你并不总是有这样选择内部命令抑或外部命令的机会,但在某些情况下,你一定能做出正确的选择。

6、没有必要使用cat命令

这是我们经常在论坛里讨论的话题之一。没有必要使用cat命令指的是在有些时候,我们会发现根本没有必要使用cat命令。有时候,使用了多余的cat命令会让你的代码看起来很丑陋,而且还会带来性能上的问题。

例如:

正确的方法应该是:

7、仔细阅读错误信息

程序员常犯的一个错误是:当我们敲入的命令报错后,我们中的大多数人只是对错误信息一瞥而过,而不会去认真的读一读。很多时候,错误信息里就包含了解决办法。更重要的是,有时候我们修改了某个错误并再次运行后,系统依旧会报错。然后我们再次修改,但系统再次报错。这可能会持续很长时间。但实际上,旧的错误可能已经被纠正,只是由于出现了其它一些新错误才导致系统再次报错。而我们依旧在怀疑为什么修改好的代码依然不能正常运行。因此,请你养成仔细阅读错误信息的习惯。

8、尽量避免臃肿的命令

你正在尝试去从一个大的文件中筛选某条信息。接下来你可能写一大堆命令来实现这一功能。可是,尽管你将得到正确的结果,你写的命令却不够好,且晦涩难懂。因此,我们应该尽量避免这种情况发生。下面这个例子就是代码优化的好例子。

从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修 功课之一。

Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh),等等。不同的Shell语言的语法有所不同,所以不能交换使用。每种Shell都有其特色之处,基本上,掌握其中任何一种 就足够了。在本文中,我们关注的重点是Bash,也就是Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用;同时,Bash也是大多数Linux系统默认的Shell。在一般情况下,人们并不区分 Bourne Shell和Bourne Again Shell,所以,在下面的文字中,我们可以看到#!/bin/sh,它同样也可以改为#!/bin/bash。

利用vi等文本编辑器编写Shell脚本的格式是固定的,如下:

?
1
2
3
#!/bin/sh
#comments
Your commands go here

首行中的符号#!告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程 序。如果首行没有这句话,在执行脚本文件的时候,将会出现错误。后续的部分就是主程序,Shell脚本像高级语言一样,也有变量赋值,也有控制语句。除第 一行外,以#开头的行就是注释行,直到此行的结束。如果一行未完成,可以在行尾加上",这个符号表明下一行与此行会合并为同一行。

编辑完毕,将脚本存盘为filename.sh,文件名后缀sh表明这是一个Bash脚本文件。执行脚本的时候,要先将脚本文件的属性改为可执行的:

?
1
chmod +x filename.sh

执行脚本的方法是:

?
1
./filename.sh

下面我们从经典的“hello world”入手,看一看最简单的Shell脚本的模样。

?
1
2
3
4
#!/bin/sh
#print hello world in the console window
a = "hello world"
echo $a

Shell Script是一种弱类型语言,使用变量的时候无需首先声明其类型。新的变量会在本地数据区分配内存进行存储,这个变量归当前的Shell所有,任何子进 程都不能访问本地变量。这些变量与环境变量不同,环境变量被存储在另一内存区,叫做用户环境区,这块内存中的变量可以被子进程访问。变量赋值的方式是:

?
1
variable_name = variable_value

如果对一个已经有值的变量赋值,新值将取代旧值。取值的时候要在变量名前加$,$variable_name可以在引号中使用,这一点和其他高级语言是明显不同的。如果出现混淆的情况,可以使用花括号来区分,例如:

echo "Hi, $as"

就不会输出“Hi, hello worlds”,而是输出“Hi,”。这是因为Shell把$as当成一个变量,而$as未被赋值,其值为空。正确的方法是:

echo "Hi, ${a}s"

单引号中的变量不会进行变量替换操作。
关于变量,还需要知道几个与其相关的Linux命令。

env用于显示用户环境区中的变量及其取值;set用于显示本地数据区和用户环境区中的变量及其取值;unset用于删除指定变量当前的取值,该值将被指定为NULL;export命令用于将本地数据区中的变量转移到用户环境区。

更多
  • 该日志由 于2017年12月13日发表在 未分类 分类下, 你可以发表评论,并在保留原文地址 及作者的情况下引用到你的网站或博客。
  • 本文链接: shell脚本编程 linux shell编程实例 | 帮助信息-动天数据
  • 文章标签:
  • 版权所有: 帮助信息-动天数据-转载请标明出处
  • 【上一篇】 【下一篇】

    0 Comments.