在日常交际英语中,Shell可以翻译成壳,大多指能够对内部核心起到保护作用的一种装置或结构。在计算机科学中,shell其实是指:为操作者提供的、能够通过系统调用或库调用使用整个计算机资源的访问接口。
它既是一种命令解析器又是一种程序设计语言。作为命令解析器,它可以解释和执行用户输入的命令,也可以自动地解释和执行预先编写好并保存在某个文本文件中的一系列的命令;作为程序设计语言,shell特别定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和条件分支,让我们可以像使用高级语言那样去使用shell,使之能够胜任复杂逻辑环境的应用,提高其执行效率,更容易发挥其自动化的特性。
与前面的内容相似的,还有下列两个概念:交互式shell和非交互式shell
交互式模式就是shell等待你的输入,并且执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、注销登录。当你注销登录后,shell将终止运行。
shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。
在"UNIX Like"中主要的shell类型有:
Bourne shell (sh)
Korn shell (ksh)
Bourne Again shell (bash)
POSIX shell (sh)
C shell (csh)
TENEX/TOPS C shell (tcsh)
=============================================================
Bourne Shell
首个重要的标准Unix Shell是1970年底在V7 Unix(AT&T第7版)中引入的,并且以它的创始科技部基础条件平台——“国家气象网络计算应用节点建设”(2004DKA50730)资助者Stephen Bourne的名字命名。Bourne shell 是一个交换式的命令解释器和命令编程语言。Bourne shell可以运行为login shell或者login shell的子shell(subshell)。只有login命令可以调用Bourne shell作为一个login shell。此时,shell先读取/etc/profile文件和$HOME/.profile文件。/etc/profile文件为所有的用户定制环境,$HOME/.profile文件为本用户定制环境。最后,shell会等待读取你的输入。
C Shell
Bill Joy于20世纪80年代早期,在伯克利的加利福尼亚大学开发了C shell。它主要是为了让用户更容易的使用交互式功能,并把ALGOL风格的语法结构变成了C语言风格。它新增了命令历史、别名、文件名替换、作业控制等功能。
Korn Shell
有很长一段时间,只有两类shell供人们选择,Bourne shell用来编程,C shell用来交互。为了改变这种状况,AT&T的Bell实验室David Korn开发了Korn shell。ksh结合了所有的C shell的交互式特性,并融入了Bourne shell的语法。因此,Korn shell广受用户的欢迎。它还新增了数学计算,进程协作(coprocess)、行内编辑(inline editing)等功能。Korn Shell是一个交互式的命令解释器和命令编程语言.它符合POSIX标准(Portable Operating System,一个操作系统的国际标准)。但POSIX其实并不是一个操作系统(本来应被称为POS标准,但后来为了迎合UNIX,而特别的在POS后面增加了IX后缀,故而称为了POSIX标准),而是一个目标在于方便应用程序的移植的标准,使得应用程序在源程序级别实现跨多种平台编译称为可能。
Bourne Again Shell (bash)
bash是GNU计划的一部分,用来替代Bourne shell。它用于基于GNU的系统如Linux.大多数的Linux(Red Hat,Slackware,Caldera)都以bash作为缺省的shell,并且运行sh时,其实调用的是bash,因为sh不过是bash的一个符号链接罢了。
POSIX Shell
POSIX shell 是Korn shell的一个变种. 当前提供POSIX shell的最大卖主是Hewlett-Packard。在HP-UX 11.0,POSIX shell 就是/bin/sh。
——引自《百度百科》
=============================================================
bash是大多数Linux系统以及Mac OS X默认的shell,它能运行于大多数Unix类操作系统之上,甚至被移植到了Microsoft Windows上的Cygwin系统中,以实现Windows的POSIX虚拟接口。
bash的命令语法是Bourne shell(简称sh)命令语法的超集。数量庞大的sh脚本通常可以不经修改而直接在bash中执行,除了那些使用了sh的特殊变量或内置命令的脚本才需要修改。bash的命令语法很多来自Korn shell(ksh)和C shell(csh),例如命令行编辑,命令历史,目录栈,$RANDOM和$PPID变量,以及POSIX的命令替换语法: $(...)。
当bash作为交互的登录shell,或者是带有--login选项的非交互的shell时,只要/etc/profile文件存在,它首先读取并执行其中的命令。读取该文件之后,它以如下的顺序查找~/.bash_profile, ~/.bash_login和~/.profile, 并以此顺序从存在并且可读的第一个文件中读取并执行其中的命令。--noprofile选项可以被用来在shell启动时阻止它的这种行为。
一个登录 shell 退出时,只要~/.bash_logout和/etc/bash.bash_logout文件存在,bash就会读取并执行这些文件中的命令。
当一个交互的shell但不是登录shell启动时,只要~/.bashrc文件存在,bash就会从中读取并执行命令。通过使用--norc选项来阻止它的这种行为。--rcfile "file"选项将强制bash读取并执行"file"文件中的命令,而不是~/.bashrc中的。
当bash以非交互的方式启动时,例如在运行一个shell脚本,bash会在环境变量中查找BASH_ENV变量,如果该变量存在则将它的值展开,使用展开的值作为一个文件的名称来读取并执行。Bash运作的过程就如同执行了下列命令:
if [ -n "$BASH_ENV" ] ; then
. "$BASH_ENV"
fi
但是没有使用PATH变量的值来搜索那个文件名。
如果bash以名称sh被调用,它试图尽可能地模仿sh历史版本的启动过程,同时也遵循POSIX标准。当作为交互式登录shell启动时,或者使用了--login选项启动非交互shell时,它首先以这样的顺序去尝试读取并执行/etc/profile文件和~/.profile文件中的命令。--noprofile选项可以被用来阻止这种行为。当使用命令sh来启动一个交互式的shell时,bash会查找环境ENV变量,如果已经被定义的话就展开它的值,然后使用展开后的值作为要读取和执行的文件的名称。由于使用sh启动的shell不会尝试读取和执行任何其他的启动文件,选项--rcfile就无效了。使用名称sh启动的非交互shell不会尝试读取任何其他启动文件。当以sh启动时,bash在读取启动文件之后进入posix模式。
当bash以posix模式启动时,就像使用--posix选项一样,它的启动文件遵循POSIX标准。这种模式下,交互式shell展开ENV变量的并读取和执行以ENV变量值为文件名的配置文件。不会读取其他启动文件。
当Bash正在使用其标准输入连接到网络的时候,它尝试判断是不是通过远程shell守护程序启动的,通常为rshd,或secure shell daemon(sshd)。如果bash发现它是由rshd启动的,并且如果~/.bashrc文件存在并可读,它将读取并执行其中的命令。如果以sh命令启动,它不会这样做了。--norc选项可以用来阻止这种行为,--rcfile选项用来强制读取另一个文件,但是通常rshd不会允许它们被指定或者以那些选项被调度启动。
如果shell是以与有效用户(组)ID而不是真实用户(组)ID来启动的,并且没有-p选项,那么,不会读取启动文件,也不会从环境中继承shell函数,如果环境变量中有SHELLOPTS,BASHOPTS,CDPATH和GLOBIGNORE的话,则忽略,将有效用户ID被设置为真实用户ID。如果支持-p选项,则启动时的行为是类似的, 但是不会重置有效用户ID。
对于任何一款Linux发行版操作系统而言,bash不过就是一个应用程序而已,只不过这个应用程序十分特殊罢了。而在Linux中,对于bash自身而言,它就是一个外部命令而已。下面我们简单来看看这个外部命令的作用吧。
bash:外部命令,命令文件存放的路径为:/bin/bash,全称为:GNU Bourne-Again SHell
使用格式:bash [options] [file]
命令描述:
Bash是一种sh兼容的,可以从标准输入或文件中读取并执行命令的命令语言解释程序。Bash也融合了Korn和C shell的一些有用的特性。
Bash是一种一致性实现的Shell和遵循IEEE POSIX规范的实用工具。默认的,bash被配置为与POSIX一致。
常用选项:
-c string:
如果有 -c 选项,那么将从"string"中读取命令。如果string后面有参数(argument),它们将分配给从$0开始的位置参数。
-i:
如果有-i选项,shell将交互地执行(interactive)。
-l:
选项使得bash以类似登录shell(login shell)的方式启动。
-r:
如果有-r选项,shell成为受限的(restricted)。
-s:
如果有-s选项,或者如果选项处理完以后,没有参数剩余,那么命令将从标准输入读取。这个选项允许在启动一个交互shell时可以设置位置参数。
-D:
向标准输出打印一个以$为前导的,以双引号引用的字符串列表。这是在当前语言环境不是C或POSIX时,脚本中用于完成语言转换的字符串。这暗指-n选项;不会执行命令。
[-+]O [shopt_option]
shopt_option是一个shopt内置命令可接受的选项。如果有shopt_option,-O将设置那个选项的值;+O取消它。如果没有给出shopt_option,被shopt接受的shell选项的名称和值将会显示在标准输出上。如果启动选项是+O,输出将显示为一种可以重用为输入的格式。
--:
"--"标志选项的结束,禁止其余的选项处理。任何"--"之后的参数将作为文件名和参数对待。参数-与此等价。
Bash也解释一些多字符的选项。这些选项必须在单字符选项被识别之前出现在命令行。
--debugger:
为shell启动之前被执行的调试器配置文件做准备。打开展开调试模式和shell功能追踪。
--dump-po-strings:
等价于-D,但是输出是GNU gettext po(可移植对象)文件格式
--dump-strings:
等价于-D
--help:
在标准输出显示用法信息并成功退出
--init-file file
--rcfile file:
如果shell是交互的,执行file中的而不是标准的个人初始化文件~/.bashrc中的命令
--login:
等价于 -l
--noediting:
如果shell是交互的,不使用GNU readline库来读取命令行。
--noprofile:
不读取系统全局的启动文件/etc/profile或者任何个人初始化文件~/.bash_profile, ~/.bash_login,或~/.profile 。默认情况下,bash在作为登录shell被调用时就会读取这些文件
--norc:
如果shell是交互的,不读取和执行个人初始化文件~/.bashrc。如果shell被调用为sh,这个选项是默认启用的。
--posix:
如果默认操作与POSIX 1003.2标准不同的话,改变bash的行为(posix mode)。
--restricted:
shell被限制。
--rpm-requires:
生产一个为让shell脚本运行所需的文件列表。这个暗指-n。受限于和编译时错误检测相同的限制;Backticks, [] tests, 还有evals不会被分析,可能会丢失一些依赖关系。
--verbose:
等价于 -v
--version:
在标准输出显示此bash实例的版本信息并成功退出。
参数:
如果选项处理之后仍由参数剩余,并且没有提供-s或-c选项的话,那么第一个参数将被假定成一个包含shell命令的文件的名字如果bash以这种方式启动,$0就被设置成这个文件的名字,位置参数也将被设置为剩余的参数。Bash从这个文件中读取并执行命令,然后退出。Bash的退出状态是脚步中最后一个命令的退出状态。如果没有命令被执行,退出状态为0 。首先尝试在当前目录下打开这个文件,接下来,如果文件未被发现,那么shell将为了找到脚本文件而搜索PATH中的目录。
shell中定义的一些:
下列定义在文档余下部分中通用。
blank:一个空格或是制表符
word:一个被shell视作以一个单独单元的字符串,也被称为令牌。
name:一个只能以字母或下划线开始,并且只包括字母数字和下划线的字符串,也被称为标识符。
metacharacter:元字符,一个如果不引用就将成为词的分隔符的字符,如下字符之一:
| & ; ( ) < > space tab
control operator:控制操作符,一个拥有控制功能的令牌,如下符号之一:
|| & && ; ;; ( ) | <newline>
既然shell是如此的重要而且也是如此的不可或缺,那么我们应该如何使用shell呢?其实很简单,想要学会使用shell就必须要了解shell给操作者提供了哪些功能特性。
下面就以bash为例,介绍一下它的基本功能特性:
一、命令解释:
这个功能特性可以说是任何一种shell或者会所任何一款接口程序所必须具备的基本功能,甚至应该说是其与生俱来的使命了。
那么bash是如何解释命令的呢?
1.在bash中,只有键入了回车,bash才去试图解释命令;
2.在键入回车之后,bash尝试着将整个命令行的内容按照空白字符分段,bash会认为最左侧的一段就是要执行的命令;
3.bash在确定了可能是命令的字符串之后,需要知道这是内部命令还是外部命令。所谓内部命令,就是指bash自带的命令,这样的命令通常是在bash运行起来之后就被载入内存了,因为这样的命令就是bash的一部分;所谓的外部命令,就是通过安装其他应用程序存储在外部存储器某些数据块里面的命令,与bash并没有什么直接的关系。如果本次执行的是内部命令,bash可以直接运行,然后返回执行结果和执行状态信息;如果本次执行的不是内部命令,bash就会调用内置的保存了可执行二进制文件的路径的PATH变量,读取变量中的内容,从中查找是否有与本次执行的命令相同名称的文件,如果有就执行之,如果没有,就会报告错误信息,诸如:-bash: COMMAMD: command not found。其中的"COMMAND"可能会被替换为执行的命令。如下例所示:
[root@localhost ~]# sss -bash: sss: command not found
4.bash判断出命令类型并给予解释的时候,整个命令行中的其他字段,会被当作选择以及参数送给命令,如果都正确,则说明此命令可以正确执行,bash会按照命令要求的格式将此次命令的执行结果输出至标准输入;但如果这样的选项或参数不正确或者不能使用,则还需要按照以下两种情况对待:
1)如果是内部命令,bash会一直负责到底,帮助该命令报告错误信息;
2)如果是外部命令,就不是bash可以决定的了,通常这个时候,会由被执行的应用程序来判断选项和参数是否正确,如果不正确,会由应用程序来报告错误信息。
3)无论是内部命令还是外部命令,就算是外部命令也是由bash启动的,所以bash得对此命令是否能够成功执行、是否正常退出负责。在执行结束的时候,bash都要使用一个退出代码标识其执行的状态信息,并将该状态信息保存至一个名为"?"的特殊变量中。如果想要查看这个状态之,我们可以使用一般的变量内容的读取方法:echo $? 即可。通常来讲,这样的命令执行状态的返回值是由0-255的整数来表示,其中:
0:表示命令执行成功
1、2、127:为bash默认的错误代码
3-126:为用户可自定义的错误代码
128-255:某个命令非正常退出,而是由于接收到了某个信号才退出,这样的状态返回值为128+N,其中的"N"就表示命令接收到的信号的编号。
请参考下面的例子:
[root@localhost ~]# cd -a -bash: cd: -a: invalid option cd: usage: cd [-L|-P] [dir]
(此处为内部命令的选项错误)
[root@localhost ~]# cd /testtt -bash: cd: /testtt: No such file or directory
(此处为内部命令的参数错误)
[root@localhost ~]# echo $? 1
(此处为命令执行的状态返回值。一般情况下,若该返回值为"0"则表示命令成功执行,否则即为命令执行失败)
[root@localhost ~]# ls --P ls: unrecognized option '--P' Try `ls --help' for more information.
(此处为选项错误)
[root@localhost ~]# ls /testtt ls: cannot access /testtt: No such file or directory
(此处为参数错误)
[root@localhost ~]# echo $? 2
(此处为命令执行的状态返回值。一般情况下,若该返回值为"0"则表示命令成功执行,否则即为命令执行失败)
二、命令别名
bash给我们提供了给命令建立别名的特权,不管是系统管理员也好,普通用户也好,都可以定义别名命令,而且定义的方法非常简单:
1.可以使用alias命令来定义别名命令;
2.可以使用unalias命令取消已经定义的别名命令;
3.无论是使用alias命令定义还是unalias命令来取消,都是改了内存中的bash运行参数,因此只要bash关闭,该功能就失效了。因此,为了能够让别名命令永久有效,可以将这样的定义内容写在家目录中的.bashrc文件中,每个用户家里面都有这个文件,童叟无欺。
每一个简单命令的第一个字如果不被引号引用,bash就会对其实施被检查,看看这个命令是否有某个命令的别名。如果是,这个命令就会被对应的别名替换。字符"/","$","`"和"="以及shell任何一个shell的元字符不能出现在别名中。替换的文本内容可以包含任何的有效的shell输入,也包括shell的元字符。替换文本的第一个词也被检查是否为别名。但是如果它与被替换的别名相同,就不会再替换第二次。这意味着可以用ls作为ls -F的别名,bash不会递归地展开替换文本。如果别名的最后一个字符是空白字符,那么命令中别名之后的下一个词也将被检查是否能进行别名展开。
如果shell不是交互的,别名将不会展开,除非使用内置命令shopt设置了expand_aliases选项。
关于别名的定义和使用的规则比较混乱。Bash在执行一行中的任何命令之前,总是完整地读入一行的输入。别名在命令被读取时展开,而不是在执行的时候。因此,别名定义如果和另一个命令在同一行,那么不会起作用,除非读入了下一行。别名定义之后,同一行中的命令不会受新的别名影响。这种行为在函数执行时存在争议,因为在函数中定义的别名替换是别名被读取时才会发生,而不是函数被执行的时候,因为函数定义本身是一个复合命令。结果,在函数中定义的别名只有当这个函数执行完才会生效。为了保险起见,应当总是将别名定义放在单独的一行,不在复合命令中使用alias。
别名这个特性在有些时候会非常有用,因为依靠这个特性,我们可以:
1.将复杂的命令简单化,如:
[root@localhost ~]# alias l.='ls -d .* --color=auto' [root@localhost ~]# alias ll='ls -l --color=auto' [root@localhost ~]# alias ls='ls --color=auto'
2.将危险的命令安全化,如:
[root@localhost ~]# alias cp='cp -i' [root@localhost ~]# alias mv='mv -i' [root@localhost ~]# alias rm='rm -i'
3.根据自己的喜好和使用习惯来定义不同风格的命令,如:
[root@localhost ~]# alias cdnet='cd /etc/sysconfig/network-scripts/' [root@localhost ~]# alias viht='vim /etc/httpd/conf/httpd.conf'
(待续...)
转载于:https://blog.51cto.com/zhaotianyu/1785288