登录  | 加入社区

黑狼游客您好!登录后享受更多精彩

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

查看: 933|回复: 0

20分钟轻松学会shell编程,80%人看过这篇就收藏了

[复制链接]

 成长值: 35950

8169

主题

7094

帖子

6831

现金

黑狼创办人

Rank: 12Rank: 12Rank: 12

积分
6831
发表于 2017-11-1 23:27:19 | 显示全部楼层 |阅读模式 来自 河南郑州
在过去几十年中所出现的UNIX和类UNIX操作系统家族已经成为如今最为流行、使用最广泛的操作系统之一,这都算不上什么秘密了。对于使用了多年UNIX的程序员而言,一切都顺理成章:UNIX系统为程序开发提供了既优雅又高效的环境。这正是Dennis Ritchie和Ken Thompson在20世纪60年代晚期在贝尔实验室开发UNIX时的初衷。
在本书中,我们使用的术语UNIX泛指基于UNIX的操作系统大家族,其中包括像Solaris这样真正的UNIX操作系统以及像Linux和Mac OS X这样的类UNIX操作系统。
UNIX系统最重要的特性之一就是各式各样的程序。超过200个基本命令会随着标准操作系统发行,Linux还对标准命令数量做了扩充,通常能达到700~1000个!这些命令(也称为工具)从统计文件行数、发送电子邮件到显示特定年份的日历,可谓无所不能。
不过UNIX真正的威力并非来自数量庞大的命令,而在于你可以非常轻松、优雅地将这些命令组合在一起完成非常复杂的任务。
UNIX的标准用户界面是命令行,其实就是Shell,它的角色是作为用户和系统最底层之间(内核)的缓冲带。Shell就是一个程序,读入用户输入的命令,将其转换成系统更易于理解的形式。它还包括了一些核心编程构件,可以做出判断、执行循环以及为变量储值。
从AT&T发行版(源自Stephen Bourne在贝尔实验室编写的初版)开始,标准Shell就是同UNIX系统捆绑在一起的。自那时起,IEEE根据Bourne Shell以及后续的一些其他Shell制订了标准。该标准目前的(本书写作之时)版本是Shell and Utilities volume of IEEE Std 1003.1-2001,也称为POSIX标准。本书余下的内容都离不开Shell。在本章中,你将学习到什么是UNIX的Shell,Shell能够做什么,以及为什么说它是每个高级用户工具箱中不可或缺的一部分。20分钟轻松学会shell很容易,不过如果想要全面掌握还需要专业的书籍来深度学习。本文摘自《UNIX/Linux/OS X中的Shell编程(第4版)》
GXygXxsFfZtzFfwf.jpg 试读更多内容,请访问异步社区
2.1 内核和实用工具
UNIX系统在逻辑上被划分为两个不同的部分:内核和实用工具(Utility),如图2.1所示。或者你也可以认为是内核和其他部分,通常来说,所有的访问都要经由Shell。
FTW6Z9UldTtCM9Tp.jpg
图2.1 UNIX系统
内核是UNIX系统的核心所在,当打开计算机并启动(booted)之后,内核就位于计算机的内存中,直到关机为止。
组成完整的UNIX系统的各种实用工具位于计算机磁盘中,在需要的时候会被加载到内存中并执行。实际上你所知道的所有UNIX命令都是实用工具,因此这些命令所对应的程序也都在磁盘上,仅在需要时才会被载入内存。举例来说,当你执行date命令时,UNIX系统会将名为date的程序从磁盘上载入到内存中,读取其代码来执行特定的操作。
Shell也是一个实用工具程序,它作为登录过程的一部分被载入到内存中执行。实际上,有必要了解当终端或终端窗口中的第一个Shell启动时所发生的一系列事件。
2.2 登录Shell
在早期,终端是一个物理设备,通过线缆连接到安装了UNIX系统的硬件上。而如今,终端程序能够让你停留在Linux、Mac或Windows环境内部,在受控窗口(managed window)中同网络上的设备交互。通常来说,你会启动如Terminal或xterm这类程序,然后在需要的时候利用ssh、telnet或rlogin连接到远程系统。
对于系统上的每个物理终端,都会激活一个叫作getty的程序,如图2.2所示。
RXirU55XM02k3u4H.jpg
图2.2 getty进程
只要系统允许用户登录,UNIX系统(更准确地说,应该是叫作init的程序)就会在每个终端端口自动启动一个getty程序。getty是一个设备驱动程序,能够让login程序在其所分配的终端上显示login:,等待用户输入内容。
如果你是通过ssh这类程序来连接的,会分配到一个伪终端或伪tty。这就是为什么在输入who命令时会看到有类似于ptty3或pty1这样的条目。
在这两种情况下,会有程序读取账户和密码信息,对这些信息进行验证,如果没有问题的话,就调用登录所需的登录程序。
只要输入相应字符并敲下Enter键,login程序就完成了登录过程(见图2.3)。
当login开始执行时,它会在终端上显示字符串Password:,然后等待用户输入密码。完成输入并按下Enter键后(出于安全性的考虑,你在屏幕上看不到输入的内容),login会比对文件/etc/passwd中相应的条目来验证登录名和密码。每个用户在该文件中都有对应的条目,其中包括了登录名、主目录以及用户登录后要启动的程序。最后一部分信息(登录Shell)存储在每行最后一个冒号之后。如果这个冒号后面没有内容,则默认使用标准Shell,即/bin/sh。
MwZPWI5L39iS23PF.jpg
图2.3 用户sue终端上启动的login
如果是通过终端程序登录,数据交换也许会涉及系统上的程序(如ssh)和服务器上的程序(如sshd),要是你在自己的UNIX计算机上打开了窗口,可能不需要再次输入密码就能够立刻登入。非常方便!
把话题转回密码文件。下面3行展示了/etc/passwd文件内容的典型形式,对应着系统用户:sue、pat和bob。
sue:*:15:47::/users/sue:
pat:*:99:7::/users/pat:/bin/ksh
bob:*:13:100::/users/data:/users/data/bin/data_entry
待login将所输入密码的加密形式与特定账户保存在/etc/shadow中的加密形式进行比对之后,如果没有问题,它会检查要执行的登录程序的名称。在绝大多数情况下,这个登录程序会是/bin/sh、/bin/ksh或/bin/bash。在少数情况下,可能会是一个特殊的定制程序或者/bin/nologin,后者用于不能进行交互式访问的账户(常用于文件所有权管理)。其背后的理念就是你可以为登录账户进行设置,使其登录到系统之后能够自动运行指定的程序。大多数时候指定的程序都是Shell,毕竟它是一种通用的实用工具,不过这并非是唯一的选择。
来看用户sue。一旦该用户通过验证,login会结束掉自身,将控制权转交给sue的终端连接,该连接与标准Shell相连,然后login就从内存中消失了(见图2.4)。
按照之前/etc/passwd文件中显示的其他条目,pat得到的是存储在/bin下的ksh(这是Korn Shell),bob得到的是一个名为data_entry的指定程序(见图2.5)。
mtl7Dt97aeTU816I.jpg
图2.4 login执行/usr/bin/sh
c0LlUQ8LOglnlJ8q.jpg
图2.5 3个登录的用户
之前提到过,init程序会针对网络连接运行类似于getty的程序。例如,sshd、telnetd和rlogind会响应来自ssh、telnet和rlogin的连接请求。这些程序并没有直接和特定的物理终端或调制解调器线路联系在一起,而是将用户的Shell连接到伪终端上。你可以在X Window系统的窗口中或使用who命令查看是否已经通过网络或联网的终端连接登录到了系统中:
$ who
phw pts/0 Jul 20 17:37 使用rlogin登录
$
2.3 在Shell中输入命令
当Shell启动时,它会在终端中显示出一个命令行提示符,通常是美元符$,然后等待用户输入命令(图2.6中的第1步和第2步)。每次输入命令并按Enter键(第3步),Shell就会分析输入的内容,然后执行所请求的操作(第4步)。
如果你要求Shell调用某个程序,Shell会搜索磁盘,查找环境变量PATH中指定的所有目录,直到找到指定的程序。找到了该程序后,Shell会将自己复制一份(称为子Shell),让内核使用指定的程序替换这个子Shell,接着登录Shell就会"休眠",等待被调用的程序执行完毕(第5步)。内核将指定程序复制到内存中并开始执行。这个复制过来的程序称为进程。程序和进程之间是有区别的,前者是保存在磁盘上的文件,而后者位于内存中并被逐行执行。
如果程序将输出写入到标准输出中,那么输出内容会出现在终端里,除非你将其重定向或通过管道导向其他命令。与此类似,如果程序从标准输入中读取输入,那么它会等着你输入内容,除非输入被重定向到了另一个文件或通过管道从其他命令导入(第6步)。
当命令执行完毕后,就会从内存中消失,控制权再次交给登录Shell,它会提示你输入下一条命令(第7步和第8步)。
XyhWSdZ5oKRnSKd8.jpg
图2.6 命令执行周期
注意,只要你没有登出系统,这个周期就会周而复始下去。如果登出系统,Shell就会终止执行,系统将会启动一个新的getty(或者rlogind等)并等待其他用户登入,如图2.7所示。
重要的是要认识到Shell就是一个程序而已。它在系统中没有什么特权,也就是说,只要有足够的专业技术和热情,任何人都可以创建自己的Shell。这就是为什么如今会有这么多不同风格的Shell,其中包括由Stephen Bourne开发的古老的Bourne Shell、由David Korn开发的KornShell、主要用于Linux系统的Bourne again Shell以及由Bill Joy开发的C Shell。这些Shell都旨在应对特定的需求,各自都有自己独特的功能和特色。
fDneEWZvobe2W30h.jpg
图2.7 登录周期
2.4 Shell的职责
现在你知道了Shell会分析(用计算机行话来说,就是解析)输入的每一行命令,然后执行指定的程序。在解析期间,文件名中的特殊字符(如*)会被扩展,就像第一章讲到的那样。
Shell还有其他的职责,如图2.8所示。
dpnruCGdKGUuDCDR.jpg
图2.8 Shell的职责
2.4.1 程序执行
Shell负责执行你在终端中指定的所有程序。
每次输入一行内容,Shell就会分析该行,然后决定执行什么操作。就Shell而言,每一行都遵循以下基本格式:
program-name arguments
说得更正式些,输入的这一行叫做命令行。Shell会扫描该命令行,确定要执行的程序名称及所传入的程序参数。
Shell使用一些特殊字符来确定程序名称及每个参数的起止。这些字符统称为空白字符(whitespace characters),它们包括空格符、水平制表符和行尾符(更正式的叫法是换行符)。连续的多个空白字符会被Shell忽略。如果你输入命令
mv tmp/mazewars games
Shell会扫描该命令行,提取行首到第一个空白字符之间的所有内容作为待执行的程序名称:mv。随后的空白字符(多余的空格)会被忽略,直到下一个空白字符之间的字符作为mv的第一个参数:tmp/mazewars。再到下一个空白字符(在本例中是换行符)之间的字符作为mv的第二个参数:games。解析完命令行之后,Shell就开始执行mv命令,其中包括两个指定的参数:tmp/mazewars和games(见图2.9)。
j29pJ6QeU7AgEeEE.jpg
图2.9 执行带有两个参数的mv命令
刚才提到过,多个空白字符会被Shell忽略。这意味着当Shell处理下面的命令行时:
echo when do we eat?
会向echo程序传递4个参数:when、do、we和eat?(见图2.10)。
zaafD1dfD7dbDRC1.jpg
图2.10 执行带有4个参数的echo命令
echo会提取命令参数并将其显示在终端中,因此在输出的参数之间加上一个空格会使得命令输出变得更易读:
$ echo when do we eat?
when do we eat?
$
结果证明echo命令完全看不到这些空白字符,它们都被Shell给"没收"了。等到第5章讲引用的时候,你就知道该如何把空白字符包含到程序参数中了,不过,通常来说,去掉这些多余的空白字符正是我们想要的做法。
我们之前讲到过,Shell会搜索磁盘,直到找到需要执行的程序为止,然后由UNIX内核负责程序的执行。在大多数时候,的确如此。但有些命令实际上是内建于Shell自身中的。这些内建命令包括cd、pwd和echo。Shell在磁盘中搜索命令之前,它首先会判断该命令是否为内建命令,如果是的话,就直接执行。
不过在调用命令之前,Shell还有点事需要处理,因此,让我们先来讨论一下这方面的内容。
2.4.2 变量及文件名替换
和比较正式的编程语言一样,Shell允许将值赋给变量。只要你在命令行中将某个变量放在美元符号$之后,Shell就会将该变量替换成对应的变量值。我们会在第4章中详细讨论这个话题。
除此之外,Shell还会在命令行中执行文件名替换。实际上Shell,在确定要执行的程序及其参数之前,会扫描命令行,从中查找文件名替换字符*、?或[...]。
假设当前目录下包含这些文件:
$ ls
mrs.todd
prog1
shortcut
sweeney
$
现在让我们在echo命令中使用文件名替换(*):
$ echo `*``` 列出所有文件
mrs.todd prog1 shortcut Sweeney
$
我们给echo程序传入了几个参数?1个还是4个?因为Shell会执行文件名替换,所以答案是4个。当Shell分析下列命令行时
echo *
它识别出了特殊字符*,将其替换成当前目录下的所有文件名(甚至还会将这些文件名依字母顺序排列):
echo mrs.todd prog1 shortcut sweeney
然后Shell决定将哪些参数传给实际的命令。因此,echo根本不知道星号*的存在,它只知道命令行上有4个参数(见图2.11)。
cmO2IN27bm7U0OMB.jpg
图2.11 执行echo
2.4.3 I/O重定向
Shell还要负责处理输入/输出重定向。它会扫描每一个命令行,从中查找特殊的重定向字符或>>(如果你觉得好奇的话,还有一个重定向序列,然后将命令行中的下一个单词作为输出重定向所指向的文件名。在本例中,这个文件名为reminder。如果reminder已经存在且用户具有写权限,那么文件中已有的内容会被覆盖掉。如果没有该文件或其所在目录的写权限,Shell会产生错误信息。
在Shell执行程序之前,它会将程序的标准输出重定向到指定的文件。在大多数情况下,程序根本不知道自己的输出被重定向了。它仍照旧向标准输出中写入(这通常是终端),意识不到Shell已经将信息重定向到了文件中。
让我们来看两个几乎一样的命令:
$ wc -l users
5 users
$ wc -l < users
5
$
在第一个例子中,Shell解析命令行,确定要执行的程序名称是wc并为其传入两个参数:-l和users(见图2.12)。
P3L3uo9rrZmuTZn6.jpg
图2.12 执行wc -l users
当wc执行时,会看到传入的两个参数。第一个参数是-l,告诉它需要统计行数。第二个参数指定了待统计行数的文件。因此wc会打开文件users,统计行数,然后打印出结果及对应的文件名。

第二个例子中的wc操作略有不同。Shell在扫描命令行时发现了输入重定向字符




上一篇:linux shell实用常用命令
下一篇:ELK日志分析系统
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

 

QQ|申请友链|小黑屋|手机版|Hlshell Inc. ( 豫ICP备16002110号-5 )

GMT+8, 2024-5-16 00:50 , Processed in 0.095663 second(s), 47 queries .

HLShell有权修改版权声明内容,如有任何爭議,HLShell將保留最終決定權!

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表