三、命令历史
bash从Korn Shell和C Shell中吸收了很多的精华,其中之一正式为已经执行过的命令保存一个缓存副本的特性,我们称之为"命令历史"功能。
我们为什么要使用命令历史功能呢?
每个用户登录成功之后,尤其是使用bash这样的文件接口登录之后,所有的操作都是由执行命令来实现的,那么就不可避免的会出现重复执行某个命令的情况,如果每个命令都依靠键入的方式来输入的话,固然没有问题,但是效率不高而且也是浪费时间的"可耻"行为,命令历史刚好给我们提供了解决这种问题的方法。
命令历史的保存分为两个阶段:
1.在bash开启之后,每一条被执行过的命令都会暂时缓存在内存中一个专门指定用来缓存历史命令的空间中;
2.在bash关闭之前,bash总是积极的尝试将内存中的历史命令缓存中的历史命令保存到用户家目录中的.bash_history文件中。
为了让用户在再次登录之后依然可以使用上次bash关闭之前所执行过的历史命令,在用户身份验证通过开启bash的过程中,bash会将用户家目录中的.bash_history文件中保存的所有内容,加载到内存中。这样一来,在用户刚刚登录到系统的时候,内存缓存中的命令历史和.bash_history文件中保存的历史命令是完全一致的,直到你键入一个新命令为止。
为此,bash还准备或定义了数个环境变量来描述命令历史的相关特性,这些变量包括:
HISTSIZE:定义在内存缓存中能够缓存的历史命令的数量,默认值为1000;
HISTFILE:定义在磁盘上保存用户历史命令的文件,默认为~/.bash_history;
HISTFILESIZE:定义在命令历史文件中最多存放的历史命令的数量,默认值为1000;
HISTTIMEFORMAT:显示历史命令的时间格式,可以扩展显示更多的信息,默认未定义;
HISTCONTROL:控制历史命令的记录方式,有ignoredups, ignorespace, ignoreboth三个值可选,默认值为ignoredups;
HISTIGNORE:根据定义的特性,选择哪类命令不存储到命令历史中,默认未定义;
那么如何查看内存中的历史命令缓存呢?我们可以使用history命令来实现。
history:
#:显示最近的#条历史
-a: 追加本次会话新执行的命令历史列表至历史文件
-c:清空内存中的历史命令记录列表
-d:删除历史中指定的命令;
-n: 读历史文件中未读过的行到历史列表
-p: 展开历史参数成多个行,但不存在历史列表中
-r:从历史命令文件中将所有历史命令附加到内存的历史命令记录列表
-s: 展开历史参数成一行,附加在历史列表之后
-w:保存历史命令记录列表到历史命令文件中(.bash_history)
可以使用!来调用历史命令缓存中缓存的内容,如下所示:
!#: 重复执行历史命令记录列表中的特定ID号的命令
!!: 重复执行最后一条命令
!-#: 重复执行倒数第#条命令
!string: 重复执行历史命令记录列表中最后一个以string开头的命令
!^: 最后一条命令的第一个参数
!$: 最后一条命令的最后一个参数 注意:"!$" = "ESC, ." = "Alt+."
!*: 最后一条命令的所有参数
!:#: 最后一条命令的第#个参数
!#:^: 指定编号的历史命令的第一个参数
!#:$: 指定编号的历史命令的最后一个参数
!#:*: 指定编号的历史命令的所有参数
!#:#: 指定编号的历史命令的第#个参数
!STR:^: 指定字符串开头的历史命令的第一个参数
!STR:$: 指定字符串开头的历史命令的最后一个参数
!STR:*: 指定字符串开头的历史命令的所有参数
!STR:#: 指定字符串开头的历史命令的第#个参数
bash的快捷键
Ctrl+l 清屏,相当于clear命令
Ctrl+c 取消命令的执行
Ctrl-a 会移动到命令行的最前面
Ctrl-e 会移动到命令行的最后面
Ctrl-u 会删除到行首
Ctrl-k 会删除到行尾
Ctrl-arrow ctrl-b ctrl-f 会向左或向右移动一个字符
Esc-b 左移一个单词
Esc-f 右移一个单词
四、SHELL语法
简单命令:
简单命令是由空白字符分隔的词和重定向,并且使用控制操作符结束的一个可选的变量分配的序列。第一个词指明了要执行的命令,它被作为第0个参数。其余的词被视为这个命令的参数。
简单命令的返回值就是它的退出状态,或是如果命令被signal n结束的话,返回值就是128+n。
管道:
管道是被控制字符"|"或"|&"分隔开来一个或多个命令的序列,管道的格式是这样:
[time [-p]] [ ! ] command [ | command2 ... ]
命令command的标准输出通过管道连接到命令command2的标准输入。这样的连接是在任何重定向被命令指定之前进行的。
如果使用|&,command的标准错误会通过管道连接到command2的标准输入;这是2>&1 |的简写形式。这种隐含的标准错误的重定向是在任何重定向被命令指定之后进行的。
除非pipefail选项开启,否则管道的退出状态就是最后一个命令的退出状态值。如果pipefail开启,管道的返回状态就是:如果所有命令成功退出,就是0;否则管道状态返回值就是最后一个命令的非0状态返回值。如果保留字!被用在管道之前,管道的退出状态就是如上所述的退出状态的逻辑非的结果。shell在返回退出状态值之前,一直等待管道中的所有命令执行结束。
如果保留字time用在管道之前,在管道中止时,报告执行管道耗费的用户和系统时间。-p选项将使输出符合POSIX指定的格式。TIMEFORMAT变量可以设置为一个格式化的字符串,指定时间信息应当如何显示。
管道中的每个令都作为单独的进程来执行
序列:
list(这里可以翻译为序列)是一个或多个被操作符";","&","&&"或"||"分隔的序列,并且可以选择用";","&"或"<newline>"等字符作为结束。
这些序列操作符中,"&&"和"||"优先级相同,其次是";"和"&",它们的优先级是相同的。
序列中可以有一个或多个换行符来分隔命令,而不是使用分号分隔。
如果一个命令是由控制操作符"&"结束的,shell将在后台的子shell中执行这个命令。shell不会等待命令执行结束,返回状态总是0。以分号";"分隔的命令会被顺序执行;shell会等待每个命令依次结束。返回状态是最后执行的命令的返回状态。
控制操作符"&&"和"||"各自分别代表"与"和"或"序列。"与"序列的形式是:
command1 && command2
command2只有在command1返回0时才被执行。
"或"序列的形式是:
command1 || command2
command2只有在command1返回非0状态时才被执行。"与"和"或"序列的返回状态是序列中最后执行的命令的返回状态。
复合命令
compound command(复合命令)是如下情况之一:
(list):
"list"将在一个子shell环境中执行。变量赋值和影响shell环境变量的内置命令在命令执行完成后将不会再起作用。 状态返回值是"list"所代表的命令的状态返回值。
{ list; }
"list"将在当前shell环境中执行。"list"必须以一个换行符或分号结束。这种做法也称为命令组。状态返回值是"list"所代表的命令的状态返回值。注意与元字符"("和")"不同,"{"和"}"是保留字并且必须出现在保留字被允许识别的场合。由于它们不会产生断词,它们和"list"之间必须用空格分开。
((expression))
根据下面的"ARITHMETIC EVALUATION"段所描述的规则来看,"expression"会被求值。如果"expression"的值为非"0"值的话,状态返回值就是0;否则返回状态为1。这其实就相当于let "expression"。
[[ expression ]]
根据条件表达式"expression"求值的情况返回一个"0"或"1"的状态值。"expression"主要由条件表达式的描述符组成。"[["和"]]"中的词不会拆词和路径展开; 而波浪线展开,参数和变量展开,算术展开,命令替换,进程替换和引用的去除则都将执行。像-f这样的条件操作符必须取消引用才能作为住描述符而被识别。
当使用"[[","<"和">"操作符会使用当前环境逻辑排序。
当使用"=="和"!="操作符时,操作符右边的字符串被认为是一个模式,根据模式匹配的规则进行匹配。如果shell开启nocasematch选项,那么则执行大小写无关的匹配。如果匹配则返回值是 0,否则返回1。模式的所有部分都应该被引用,强制使它作为一个字符串进行匹配。
还有一个额外的二进制操作符"=~",如果可用,跟"=="以及"!="的优先级相同。当使用这种操作符的时候,操作符右边的字符串会被当作展开正则表达式进行相应匹配。如果匹配则返回值是 0,否则返回1。如果正则表达式有语法错误,则条件表达式的返回值是2。如果shell开启nocasematch选项,那么则执行大小写无关的匹配。模式的所有部分都应该被引用,强制使它作为一个字符串进行匹配。子串是由被保存在数组变量BASH_REMATCH中的被括号括选起来的子表达式匹配。数组变量BASH_REMATCH中使用下标"0"的元素是匹配的整个正则表达式的字符串的一部分。数组变量BASH_REMATCH中使用下标"n"的元素是被括号中的子表达式第n次匹配的字符串的一部分。
表达式可以用下列操作符结合起来。根据优先级的降序列出如下:
( expression )
返回表达式"expression"的值。括号可以用来提升操作符的优先级。
! expression
如果表达式"expression"返回假,则整个表达式返回真。
expression1 && expression2
如果表达式expression1和expression2都返回真,则整个表达式返回真。
expression1 || expression2
如果表达式"expression1"或者"expression2"二者至少一个返回真,则整个表达式返回真。
如果expression1可以决定整个条件表达式的返回值的话,"&&"和"||"操作符不会对表达式expression2求值。
for name [ [ in [ word ... ] ] ; ] do list ; done
"in"之后的一系列词会被展开,生成一个列表。变量"name"被依次赋以这个列表中的每个元素,"list" 每次都被执行。如果"in word"被略过,那么"for"命令将遍历每一个已设置的位置参数。返回状态是最后一个被执行的命令的退出状态。如果之后的词展开的结果是空列表则不会执行任何命令并且返回状态为"0"。
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
首先,算术表达式"expr1"根据规则求值。算数表达式"expr2"则重复计算,知道求值结果为"0"。每一次只要"expr2"计算结果非零,就执行"list"并且对算术表达式"expr3"求值。如果任何表达式被略过,都被视为执行结果是"1"。返回值是"list"中被执行的最后一个命令的返回值,或者如果任何一个表达式无效的话,返回值为false。
select name [ in word ] ; do list ; done
"in"之后的一系列词会被展开,生成一个项目列表。展开的"word"的集合被输出到标准错误上,每个前面 加上一个数字。如果"in word"被略过,将输出位置参数。届时,PS3提示符会显示出来并从标准输入读取一行。如果该行包括一个对应于显示"words"的数字,那么"name"变量的值就被设置为"word"。如果输入一个空行,那么"word"和提示符将再次显示出来。如果读入了一个EOF,命令就结束。任何其他值被获取都将导致变量name的值设置为空。读入的行保存在变量REPLY中。"list"在每次选择之后都会执行,直到执行了一个break命令。 select命令的退出状态是"list"中执行的最后一个命令的退出状态,如果没有执行命令就是0。
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
case命令首先展开"word",然后依次尝试轮流使用每个pattern来匹配它,使用与路径展开相同的匹配规则。"word"使用波浪线展开,参数和变量展开,算法替换,命令替换,过程替换以及消除引用。每一个"pattern"也都使用波浪线展开,参数和变量展开,算法替换,命令替换,过程替换。如果shell开启nocasematch选项,那么则执行大小写无关的匹配。当发现一个匹配,相应的"list"将被执行。如果发现使用了";;"操作符,找到一个匹配之后,不会再尝试其后的匹配。使用";&"可以代替了";;",使得"list"关联至下一个模式集继续执行。使用";;&"代替";;",这将导致shell使用语句测试下一个模式列表。如果有,执行已经关联至"list"的成功 匹配。如果没有模式可以匹配,返回值是0。否则,返回"list"中最后执行的命令的返回值。
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
"if list"被执行。如果退出状态是0,"then list"将被执行。否则,每个"elif list"将被按顺序执行,并且如果退出状态是0,相应的"then list"将被执行,命令结束。否则,如果存在"else list"的话,"else list"将被执行。退出状态是最后执行的命令的退出状态,或者是如果所有条件都不满足,退出状态为0,。
while list; do list; done
until list; do list; done
while命令不断地执行"do list"直到"list"中最后一个命令退出状态为0。until命令和while命令等价,除了对条件的测试恰好相反;执行"do list"直到序列中最后一个命令返回非零状态值。while和until命令的退出状态是"do list"中最后一个命令的退出状态,或者如果没有执行任何命令,退出状态为0。
协处理
coprocess(协处理)是在shell命令前面使用coproc保留字。协处理在子shell中以异步方式执行,就像命令使用"&"控制符结尾一样,使用一个双向管道在执行shell和协处理之间建立联系。协处理格式如下:
coproc [NAME] command [redirections]
这样创建了一个协处理被命名为"NAME"。如果没有指定"NAME",那么默认的名字是"COPROC"。如果"command"是要给简单命令,"NAME"不是必须要指定的;否则,将被解释为这个简单命令的第一个字。当coproc被执行,shell会在执行shell的上下文中创建一个名为"NAME"的数组变量。"command"的标准输出通过管道被连接到正在运行的shell的内部的一个文件描述符,那个文件描述符被假设为NAME[0]。"command"的标准输入通过管道被连接到正在运行的shell的一个文件描述符,那个文件描述符被假设为NAME[1]。这个管道在任何命令指定重定向之前就被建立了。文件描述符被用来作为参数提供给shell命令,并且重定向使用标准字展开。作为变量NAME_PID的值,shell开启的执行协处理的进程ID是可用的。wait内置命令被用作等待协处理进程终止。
协处理的返回状态是command的退出状态。
Shell函数定义
shell函数是一种对象。这种对象可以理解成是将一个或几个简单命令,或者是一系列带有新的可选参数的复合命令被事先定义在其中的,当调用此函数的时候,会执行函数中的所有命令。Shell函数定义方式如下:
[ function ] name () {compound-command} [redirection]
这样可以定义一个名为"name"的函数。保留字function是可选的。如果我们给出了这个function保留字,小括号就是可选的。函数的主体是复合命令。那些命令通常都包含在"{"和"}"之间的复合命令。将"name"被指作为一个简单命令运行的时候,整个复合命令也将被执行。当函数执行遭拒的时候,可以指定重定向。函数的退出状态是除非是语法错误或同名的只读函数已经存在,否则就是0。当函数被执行,函数的退出状态就是在函数体中最后那个被执行的命令的退出状态。
(待续...)
本文出自 “运维者的家” 博客,请务必保留此出处http://zhaotianyu.blog.51cto.com/132212/1785619
Linux操作系统基础解析之(七)——Bash(Shell)基础知识(2)
原文地址:http://zhaotianyu.blog.51cto.com/132212/1785619