Shell 详解

[TOC]
——主要来自《Red Hat Linux指南:基础与系统管理篇》《Linux命令行与shell脚本编程大全.第3版》mysql

一、shell

要点:命令行、标准输入和标准输出、重定向、管道、后台运行程序、kill:终止后台做业、文件名生成/路径名展开、内置命令
Shell是用户的系统界面,提供了用户与内核进行交互操做的一种接口。它接收用户输入的命令并把它送去内核执行。实际上Shell是一个命令解释器,它解释由用户输入的命令而且把它们送到内核。linux

1.1 命令行

当在命令提示符后键入命令回车,shell将执行相应的程序。好比,键入ls后回车,shell开始执行名为ls的工具。也可以让shell以一样的方式执行其余类型的程序,如shell脚本、应用程序或本身编写的程序。包含命令和参数的行称为命令行。命令是指在命令行上键入的字符,同时还指对应动做所调用的程序。正则表达式

1.1.1 语法

命令行语法说明了行中各个元素的排列顺序和间隔方式。当用户键入命令回车后,shell将扫描命令行进行语法检查。命令行上基本的语法格式以下:
command [arg1] ... [argn] RETURN
命令行上采用一个或多个空格来隔开每一个元素。其中,command为命令的名字,arg1到argn为命令的参数,回车是终止命令的按键。语法格式中的方括号代表被括起来的参数为可选项。并非全部的命令都须要参数,有些命令就没有参数,有些命令须要可变数目的参数,有些命令则须要特定数目的参数。选项是一种特殊类型的参数,前面一般为一个或两个连字符(“-”或“--”)。sql

1.命令名

一些有用的Linux命令行仅由命令名组成而不带任何参数。例如,不带任何参数的ls将显示工做目录下的文件列表。多数命令都带一个或多个参数,当使用须要带参数的命令时,若没有带参数,或带了不正确的参数,或参数数目使用错误,系统都会返回用户简短错误信息提示,这些信息称为命令的用法消息(usage message)。shell

2.参数

命令行上,每一串不含空格字符的字符序列称为记号或字。参数是一种记号,如文件名、文本串、数字或命令处理的其余对象。
下面展现一个cp的命令行:apache

[root@QUFENGBIN ~]# cp temp tempcopy
参数都有编号,其中命令自己做为参数0,它是命令行参数的开始。在这个例子中,cp为参数0,temp为参数1,tempcopy为参数2。cp至少须要两个字段(还可带多个参数,但不能少于两个),参数1是已存在的文件名,参数2是cp要建立或重写的文件。这两个参数都不是可选的,而是命令运行所必需的。
PS:shell一些关于参数的特殊字符
特殊字符 说明
$$ Shell自己的PID(ProcessID)
$! Shell最后运行的后台Process的PID
$? 最后运行的命令的结束代码(返回值)
$- 使用Set命令设定的Flag一览
$* 全部参数列表。如"$*"用「"」括起来的状况、以"$1 $2 … $n"的形式输出全部参数。
$@ 全部参数列表。如"$@"用「"」括起来的状况、以"$1" "$2" … "$n" 的形式输出全部参数。
$# 添加到Shell的参数个数
$0 Shell自己的文件名
$1~$n 添加到Shell的各参数值。$1是第1参数、$2是第2参数…。
[root@aminglinux_01 test]# cat test.sh
#!/bin/sh
echo "number:$#"
echo "scname:$0"
echo "first :$1"
echo "second:$2"
echo "argume:$@"
[root@aminglinux_01 test]# sh test.sh aa bb
number:2
scname:test.sh
first :aa
second:bb
argume:aa bb

3.选项

选项(option)是改变命令执行效果的参数。可经过指定多个选项使得命令按照不一样的方式执行。选项与特定的程序相关,并由命令行上调用的程序解释,而非由shell解释。
按照约定,选项是跟在命令以后其余参数(如文件名)以前的单独的参数。多数命令的选项前面须要加一个连字符,但这个要求是与工具相关的,而与shell无关。GNU程序的选项前一般带两个连字符。例如,--help会生成用法消息。编程

4.选项的合并

当须要多个选项时,可将多个单字符选项组合成一个参数,以一个连字符开始,在选项前不要加空格。可是,这样合并后的选项以前不能使用两个连字符。对于合并选项的具体规则与具体的运行程序有关。多数命令的选项不分先后顺序。数组

5.选项的参数

有些工具的选项自己也要带参数。如,gcc(GUN的c编译器)的-o选项必须后跟gcc产生的可执行文件名,一般选项与其参数间用空格隔开,以下:bash

[root@QUFENGBIN ~]# gcc -o prog prog.c

6.以连字符开始的参数

按照约定,工具的参数(如文件名)是容许以连字符开始的。这样当某个文件的名字为-l时,命令的意义将不明确。若是建立了这类文件,那么,一些命令约定使用--(两个连续的连字符)参数来表示选项的结束(和参数的开始)。以下示例:app

[root@QUFENGBIN test]# touch -l
touch: invalid option -- 'l'
Try 'touch --help' for more information.
[root@QUFENGBIN test]# touch -- -l
[root@QUFENGBIN test]# ls -l
total 4
-rw-r--r-- 1 root root  0 Apr 28 11:41 -l
-rw-r--r-- 1 root root 14 Apr 26 16:26 test01
[root@QUFENGBIN test]# ls -- -l
-l
[root@QUFENGBIN test]# ls -l -- -l
-rw-r--r-- 1 root root 0 Apr 28 11:41 -l

1.1.2 处理命令行

当向命令行键入命令时,Linux的tty设备驱动程序(Linux操做系统内核的一部分)将检查每一个字符,来肯定是否要当即采起动做。当键入的字符不须要采起当即的动做时,设备驱动程序将把字符存储在缓冲区中,等待字符输入。当按下回车键后,设备驱动程序将把命令行传递过程shell处理。

1.分析命令行

当shell处理命令行时,它将把命令行做为一个总体来对待,并将其分红几个组成部分。接着,shell将查找命令的名称。命令行中提示符后的第1项(即参数0)一般为命令名,所以shell将把命令行中从第1个字符到第一个空白字符(TAB或空格)之间的字符串做为命令名。命令名(第1个记号)可采用简单文件名或路径名的方式指定。例如:

[root@QUFENGBIN test]# ls
-l  test01
[root@QUFENGBIN test]# /bin/ls
-l  test01

2.绝对路径名与相对路径名

当在命令行上输入绝对路径名或非简单文件名的相对路径名时(即输入至少包含一条斜杠的路径名),shell将在指定目录下查找具备执行权限的对应文件。例如,输入命令/bin/ls,shell将查找/bin目录下具备执行权限且名为ls的文件。当输入的是一个简单文件名时,shell在一组目录中查找与该文件名匹配且具备执行权限的对应文件。shell并非在全部目录下搜索,而只在PATH变量设定的路径下搜索。

1.1.3 执行命令行

1.进程

若是shell找到了与命令行上的命令具备相同名字的可执行文件,那么,shell将启动一个新的进程(进程是指Linux命令的执行),并将命令行上的命令名、参数、选项传递给程序(可执行文件)。当命令执行时,shell将等待进程的结束,这时shell处于非活跃状态,称为休眠状态。当程序执行完毕,就将它的退出状态传递给shell,这样shell就返回到活跃状态(被唤醒),显示提示符,等待下一个命令的输入。

2.shell不处理的参数

因为shell不处理命令行上的参数,只是将它们传递给调用的程序,因此shell不知道选项和参数是否对程序有效。因此关于选项和参数的错误消息和用法消息都来自程序自身。也有些命令会忽略无效的选项。

1.2 标准输入输出

标准输出(standard output)是指程序输出信息(如文本)的地方。程序历来都不“知道”它发送到标准输出的信息究竟送往何处。这些信息能够输出到打印机、普通文件或屏幕。默认状况下,shell把命令的结果标准输出到屏幕。shell也能够将输出重定向到其余文件。
标准输入(standard input)是程序信息的来源。而对于标准输出,程序从不“知晓”信息的来源。默认状况下,程序的输入来自键盘输入。
对一个运行的程序来讲,除了具备标准输入和标准输出外,一般还有错误消息输出,称为标准错误输出(standard error)。
命令并不知道标准输入来自哪,也不知道标准输出和标准错误输出到哪。

1.2.1 做为文件的屏幕

除了普通文件、目录文件、硬连接和软连接以外,Linux还有一种文件类型:设备文件(device file)。设备文件驻留在Linux文件结构中(一般位于目录/dev中),用来表明外围设备,如终端模拟器、显示屏、打印机和硬盘驱动器。
在who工具显示的内容中,登陆名后的设备名即为屏幕的文件名。若是打开了多个窗口,每一个打开的窗口都有对应的设备名。在这些窗口中运行tty工具便可获得它们各自的名称。能够把这个设备文件看做是一个文本文件进行读写。向该文件写入会在屏幕上显示写入的内容,而从该文件读取就是从键盘上读取键入的内容。

[root@QUFENGBIN test]# tty
/dev/pts/0
[root@QUFENGBIN test]# who
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-28 11:34 (111.113.5.18)

chsh:改变登陆shell
系统管理员在创建用户帐户时,将肯定用户第一次登陆系统或打开GUI环境下终端模拟器窗口时使用的shell(见/etc/passwd文件)。

root:x:0:0:root:/root:/bin/bash
.... .....
mysql:x:1000:1000::/home/mysql:/bin/false
www:x:1001:1001::/home/www:/bin/false
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin

但在登陆系统后,用户能够本身决定运行哪一个shell。键入要使用的shell名(如bash,tcsh,或另外一个shell),而后回车,则出现的提示符即为新设定shell给出的,输入exit命令可退回到上一个shell。
使用工具chsh能够永久地修改登陆shell。首先输入命令chsh,而后在提示符后输入口令和要使用的shell的绝对路径名(如/bin/bash或者/bin/tcsh,或者另外一个shell的路径名)。

1.2.2 做为标准输入的键盘和做为标准输出的屏幕

当第一次登陆时,shell将其标准输出发送到表明屏幕的设备文件中,采用这种方式输出能够把输出内容在屏幕上显示出来。shell还将表明键盘的设备文件做为标准输入的来源,这样命令会把在键盘上键入的任何内容做为输入接收。

1.2.3 重定向

重定向(redirection)是指改变shell标准输入来源和标准输出去向的各类方式。例如,默认状况下,shell将cat的标准输入关联到键盘,标准输出关联到屏幕。但也可让shell重定向任何命令的标准输入或标注输出,方法就是将输入或输出与某命令或某文件关联,而再也不是与表明键盘或屏幕的设备文件进行关联。

1.重定向标准输出

经过重定向符号(>)能够将shell命令的输出重定向到指定的文件而再也不是屏幕。重定向的命令格式为:

command [arguments] >filename 

其中,command为可执行程序(如应用程序或者是工具),arguments是可选参数,filename是shell要重定向输出到的普通文件名。
重定向可能覆盖文件!在重定向命令执行前,若是该文件已经存在,那么shell将重写并覆盖其原来的内容。

2.重定向标准输入

与重定向标准输出同样,也能够重定向标准输入。经过重定向标准输入符号(<)可使shell将命令的输入重定向为来自指定的文件而再也不是键盘。重定向标准输入的命令格式为:

command [arguments] <filename

其中,command为可执行程序(如应用程序或者是工具),arguments是可选参数,filename是shell要重定向输入来自的普通文件名。
将文件或者标准输入做为输入的工具:将命令cat的输入重定向到文件的执行结果与命令cat后跟文件名做为参数的执行结果相同。像cat这样具备这种特性的工具属于Linux中的一类工具,这类工具还包括lpr、sort和grep。这类工具首先检测调用它们的命令行。若是命令行上存在文件名,那么这类工具就把指定的文件做为输入;不然,若是命令行上没有指定文件名,那么这类工具就把标准输入做为输入。这种功能特性是该类工具自身所具备的,而与shell或者是操做系统无关。

3.noclobber:避免文件的重写

shell提供了一种称为noclobber的特性,该特性可防止重定向时不经意地重写了已存在的文件。

[root@QUFENGBIN test]# set -o noclobber
[root@QUFENGBIN test]# echo "noclobber test" > test01
-bash: test01: cannot overwrite existing file
[root@QUFENGBIN test]# set +o noclobber
[root@QUFENGBIN test]# echo "noclobber test" > test01
[root@QUFENGBIN test]# cat test01
noclobber test

在重定向输出符号后跟管道符号,即便用符号组合“>|”能够忽略noclobber的设置。

[root@QUFENGBIN test]# set -o noclobber
[root@QUFENGBIN test]# echo "noclobber test" > test01
-bash: test01: cannot overwrite existing file
[root@QUFENGBIN test]# echo "noclobber test" >| test01
[root@QUFENGBIN test]# cat test01
noclobber test
[root@QUFENGBIN test]# set +o noclobber

4.向文件追加标准输出

使用追加输出符号(>>)能够向某个文件末尾添加新的内容,而且不改变原来已有内容。

[root@QUFENGBIN test]# cat test01
noclobber test
[root@QUFENGBIN test]# echo "noclobber test" > test01 ; cat test01
noclobber test
[root@QUFENGBIN test]# echo "noclobber test" >> test01 ; cat test01
noclobber test
noclobber test

5./dev/null:使数据消失

设备/dev/null是一个数据接收器(data sink),一般被称为位桶(bit bucket)。能够将不想看到或者是不想保存的数据重定向到/dev/null,这样数据将不留痕迹的消失。

[root@QUFENGBIN test]# echo "noclobber test" > /dev/null

当从/dev/null中读取数据时,将获得一个空字符串。

[root@QUFENGBIN test]# ls -l test01
-rw-r--r-- 1 root root 30 Apr 28 14:59 test01
[root@QUFENGBIN test]# cat /dev/null > test01
[root@QUFENGBIN test]# ls -l test01
-rw-r--r-- 1 root root 0 Apr 28 15:05 test01

1.2.4 管道

shell使用管道将一个命令的输出直接链接到另外一个命令的输入。管道(pipe,有时被称为pipeline)的功能实现相似于下面的过程:首先将一个命令的标准输出重定向到一个文件,而后将该文件做为另外一个命令的标准输入。管道不会单独处理每条命令,而且不须要中间文件。管道的符号为一条竖线(|),命令行语法格式为:

command_a [arguments] | command_b [arguments]

上面的命令行获得的结果与下面的这一组命令获得的结果相同:

command_a [arguments] > temp
command_b [arguments] < temp
rm remp

任何Linux工具均可以使用管道从命令行上指定的文件中接受输入,也能够从标准输入接受输入。
有些使用管道的命令仅从标准输入接受输入,如工具tr(transtate,转换)就只能从标准输入接受输入(即tr只能经过标准输入接受输入,而没法经过命令行参数来接受输入)。
使用tr的最简单格式:tr string1 string2
tr工具从标准输入接受输入,查找与string1匹配的字符,找到一个匹配就将string1的字符替换为string2中对应字符。tr工具将它的输出发送到标准输出。

[root@QUFENGBIN test]# tr abc ABC < test01
ABCdefgABCDEFG
[root@QUFENGBIN test]# cat test01 | tr abc ABC
ABCdefgABCDEFG
[root@QUFENGBIN test]# tr abc ABC test01
tr: extra operand ‘test01’
Try 'tr --help' for more information.

1.过滤器
过滤器(filter)是将输入数据流处理后在输出数据流的一类命令。包含过滤器的命令行用一个管道将某个命令的标准输出链接到过滤器的标准输入,用另外一个管道将过滤器的标准输出链接到另外一个命令的标准输入。并非全部的工具均可以用做过滤器。
2.tee:向两个方向输入
tee工具将标准输入复制到文件和标准输出。该工具被命名为tee是由于:它只有一个输入,但输出到两个方向。

[root@QUFENGBIN test]# who | tee who.out | grep root
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-28 14:50 (111.113.5.18)
[root@QUFENGBIN test]# cat who.out
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-28 14:50 (111.113.5.18)

1.3 在后台运行程序

前台:当在前台运行命令时,shell将一直等到命令执行完毕。才会给出提示符使得你可继续输入下一个命令。当命令在后台运行时,就没必要等待该命令完成,可直接输入另外一个命令。
做业:做业(job)是由一个或者(可经过管道链接的)多个命令组成的序列。前台只能有一个做业位于窗口或屏幕中,但能够有多个做业在后台运行。同一时间运行多个做业是Linux的重要特性,这常被称为多任务特性。
做业编号与PID号:若是在命令行的末尾输入与符号(&)后回车,那么shell将在后台运行这个做业。同时,shell会给这个做业分配一个做业编号(是个小数字),并将其显示在方括号内。在做业编号以后,shell将显示进程标识(process identification,PID)号,该号是由操做系统分配的一个大数。每一个大数后面都标识了后台运行的一条命令。而后,shell将显示另外一个提示符,这是即可以键入另外一个命令。当后台做业运行结束时,shell将显示一个消息,这个消息的内容为:已结束做业的做业编号和运行该做业的命令行。

1.将做业从前台移至后台

CONTROL+Z:程序挂起键,shell把前台的做业挂起(阻止其继续运行),并终止做业中的进程,将进程的标准输入与键盘隔开。用bg命令后跟做业编号能够把挂起的做业放到后台执行。若是仅有一个做业被挂起。那么能够没必要指明做业编号。只有前台做业能够从键盘得到输入。为了将键盘和后台某个正运行的做业链接起来,必须把该后台做业移至前台。用fg命令后跟做业编号能够把后台的做业移至前台。不带任何参数的fg命令能够将后台惟一的做业移至前台。

2.kill:终止后台做业

命令行上输入kill后跟进程的PID号(或者后跟%和做业编号),能够将后台正在运行的进程(或做业)终止,使用中断键(一般CONTROL+C)是不能实现其功能的。

1.4 文件名生成/路径名展开

通配符和通配:当输入包含特殊字符(也称为元字符)的部分文件名时,shell能够生成与已有文件的名字匹配的文件名。这些特殊的字符也经常被称为通配符(wildcard)。当某个特殊字符做为参数出如今命令行上时,shell将该参数扩展为有序的文件名列表,并将列表传递给命令行上调用的程序。包含特殊字符的文件名称为模糊文件引用(ambiguous file reference),由于它们不与任何一个特定文件相关联。对这些文件名操做的过程称为路径名展开(path expansion)或者通配(globbing)。

1.4.1 特殊字符?

问号(?)是shell生成文件名的特殊字符,它与已有文件名中的某个单独字符匹配。

[root@QUFENGBIN test]# ls test0?
test01
[root@QUFENGBIN test]# echo wh?.out
who.out
[root@QUFENGBIN test]# cat wh?.out
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-28 14:50 (111.113.5.18)

1.4.2 特殊字符*

星号(*)的功能与问号的相似,不一样之处在于,星号能够跟文件名中的任意多个(包括0个)字符匹配。

1.4.3 特殊字符[]

用方括号将一个字符列表括起来使得shell与包含列表中每一个单独字符的文件名进行匹配。例如,test?可匹配test后跟任何一个字符的文件名,而方括号更严格些,test[01]仅与test0、test1匹配。这里,方括号定义了一个字符类(character class),该类由括号内的全部字符组成。
每一个定义字符类只能替换文件名中的一个字符。方括号和其中的内容合起来的功能就如同问号同样,可是只能用字符类中的一个成员替换。
ps:左方括号后直接跟叹号(!)或脱字符(^)也能够定义字符类,该类与任何不在方括号内的字符匹配。例如,[^ab]*与不以a或b开始的文件名匹配。

[root@QUFENGBIN test]# ls
aa  ab  ac  ad  ba  bb  bc  bd  cc  dd
[root@QUFENGBIN test]# ls *[^ab]
ac  ad  bc  bd  cc  dd
[root@QUFENGBIN test]# ls [b-d]*
ba  bb  bc  bd  cc  dd

下面的例子表示ls工具不能翻译模糊文件引用。第一个ls命令带有参数aa*,shell将其扩展为匹配的文件名aa,并将该名字传递给ls。第二个命令将*转义,shell再也不将*看做特殊字符,并将其传递给ls,ls报错。大多数工具和程序像ls同样也不能解释模糊文件引用,该解释工做由shell完成。

[root@QUFENGBIN test]# ls aa*
aa
[root@QUFENGBIN test]# ls aa\*
ls: cannot access aa*: No such file or directory
[root@QUFENGBIN test]# ls aaa*
ls: cannot access aaa*: No such file or directory

注意:模糊文件引用由shell进行扩展,而不是shell调用的程序进行扩展。上面的例子都不能“看到”模糊文件引用。shell对模糊文件引用进行扩展,并将扩展获得的文件列表传递给工具。在下面的echo例子中验证了这一点,由于它显示了参数而不是模糊文件引用。

[root@QUFENGBIN test]# ls
aa  ab  ac  ad  ba  bb  bc  bd  cc  dd
[root@QUFENGBIN test]# echo a?
aa ab ac ad
[root@QUFENGBIN test]# echo *
aa ab ac ad ba bb bc bd cc dd
[root@QUFENGBIN test]# echo a*
aa ab ac ad
[root@QUFENGBIN test]# echo .*
. ..
[root@QUFENGBIN test]# echo [a-m]*
aa ab ac ad ba bb bc bd cc dd
[root@QUFENGBIN test]# echo [x-z]*
[x-z]*
[root@QUFENGBIN test]# echo *[a-d]
aa ab ac ad ba bb bc bd cc dd
[root@QUFENGBIN test]# echo *[x-z]
*[x-z]
[root@QUFENGBIN test]# echo [a-d]\*
[a-d]*
[root@QUFENGBIN test]# echo [x-z]\*
[x-z]*

1.5 内置命令(内建命令)

外部命令
外部命令,有时候也被称为文件系统命令,是存在于bash shell以外的程序。它们并非shell程序的一部分。外部命令程序一般位于/bin、/usr/bin、/sbin或/usr/sbin中。ps是一个外部命令,你可使用which和type命令找到它。
04cfde2995914294fb7e96b6d702a7d9.png
当外部命令执行时,会建立出一个子进程。这种操做被称为衍生(forking)。
内置命令(内建命令)
内建命令和外部命令的区别在于前者不须要使用子进程来执行。它们已经和shell编译成了一体,做为shell工具的组成部分存在。不须要借助外部程序文件来运行。能够利用 type 命令来了解某个命令是不是内建的。每一个shell都有本身的内置命令集合。
输入命令“man bash”,再输入命令“/^SHELL BUILTIN COMMANDS”,shell将在内置命令部分搜索始于SHELL的行,从而能够查看内置命令的man页内容。

二、Bourne Again Shell

要点:初始化文件、重定向标准错误输出、编写简单的shell脚本、做业控制、操做目录栈、参数和变量、进程、命令历史机制、从新执行和编辑命令、别名、函数、控制bash特性和选项、处理命令行
Bourne Again Shell是一种命令解释器,同时也是一种高级编程语言。做为命令解释器,它们经过提示符响应并处理用户在命令行界面上输入的命令。而做为一种编程语言,它们将处理存放在所谓shell脚本文件中的命令。

2.1 shell基础

内容包括编写和使用初始化文件、重定向标准错误输出、编写和执行简单的shell脚本、命令分割和分组、实现做业控制和操做目录栈。

2.1.1 初始化文件

当启动shell时,它将运行初始化文件初始化本身。具体运行哪一个文件取决于该shell是一个登陆shell仍是一个非登陆shell的交互式shell(好比经过命令bash),又或者是一个非交互式shell(用来执行shell脚本)。要想运行初始化文件中的命令,用户必须具有读权限。

1.登陆shell

登陆shell原本就属于交互式shell。
/etc/profile:shell首先执行/etc/profile中的命令。经过设置这个文件,超级用户能够为全系统内的全部bash用户创建默认特征。
.bash_profile、.bash_login和.profile:而后shell依次查找~/.bash_profile、~/.bash_login和~/.profile,并执行它找到的首个文件中的命令。能够将命令放置在这些文件中的某个里以覆盖掉/etc/profile文件中的默认设置。
.bash_logout:当用户注销时,bash执行文件~/.bash_logout中的命令。这个文件包含了退出会话时须要执行的清理任务(好比删除临时文件)经常使用到的命令。

2.交互式非登陆shell

在交互式非登陆shell中并不执行前面提到的初始化文件中的命令。然而,交互式非登陆shell从登陆shell继承了由这些初始化文件设置的shell变量。
/etc/bashrc:尽管不是经过bash直接调用,许多~/.bashrc文件仍是调用/etc/bashrc。这种安排使得超级用户能够为全系统内的非登陆bash shell创建默认特性。
.bashrc:交互式非登陆shell执行~/.bashrc文件中的命令,而登陆shell的初始化文件(好比.bash_profile)一般会运行这个文件。这样,登陆shell和非登陆shell均可以使用.bashrc中的命令。

3.非交互式shell

非交互式shell(如那些运行shell脚本的shell)并不执行前面描述的初始化文件中的命令。然而,这些shell从登陆shell那里继承了由这些初始化文件设置的shell变量。
BASH_ENV:非交互式shell查找环境变量BASH_ENV(或者当shell做为sh调用时为ENV),并执行由该变量命名的文件中的命令。

4.创建初始化文件

尽管有不少种初始化文件和shell,可是用户一般只须要主目录下的.bash_profile和.bashrc文件。.bash_profile中经过如下命令将为登陆shell执行.bashrc(若是该文件存在)中的命令。

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

5..(句点)或者source:在当前shell中运行初始化文件

在编辑诸如.bashrc这类的初始化文件以后,要使这些修改起做用,用户没有必要注销而后再次登陆,可使用内置命令“.”(句点)或者source(这是两个相同的命令)。与其余命令同样,在命令行上,“.”后面必须有一个空格。内置命令“.”或者source用起来相似于运行一个shell脚本,可是这些命令将该脚本做为当前进程的一部分运行。所以,当使用“.”或者source运行脚本的时候,在脚本中改变的变量也将影响到运行该脚本的shell。可使用“.”或者source命令来运行任何shell脚本,而不只仅是初始化文件,可是可能会带来反作用(好比可能修改用户依赖的shell变量的值)。若是将初始化文件做为常规shell脚本运行,而且不使用“.”或source内置命令,那么启动脚本中建立的变量将只在运行该脚本的子shell中起做用。

2.1.2 符号命令

Bourne Again Shell以多种方式使用符号(、)、[、]和$。为了不混淆,下表列出了每种符号最通用的用法。
符号 命令
() 子shell
$() 命令替换
(()) 算术表达式计算,let的同义词(当被括起来的值中包含等号时使用)
$(()) 算术展开(不用于被括起来的值中包含等号的情形)
[] test命令
[[]] 条件表达式,相似于[],可是添加了字符串比较

2.1.3 重定向标准错误输出

除了标准输出以外,命令还能够将输出发送到标准错误输出(standard error)。命令将错误消息发送到标准错误输出,这样就能够避免与发送到标准输出的信息混淆在一块儿。
与处理标准输出同样,默认状况下,shell 将命令的标准错误输出发送到屏幕上。除非将标准输出和标准错误输出中的某一个重定向,不然不能区分命令的输出究竟是标准输出仍是标准错误输出
文件描述符:文件描述符(file descriptor)是程序发送输出和获取输入的地方。当执行一个程序时,运行该程序的进程打开了 3 个文件描述符,分别是:0(标准输入)、1(标准输出)和 2(标准错误输出)重定向标准输出符号(>)是 1> 的简写,它通知 shell 将标准输出重定向。相似地,< 是 0< 的简写,表示将标准输入重定向。符号 2> 将标准错误输出重定向
下面是个例子:
当运行 cat 时,若是所带参数中的某个文件不存在,而另外一个文件存在,那么 cat 将发送一条错误消息到标准错误输出,同时还将已存在的那个文件复制一份到标准输出。除非将它们重定向,不然两条消息都将出如今屏幕上。

[root@QUFENGBIN test]# cat y
This is y
[root@QUFENGBIN test]# cat x
cat: x: No such file or directory
[root@QUFENGBIN test]# cat x y
cat: x: No such file or directory
This is y

将命令的标准输出重定向时,发送到标准错误输出的输出结果将不受影响,仍然出如今屏幕上。

[root@QUFENGBIN test]# cat x y > hold
cat: x: No such file or directory
[root@QUFENGBIN test]# cat hold
This is y
[root@QUFENGBIN test]# cat x y 1> hold1 2>hold2
[root@QUFENGBIN test]# cat hold1
This is y
[root@QUFENGBIN test]# cat hold2
cat: x: No such file or directory

复制文件描述符:在下一个例子中,1> 将标准输出重定向到文件hold。而后,2>&1 声明文件描述符 2 为文件描述符 1 的副本。结果是, 标准输出和标准错误输出均被重定向到文件hold中。

root@QUFENGBIN test]# cat x y 1>hold 2>&1
[root@QUFENGBIN test]# cat hold
cat: x: No such file or directory
This is y

在上面这个示例中,1>hold 放在了 2>&1 的前面。若是将它们的顺序颠倒的话,在标准输出重定向到文件 hold 以前,标准错误输出就已经拷贝了标准输出的一个副本。这样一来,就只有标准输出被重定向到文件hold。

[root@QUFENGBIN test]# cat x y 2>&1 1>hold
cat: x: No such file or directory
[root@QUFENGBIN test]# cat hold
This is y
[root@QUFENGBIN test]# cat x y 2>&1
cat: x: No such file or directory
This is y

Bourne Again Shell所支持的重定向操做符以下表。

操做符 含义
<filename 将标准输入重定向为文件 filename
>filename 除非文件 filename 已存在而且设置了 noclobber 标记,不然标准输出将被重定向到文件 filename 。若是文件 filename 不存在且没有设置 noclobber 标记,那么重定向操做将建立该文件
>|filename 即便文件 filename 已存在而且设置了 noclobber 标记,仍将标准输出重定向到该文件
>>filename 除非文件 filename 已存在而且设置了 noclobber 标记,不然标准输出将被重定向到文件 filename,并将内容添加到原文件的末尾。若是文件 filename 不存在且没有设置 noclobber 标记,那么将建立该文件
<&m 从文件描述符m复制标准输入
[n]>&m 从文件描述符m复制标准输出或者文件描述符n(若是命令中指定了n)
[n]<&- 关闭标准输入或者文件描述符n(若是指定了n)
[n]>&- 关闭标准输出或者文件描述符n(若是指定了n)

2.1.4 编写一个简单的shell脚本

shell 脚本是包含 shell 可执行命令的文件。shell 脚本中的命令能够是用户在 shell 提示符后面输入的任何命令。除了可使用用户在命令行下面输入的命令以外,shell 脚本还可使用控制流(control flow)命令(亦称为控制结构(control structure))。使用这组命令能够改变脚本中命令的执行顺序。

1.chmod:使文件可执行

任何用户想把文件名做为命令行执行,都必须具有执行访问权。若是该文件是一个 shel l脚本,用户尝试执行这个文件时,还必须具有读访问权限。而在执行一个二进制可执行文件(已编译程序)时,并不须要读访问权限。

[root@QUFENGBIN test]# ll whoson
-rw-r--r-- 1 root root 41 Apr 30 20:09 whoson
[root@QUFENGBIN test]# cat whoson
date
echo "User Currently Logged In"
who
[root@QUFENGBIN test]# whoson
-bash: whoson: command not found
[root@QUFENGBIN test]# ./whoson
-bash: ./whoson: Permission denied
[root@QUFENGBIN test]# chmod u+x whoson
[root@QUFENGBIN test]# ./whoson
Mon Apr 30 21:36:18 CST 2018
User Currently Logged In
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-30 11:55 (118.74.57.231)
[root@QUFENGBIN test]# whoson
-bash: whoson: command not found
[root@QUFENGBIN test]# PATH=$PATH:.
[root@QUFENGBIN test]# whoson
Mon Apr 30 21:36:53 CST 2018
User Currently Logged In
root     tty1         2018-04-17 15:44
root     pts/0        2018-04-30 11:55 (118.74.57.231)

2.#! 指定shell

在 shell 脚本文件的第一行能够放置一行特殊的字符串,告诉操做系统使用哪一个 shell 来执行这个文件。由于操做系统在试图 exec 文件以前检查该程序的开头字符串,这些字符让操做系统没必要进行失败的尝试。若是脚本的前两个字符是 #! ,那么系统将这两个字符后面的那些字符做为用来执行该脚本的命令解释器的绝对路径名。它能够是任何程序的路径名,而不只仅是 shell 。

3.#开始一行注释

4.执行 shell 脚本(fork、exec、source)

fork 和 exec 系统调用:用户在命令行上输入一条命令以后,shell 将 fork 一个新的进程,以建立当前 shell 进程的一个副本(子shell)。这个新的进程将试图 exec(execute,执行)该命令。与 fork 同样,exec 例程也是由操做系统执行(系统调用)。若是该命令是一个二进制可执行程序,好比编译好的 C 程序,那么 exec 执行成功,系统调用该可执行程序将新建立的子 shell 覆盖掉。而若是这个命令是一个 shell 脚本,exec 执行失败。当 exec 失败时,将会假设该命令是一个 shell 脚本,子 shell 将执行脚本中的命令。与登陆 shell 指望从命令行读取输入不一样,子 shell 从文件(shell 脚本)中获取输入。因此,若是不具有 shell 脚本文件的执行权限,那么,用户可使用 bash 命令来 exec 一个 shell 直接运行该脚本,这样就能够运行脚本中的命令。

5.fork、exec、source 区别

fork  ( ~/test/test01.sh) :若是 shell 中包含执行命令,那么子命令并不影响父级的命令,在子命令执行完后再执行父级命令。子级的环境变量不会影响到父级。
说明:fork 是最普通的, 就是直接在脚本里面用 ~/test/test01.sh 来调用 test01.sh 这个脚本。运行的时候开一个 sub-shell 执行调用的脚本,sub-shell 执行的时候, parent-shell 还在。sub-shell 执行完毕后返回 parent-shell.。sub-shell 从 parent-shell 继承环境变量,可是 sub-shell 中的环境变量不会带回 parent-shell。
exec (exec ~/test/test01.sh):执行子级的命令后,再也不执行父级命令。
说明:exec 与 fork不一样,不须要新开一个 sub-shell 来执行被调用的脚本。被调用的脚本与父脚本在同一个 shell 内执行。可是使用 exec 调用一个新脚本之后, 父脚本中 exec 行以后的内容就不会再执行了。这是 exec 和source 的区别。
source (source ~/test/test01.sh):执行子级命令后继续执行父级命令,同时子级设置的环境变量会影响到父级的环境变量。
说明:与 fork 的区别是不新开一个 sub-shell 来执行被调用的脚本,而是在同一个 shell 中执行。 因此被调用的脚本中声明的变量和环境变量,均可以在主脚本中获得和使用。

[root@QUFENGBIN test]# chmod u+x test01.sh
[root@QUFENGBIN test]# chmod u+x test02.sh
[root@QUFENGBIN test]# cat test01.sh
#!/bin/bash
A=B
echo "PID for test01.sh before exec/source/fork:$$"
export A
echo "test01.sh: \$A is $A"
case $1 in
        exec)
                echo "using exec…"
                exec ./test02.sh ;;
        source)
                echo "using source…"
                . ./test02.sh ;;
        *)
                echo "using fork by default…"
                ./test02.sh ;;
esac
echo "PID for test01.sh after exec/source/fork:$$"
echo "test01.sh: \$A is $A"
[root@QUFENGBIN test]# cat test02.sh
#!/bin/bash
echo "PID for test02.sh: $$"
echo "test02.sh get \$A=$A from test01.sh"
A=C
export A
echo "test02.sh: \$A is $A"
[root@QUFENGBIN test]# ./test01.sh
PID for test01.sh before exec/source/fork:1259
test01.sh: $A is B
using fork by default…
PID for test02.sh: 1260
test02.sh get $A=B from test01.sh
test02.sh: $A is C
PID for test01.sh after exec/source/fork:1259
test01.sh: $A is B
[root@QUFENGBIN test]# ./test01.sh exec
PID for test01.sh before exec/source/fork:1261
test01.sh: $A is B
using exec…
PID for test02.sh: 1261
test02.sh get $A=B from test01.sh
test02.sh: $A is C
[root@QUFENGBIN test]# ./test01.sh source
PID for test01.sh before exec/source/fork:1262
test01.sh: $A is B
using source…
PID for test02.sh: 1262
test02.sh get $A=B from test01.sh
test02.sh: $A is C
PID for test01.sh after exec/source/fork:1262
test01.sh: $A is C

2.2 参数和变量

什么是环境变量
bash shell 经过一个叫作环境变量(environment variable)的特性来存储有关 shell 会话和工做环境的信息(这也是它们被称为环境变量的信息)。这项特性容许你在内存中存储数据,一遍程序或 shell 在运行的脚本可以轻松的访问它们。在 bash shell 中,环境变量分为两类:全局变量、局部变量
全局环境变量
全局环境变量对于 shell 会话和全部生成的子 shell 都是可见的。局部变量则只对于建立它们的 shell 可见。Linux 系统在开始 bash 会话前就设置了一些全局环境变量。系统环境变量基本上都使用全大写字母,以区别于普通用户的环境变量。要查看全局环境变量,可以使用 env 命令或者 printenv 命令。
变量
在 shell 中,shell 参数(shell parameter)与用户可访问的某个值相关,有几种不一样的 shell 参数。参数的名字由字母、数字和下划线组成,常被称为 shell 变量(shell variable),或者简称为变量(variable)。变量名必须以字母或者下划线开头,而不能是数字。
用户建立的变量
用户命令或赋值的 shell 变量称为用户建立的变量(user-created variable)。用户能够在任什么时候候修改用户建立的变量的值,或者将其设置为只读。还能够将用户建立的变量变成全局的。全局变量(又称为环境变量)能够被任何shell和从最初shell建立的其它程序访问。这里有一个命名约定,即全局变量只使用大写字母,而其它变量则使用大小写混合命名
关键字变量
关键字 shell 变量(keyword shell variable,简称为关键字变量)对于 shell 而言,具备特殊的意义,它们的名字通常比较短并且有助于记忆。当用户启动 shell 的时候(好比登陆),shell 将从环境中继承几个关键字变量。HOME 和 PATH 就属于这样的变量。
位置参数和特殊参数
位置参数和特殊参数的名字并不像变量名。其中,大多数参数的名字都只由一个字符组成(好比一、?和#等),而且像其余全部变量同样,在引用它们时通常在其名字前面加上美圆符号(如$一、$?和$#)。这些参数的值反映了用户与 shell 交互的不一样方面。不管什么时候,用户输入的一行命令中的每一个参数都将成为位置参数(positional parameter)的值。用户使用位置参数能够访问命令行参数,在编写 shell 脚本时将用到这项功能。内置命令 set 能够用来对位置参数赋值。其余常常须要用到的 shell 脚本值,好比最后一次执行的命令名、命令行参数的个数以及最近执行命令的状态,这些值均保存在特殊参数(special parameter)中。用户不能对特殊参数赋值。

2.2.1 用户建立的变量/引用变量

[root@aminglinux_01 ~]# person=alex
[root@aminglinux_01 ~]# echo person
person
[root@aminglinux_01 ~]# echo $person
alex

命令echo $person显示变量person的值,而不是显示$person,这是由于不会将$person做为参数传递给echo。因为名字开头出现$,于是shell识别出这是一个变量的名字,并将变量的值代入,同时将该值传递给echo。内置命令echo显示该变量的值,而不是它的名字,然而echo毫不会知道用户调用它时使用了变量
引用$:若是将开头的$用单引号引发来,就能够阻止shell代入变量的值。双引号不能阻止代入(会把引号之间的全部字符代入,包括单引号),而单引号和反斜杠符号\均可以阻止代入。

[root@aminglinux_01 ~]# echo $person
alex
[root@aminglinux_01 ~]# echo "$person"
alex
[root@aminglinux_01 ~]# echo '$person'
$person
[root@aminglinux_01 ~]# echo \$person
$person
[root@aminglinux_01 ~]# echo "'$person'"
'alex'
[root@aminglinux_01 ~]# echo "'\$person'"
'$person'

空格符:双引号不能阻止变量替换,可是能够关闭大多数其余字符的特殊含义。

[root@aminglinux_01 ~]# person="alex and jenny"
[root@aminglinux_01 ~]# echo $person
alex and jenny
[root@aminglinux_01 ~]# person=alex and jenny
-bash: and: 未找到命令

当引用一个包含制表符和多个相连空格符的变量时,须要使用引号来保留这些空格。若是没有将该变量用引号引发来,在将其传递给工具以前,shell将把每一个空白字符构成的串压缩成单个空格符:

[root@aminglinux_01 ~]# person="alex    and     jenny"
[root@aminglinux_01 ~]# echo $person
alex and jenny
[root@aminglinux_01 ~]# echo "$person"
alex    and     jenny

赋值中的路径名展开:当引用一个包含未被引号引发来的特殊字符的变量时,全部 shell 均将这些字符解释为特殊字符。

[root@aminglinux_01 test]# ls
1.txt  2.txt  alex.1  alex.2
[root@aminglinux_01 test]# memo=alex*
[root@aminglinux_01 test]# echo $memo
alex.1 alex.2
[root@aminglinux_01 test]# echo "$memo"
alex*

PS:语法 $VARIABLE 是 ${VARIABLE} 的特殊情形,后者要更加通用,它将变量名用${}括起来。花括号将变量名隔离起来。将一个变量和一个字符串链接起来的时候,花括号是必要的:

[root@aminglinux_01 test]# PREF=counter
[root@aminglinux_01 test]# WAY=$PREFclock
[root@aminglinux_01 test]# FAKE=$PREFfeit
[root@aminglinux_01 test]# echo $WAY $FAKE

[root@aminglinux_01 test]# WAY=${PREF}clock
[root@aminglinux_01 test]# FAKE=${PREF}feit
[root@aminglinux_01 test]# echo $WAY $FAKE
counterclock counterfeit

Bourne Again Shell使用特殊的变量$一、$二、$3直到$9,经过位置参数引用命令行中的参数。若是须要引用第9个之后的参数,就必须使用花括号:${10}。命令的名字保存在$0中。
unset:删除变量

2.2.2 变量属性

1. readonly :使变量值不可更改

可使用内置命令readonly确保某个变量的值不被改变。

[root@aminglinux_01 test]# person=jenny
[root@aminglinux_01 test]# echo $person
jenny
[root@aminglinux_01 test]# readonly person
[root@aminglinux_01 test]# person=alex
-bash: person: 只读变量

当不带参数使用内置命令readonly时,它会显示全部只读变量的列表。

[root@aminglinux_01 test]# readonly
declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath"
declare -ir BASHPID
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -ir EUID="0"
declare -ir PPID="1018"
declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor"
declare -ir UID="0"
declare -r person="jenny"

2. declare 和 typeset :为变量赋予属性

内置命令 declare 和 typeset(这是同一个命令的两个名字)能够用来设置 shell 变量的属性和值。下表列出了5种属性。
属性 含义
-a 声明一个数组变量
-f 声明一个函数名变量
-i 声明一个整型变量
-r 声明变量为只读,也可以使用 readonly
-x 输出变量(设置为全局变量),也可用 export

列出变量属性:若是不带任何参数或者选项,那么内置命令 declare 将列出全部 shell 变量。不带任何参数运行 set 命令,也会获得一样结果。若是内置命令 declare 带有选项,可是没有变量名做为参数,那么该命令将列出全部具备指定属性集合的 shell 变量。

2.2.3 关键字变量

关键字变量既能够经过继承而来,也能够在 shell 启动时声明并初始化。

1. HOME : 用户主目录

默认状况下,用户主目录就是用户登陆以后的工做目录。当用户建立其帐号时,其主目录就已经建立下来了,这个文件名存放在 /etc/passwd 中。

[root@aminglinux_01 test]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

代字符(~):shell 使用 HOME 的值来展开路径名,该路径名使用简写形式(代字符 ~ )来表示用户的主目录

[root@aminglinux_01 test]# cd
[root@aminglinux_01 ~]# pwd
/root
[root@aminglinux_01 ~]# echo ~
/root

2. PATH :shell 查找程序的路径

在向 shell 中输入一个绝对路径名或者相对路径名,而不是一个简单的文件名做为命令时,shell 就会在指定的这个目录下,用指定文件名查找可执行文件。若是该路径名对应的文件不存在,shell 将会报告“ command not found ”(命令找不到)错误。若是指定文件存在,可是用户没有执行权限,或者是用户没有 shell 脚本的读权限和执行权限,shell 将报告“ Permission denied ”(权限禁止)错误。
若是使用简单的文件名做为命令,shell 将搜索某些目录,以查找用户想要执行的程序。shell 在几个目录中搜索文件,查找与该命令具备相同的名字、且用户具备执行权限(对于编译好的程序)或者具备读权限和执行权限(对于shell 脚本)的文件。shell 变量 PATH 控制着这些搜索路径。

[root@QUFENGBIN ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/mysql/bin:/usr/local/openssl/bin:/root/bin

3. MAIL :保存电子邮件的地方

变量 MAIL 包含了保存用户邮件的文件的路径名。保存用户邮件的文件就是该用户的 mailbox ,一般是 /var/spool/mail/name ,其中 name 是用户的登陆名。若是设置了 MAIL 可是没有设置 MAILPATH,那么邮件到达 MAIL 指定的文件时,shell 将提醒用户。
变量 MAILPATH 包含了一个用冒号隔开的文件名列表。若是设置了这个变量,那么当这个列表中的任何一个文件发生改变的时候(好比邮件到达时),shell 都将提醒用户。能够在该列表中任何一个文件名后面加上一个问号(?),问号后面跟着一条消息。若是有新邮件,shell 就会显示该消息。它取代了用户登陆系统时因有邮件而出现的“ you have mail ”消息。
变量 MAILCHECK 规定了shell以多大的额度(以秒计)检查新邮件。默认值是60秒。若是将该值设置为0,shell 将在显示每一个提示符以前检查新邮件。

[root@QUFENGBIN ~]# echo $MAIL
/var/spool/mail/root
[root@QUFENGBIN ~]# echo $MAILPATH

[root@QUFENGBIN ~]# echo $MAILCHECK
60

4. PS1 :用户主提示符

Bourne Again Shell 默认提示符是一个美圆符号($)。若是以 root 身份运行 bash ,那么提示符是 # 号。变量 PS1 保存了 shell 用来提示用户输入命令的提示符串。当用户修改 PS1 的值时,用户的提示符就会发生改变。

[root@QUFENGBIN ~]# echo $PS1
[\u@\h \W]\$
[root@QUFENGBIN ~]# PS1="[\u@\h \W \!]$ "
[root@QUFENGBIN ~ 7]$

PS1="[\u@\h \W !]$ "显示的提示符的格式为[user@host directory event]$,其中user是用户名,host为本机域名中第1个点号(.)以前的主机名,directory为工做目录的基名,event为当前命令的事件编号。

[root@QUFENGBIN ~ 11]$ PS1="\h \$"
QUFENGBIN $PS1="\@ \u $ "
10:45 AM root $ PS1="\$ "
$
下表给出了一些 PS1 符号
符号 在提示符中的显示
\$ 若是以root身份运行,就显示为#,不然就是$
\w 工做目录的路径名
\W 工做目录的基名
\! 当前事件(历史)编号
\d 按照“工做日/月/日期”格式显示的日期
\h 机器的主机名,不包括域名
\H 机器全名,包括域名
\u 当前用户的用户名
\@ 按照“12小时,AM/PM”格式显示的当前时间
\T 按照12小时制“HH:MM:SS”格式显示当前时间
\A 按照24小时制“HH:MM”格式显示当前时间
\t 按照24小时制“HH:MM:SS”格式显示当前时间

5. PS2 :用户次提示符

[root@QUFENGBIN ~]# echo $PS2
>
[root@QUFENGBIN ~ 17]$ echo 'hello world
> !!!'
hello world
!!!
[root@QUFENGBIN ~ 18]$ PS2="next: "
[root@QUFENGBIN ~ 19]$ echo "hello world
next: 2"
hello world
2

6. PS3 :菜单提示符、PS4 :调试提示符

[root@QUFENGBIN ~]# echo $PS3

[root@QUFENGBIN ~]# echo $PS4
+

7. IFS :分隔输入字段(分词)

IFS(Internet Field Separator,内部字段分隔符)shell变量指定了在命令行中用来分隔参数的字符,其默认值为空格符、制表符和换行符不管IFS的值是什么,用户总可使用一个或者多个空格符或者制表符分隔命令行中的不一样参数,这里假设这些字符并无被引用或者转义。     
当为IFS指派字符值的时候,这些字符也能够分隔字段,可是只有在展开的时候才能够这样。这种命令行解释方式称为分词(word splitting)。

[root@QUFENGBIN ~]# a=x:y:z
[root@QUFENGBIN ~]# cat $a
cat: x:y:z: No such file or directory
[root@QUFENGBIN ~]# IFS=":"
[root@QUFENGBIN ~]# cat $a
cat: x: No such file or directory
cat: y: No such file or directory
cat: z: No such file or directory

shell 根据在 IFS 中发现的分隔符划分命令行中全部被展开的词。若是不进行展开,就不会进行分词。

[root@QUFENGBIN ~]# IFS="p"
[root@QUFENGBIN ~]# export VAR
[root@QUFENGBIN ~]# aa=export
[root@QUFENGBIN ~]# echo $aa
ex ort

尽管连续出现的多个空格或制表符被视为一个分隔符,可是只要出现别的字段分隔符字段,那么每一次出现就被视为一个分隔符。

8. CDPATH :扩大cd的范围

使用 CDPATH 变量,用户能够用一个简单的文件名做为参数传递给内置命令 cd ,就将工做目录改变到某个目录,而这个目录并非工做目录的子目录。
若是没有设置 CDPATH,而只是指定了一个简单的文件名做为调用 cd 的参数,那么 cd 将在工做目录中查找具备与该参数相同的文件名的子目录。而若是设置了 CDPATH,cd 将在 CDPATH 列表中的目录搜索具备与该参数相同的文件名的子目录。若是 cd 找到了一个子目录,那么该目录就成为工做目录。
若是 CDPATH 中没有包含工做目录,而且在 CDPATH 中均搜索失败,那么 cd 将在工做目录下搜索。若是但愿 cd 首先搜索工做目录,那么可将空字符串做为 CDPATH 的第1项。空字符串用两个冒号(::)表示。若是内置命令cd 的参数是一个绝对路径——以斜杠(/)开头——则 shell 不考虑 CDPATH 。

2.2.4 shell特殊变量

2.3 特殊字符(待补充)

字符 用途 字符 用途
换行符 启动命令的执行 ; 分隔命令
() 将命令分组给子shell执行,或者是标示函数 & 在后台执行命令
| 管道 > 重定向标准输出
>> 追加标准输出 < 重定向标准输入
<< Here文档 * 模糊文件引用中的零个或者多个字符组成的串
? 模糊文件引用中的任何单个字符 \ 引用后面的字符
' 引用字符串,阻止全部替换 " 引用字符串,只容许变量替换和命令替换
`...` 执行命令替换 [] 模糊文件引用中的字符类别
$ 引用某个变量 .(内置句点) 执行命令(只在行首)
# 开始一行注释 {} 用来封装函数体
;(内置空串) 返回true &&(布尔“与”) 只有左边的命令成功(返回的退出状态值为0)才执行右边的命令
||(布尔“或”) 只有左边的命令失败(返回非零退出状态值)才执行右边的命令 !(布尔“非”) 反转命令的退出状态值
$() 执行命令替换(优先形式) [] 计算算术表达式的值

2.4 进程

进程(process)是一条命令在Linux上的执行。用户登陆时启动的shell与其余同样,也是一条命令,或者进程。当用户在命令行中输入一个Linux工具名时,就启动了一个进程。当用户运行一个shell脚本的时候,系统建立另外一个shell进程,并为脚本的每行命令建立另外的进程。根据用户调用脚本方式的不一样,脚本既能够由当前shell运行,也可由当前shell的一个子shell运行。后一种方式更加广泛。若是用户输入一个shell内置命令如cd,此时系统并不会建立进程。

2.4.1 进程结构

fork系统调用:进程结构相似于文件结构,它也是一个层次式结构,有父进程、子进程甚至根进程(root)。父进程能够建立(fork)子进程,子进程又能够建立其余进程。术语fork,就像道路中的岔道口同样,将一个进程变成两个。建立一个新的进程的系统操做例程(或者系统调用)叫作fork。
当系统启动时,Linux开始执行,它首先启动init进程。这个进程称为自发进程(spontaneous process),其PID编号为1。这个进程在进程结构中的地位与文件结构中根目录的地位相同:它是系统进程和全部用户进程的祖先。

2.4.2 shell的父子关系

用于登陆某个虚拟控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一个父shell。在CLI提示符后输入 /bin/bash 命令或其余等效的 bash 命令时,会建立一个新的shell程序。这个shell程序被称为子shell(child shell)。子shell也拥有CLI提示符,一样会等待命令输入。运行shell脚本也可以建立出子shell。
2aa93f97e4a20a6f634d8b4d1e971e66.png
171bfb4c6388e18ceda240c76f703c06.png

2.4.3 进程列表

能够在一行中指定要依次运行的一系列命令。这能够经过命令列表来实现,只须要在命令之间加入分号(;)便可。
e0993c7c87f0f3662e323041591a8b96.png
在上面的例子中,全部的命令依次执行,不存在任何问题。不过这并非进程列表。命令列表要想成为进程列表,这些命令必须包含在括号里。
尽管多出来的括号看起来没有什么太大的不一样,但起到的效果确是非同寻常。括号的加入使命令列表变成了进程列表,生成了一个子shell来执行对应的命令。
说明 进程列表是一种命令分组(command grouping)。另外一种命令分组是将命令放入花括号中,并在命令列表尾部加上分号(;)。语法为 { command; } 。使用花括号进行命令分组并不会像进程列表那样建立出子shell。
要想知道是否生成了子shell,得借助一个使用了环境变量的命令。这个命令就是 echo $BASH_SUBSHELL 。若是该命令返回 0 ,就代表没有子shell。若是返回1 或者其余更大的数字,就代表存在子shell。
90e874d0d1ca76de59b7914c23d0b060.png

2.5 命令历史机制

历史机制是一项对C shell的改造功能,它维护了用户最近发出的命令行(亦称为事件)的列表。内置命令history显示了历史列表的内容

2.5.1 控制历史机制的变量

HISTSIZE变量的值决定了在某次会话期间历史列表保存的事件数目。该值的范围正常状况下是100到1000。
当从shell中退出时,最近执行的命令将保存在HISTFILE变量指定的文件中(默认是~/.bash_history)。下一次启动shell时,将用这个文件来初始化历史列表。变量HISTFILESIZE的值决定了保存在HISTFILE中的历史行数(不必定与HISTSIZE相同)。HISTSIZE保存了会话期间记忆的事件数目,HISTFILESIZE则保存了会话之间记忆的数目,HISTFILE指定了保存历史列表的文件。

[root@aminglinux_01 ~]# echo $HISTSIZE      #在一次会话期间保存的最大事件数目
1000
[root@aminglinux_01 ~]# echo $HISTFILE      #历史文件的位置
/root/.bash_history
[root@aminglinux_01 ~]# echo $HISTFILESIZE  #会话之间保存的事件最大数目
1000

事件编号:Bourne Again Shell连续地为每行命令指派了一个事件编号(event number)。若是在PS1中包含“!”,则能够将事件编号做为bash提示符的一部分显示出来。

[root@aminglinux_01 ~]# echo $PS1
[\u@\h \W]\$
[root@aminglinux_01 ~]# PS1="[\u@\h \W \!]\$"
[root@aminglinux_01 ~ 193]$

输入history命令能够显示历史列表中的事件。事件列表按照最先的事件排在列表顶部的顺序排列。

[root@aminglinux_01 ~ 194]$history | tail
  185  info bash builtin
  186  info bash
  187  man bash
  188  echo $HISTSIZE
  189  echo $HISTFILE
  190  echo $HISTFILESIZE
  191  echo $PS1
  192  PS1="[\u@\h \W \!]\$"
  193  history
  194  history | tail

2.5.2 从新执行和编辑命令

能够从新执行历史列表中的任何事件。能够采用3种方式来浏览、修改和从新执行前面已执行的命令,即便用内置命令fc、使用感叹号(!)命令以及Readline库(暂不介绍),该库使用vi编辑器来编辑和执行事件。

1. fc:显示、编辑和从新执行命令

2. 使用感叹号(!)引用事件

!!命令从新执行前一个事件,!\$符号表示前一个命令行中最后一个字。可使用事件的绝对编号、相对编号或者事件中包含的文原本引用该事件。全部的事件引用(称为事件标志符)均以感叹号(!)开头。感叹号后面的一个或多个字符指定某个事件。
事件标志符:事件标志符指定了命令历史中的某条命令。以下表:
标志符 含义
! 除非后面紧跟着空格符、换行符、=或者(,不然当即开始某个历史事件
!! 前一个命令
!n 历史列表中编号为n的命令
!-n 往前第n条命令
!string 最近以string开头的命令行
!?string[?] 包含string的最近命令行,最后的?是可选的
!# 当前命令(目前敲入的部分)
!{event} event为一个事件标示符。花括号将event与左右的文本隔开。好比,!{-3}3 表示后面跟着3的第3个最近执行的命令
[root@aminglinux_01 ~ 205]$ls -l 3.txt
-rw-r--r--. 1 root root 25 5月   7 13:18 3.txt
[root@aminglinux_01 ~ 206]$!!
ls -l 3.txt
-rw-r--r--. 1 root root 25 5月   7 13:18 3.txt
[root@aminglinux_01 ~ 206]$!205
ls -l 3.txt
-rw-r--r--. 1 root root 25 5月   7 13:18 3.txt
[root@aminglinux_01 ~ 206]$!-1
ls -l 3.txt
-rw-r--r--. 1 root root 25 5月   7 13:18 3.txt
[root@aminglinux_01 ~ 206]$history 10
  197  fc -l
  198  type -a fc
  199  ll /usr/bin/fc
  200  type -a fc
  201  history | tail
  202  history
  203  history | tail
  204  ll
  205  ls -l 3.txt
  206  history 10
[root@aminglinux_01 ~ 207]$!l
ls -l 3.txt
-rw-r--r--. 1 root root 25 5月   7 13:18 3.txt
[root@aminglinux_01 ~ 208]$!?ll?
ll
总用量 8
-rw-r--r--. 1 root root   25 5月   7 13:18 3.txt
-rw-------. 1 root root 1418 4月  14 21:55 anaconda-ks.cfg
drwxr-xr-x. 2 root root   60 5月   7 15:45 test

3. 字标志符

字标志符(word designator)指定了事件中的某个字或一组字。以下表:
字标志符 含义
n 第n个字。通常状况下,字0就是命令名
^ 第1个字。(紧随命令名)
$ 最后那个字
m-n 最后那个字从编号m到n的全部字,若是忽略m,那么m默认为0(0-n)
n* 从第n个到最后那个之间的全部字
* 除命令名以外的全部字。与1*相同
% 最近的匹配?string?搜索的那个字

这些字的编号从0开始(命令行上的第一个字,一般指命令名),接着是1(紧随命令名的第1个字),直到n(命令行上的最后一个字)。
为了指定前一个事件中某个特定的字,在事件标志符(如!14)后面跟着一个冒号和一个表示该字在这个命令中的标号。如,!14:3指定了事件14中命令名后第3个字。使用一个用连字符隔开的两个字标志符,能够指定字范围。

[root@aminglinux_01 ~ 209]$echo 1 2 3 4 5
1 2 3 4 5
[root@aminglinux_01 ~ 210]$echo !209:2
echo 2
2
[root@aminglinux_01 ~ 211]$echo !209:^
echo 1
1
[root@aminglinux_01 ~ 212]$!209:0 !209:$
echo 5
5
[root@aminglinux_01 ~ 213]$echo !209:2-4
echo 2 3 4
2 3 4
[root@aminglinux_01 ~ 214]$!209:0-$
echo 1 2 3 4 5
1 2 3 4 5

若是一个事件只包含了单个命令,那么字编号就与参数编号对应。若是事件包含了多条命令,那么对于第一条以后的命令来讲,这种对应关系再也不成立。

[root@aminglinux_01 ~ 215]$!209 ; echo 6 7 8 9
echo 1 2 3 4 5 ; echo 6 7 8 9
1 2 3 4 5
6 7 8 9
[root@aminglinux_01 ~ 216]$echo !215:7
echo echo
echo
[root@aminglinux_01 ~ 217]$echo !215:4-8
echo 4 5 ; echo 6
4 5
6
[root@aminglinux_01 ~ 218]$echo !215:6-7
echo ; echo

[root@aminglinux_01 ~ 219]$echo !$
echo echo
echo

4. 修饰符

有时候须要改变正要执行的命令的某些方面。经过在字标志符后面或者事件标志符后面(若是没有字标志符)放置一个或多个修饰符,就能够修改事件或事件的某个字。每一个修饰符前面必须有一个冒号(:)。
替换修饰符:替换修饰符(substitute modifier)要比其余修饰符复杂。下面有个例子:

[root@aminglinux_01 ~ 222]$cad ~/3.txt
-bash: cad: 未找到命令
[root@aminglinux_01 ~ 223]$!!:s/cad/cat
cat ~/3.txt
this is a test
test
THIS

替换修饰符的语法:[g]s/old/new/
old为原字符串(并不是正则表达式),new表示代替old的那个字符串。替换修饰符用new替换old的第1次出现。在s前面放上g将形成全局替换,即将old的全部出现都替换掉。
其余修饰符:除替换修饰符外,修饰符还能够对由事件标志符和可选字标志符选取的事件部分进行简单的编辑。可使用多个修饰符,彼此之间用冒号(:)隔开。

[root@aminglinux_01 ~ 227]$ls ~/3.txt
/root/3.txt
[root@aminglinux_01 ~ 228]$!!:p
ls ~/3.txt
[root@aminglinux_01 ~ 228]$!!:h:p
ls ~
下表列出了除替换修饰符以外的事件修饰符
修饰符 功能
e(extension) 删除掉除文件扩展名以外的全部内容
h(head) 删除路径名的最后部分
p(print-not) 显示命令,但不要执行
q(quote) 引用该替换,以防止对其进行进一步的替换
r(root) 删除文件扩展名
t(tail) 删除掉路径名中除末尾以外的全部元素
x 与q相似,除了单独引用替换中的每一个字

2.6 函数

shell函数相似于shell脚本,里面存放了一系列可在稍后执行的命令。然而,由于shell将函数存放在物理内存(RAM)而不是磁盘文件中,因此,shell访问函数的速度要比访问脚本的速度快得多。shell还对函数预处理(解析),所以其启动速度也要比脚本快得多。最后,shell函数的执行和调用是在同一个shell中进行的。
shell函数的声明能够放在~/.bash_profile初始化文件中,或者放在使用该函数的脚本中,又或者直接放在命令行。可使用内置命令unset删除函数。一旦用户注销,shell将再也不保持这些函数。
PS:若是某个shell变量和函数名称相同,那么可用unset删除shell变量。再次用相同的命令,就会删除这个函数。
声明一个shell函数的语法以下:
[function] function_name()
{
commands
)

2.7 控制bash的特性和选项

2.8 处理命令行

相关文章
相关标签/搜索