《鸟哥的Linux私房菜》Chapter11 20180726~20180806
- 1.认识Bash这个shell
- 2.Shell的变量功能
- :从前往后删除,仅删除最短的符合字符串
- 3.命令别名与历史命令
- 4.Bash shell的操作环境
- 5.数据流重导向(Redirection)
- 6.管线命令
1.认识Bash这个shell
1.1.硬件、核心与shell
Shell,中文名为壳程序,它的功能是提供了用户操作应用程序的一个接口。(非特定,可以通过一个shell操作很多应用程序),之前学过的很多指令,如vi,mkdir,chown等每个指令都是一个独立的应用程序
用户通过Shell提供的接口操作应用程序,应用程序访问核心(kernel),核心通过驱动程序操作硬件。
狭义的壳程序指的是指令列方面的软件,如bash等,广义的壳程序则包括图形接口的软件,用户能通过图形接口的软件操作应用程序来呼叫核心。
既然我们通过文字接口的shell和图形接口的软件都能操作应用程序,为何要学文字接口的shell呢?
几乎各个发行版本的bash都是一样的,一法通万法通。而图形接口的软件则千变万化。
文字接口的shell远程管理速度较快,不容易断线或信息外流
借由shell提供的数据流重导向和管线命令,能够提高分析信息的效率
1.2.系统的合法shell和/etc/shells功能
根据发行者不同,shell也出现了很多版本。如Bourne SHell(sh),Sun里预设的C SHell,商业上常用的K SHell等。而Linux使用的shell版本就是Bourne Again SHell(bash),这个shell是Bourne SHell的增强版,也是基准于GUN架构下开发出来的。
Linux可以使用的shell都被记录在/etc/shells文件下。
执行下列命令查看该文件:vim /etc/shells
如图所示,图中列出了我的Linux系统中可以使用的shell
图中这些都是系统上合法的shell,某些服务在运行过程中会去访问/etc/shells,检查使用者能使用的shell。
比如某些FTP网站会去检查用户的可用shell,如果你不想某些用户使用FTP以外的主机资源,可以给这些用户一些怪怪的shell(将这些怪怪的shell写入/etc/shells文档中),类似图中的/sbin/nologin和/usr/sbin/nologin,让这些用户无法通过其他服务访问主机。
使用者什么时候取得shell来工作呢?
我登录的时候,系统给我一个shell让我来工作,而这个登录取得的shell就记录在/etc/passwd文档中
执行下列命令查看该文件:vim /etc/passwd
如图所示
红框中表明,root用户登录的时候被分配的shell是/bin/bash。我还有一个管理员用户huyan,该用户登录的时候被分配用来工作的shell也是/bin/bash。
1.3.Bash shell的功能
Bash是GUN计划中重要的软件工具之一,兼容于sh(Bourne SHell)并在此基础上加强的shell版本。Bash的优点如下
1.3.1.命令修编功能
能记忆使用过的指令高达1000个,并记录在home目录下的~/.bash_history档案中,但是不包含本次登录的指令。本次登录的指令被存储在内存中,注销时才会被写入~/.bash_history档案中。
执行下列命令打开~/.bash_history档案:vim /root/.bash_history
1.3.2.命令与档案补全功能(Tab键)
Tab接在一串指令的第一个字后面,则为命令补全。
Tab接在一串指令的第二个字后面,则为档案补齐。
1.3.3.命令别名设定功能(alias)
用alias命令可以给命令自定义别名。执行下列命令为ls -al命令取别名为lm:alias la=’ls -al’,那么执行la命令就相当于执行ls -al命令。如图所示
1.3.4.工作控制,前景背景控制(job control,foreground,background)
(略)
1.3.5.程序化脚本(shell script)
将需要下达的连续指令写成一个档案,利用shell scrip来进行主机的侦测工作。
1.3.6.通配符(wildcard)
比如说,想知道/usr/bin所有以sh开头的档案,利用通配符*可以做到。执行下列命令:ls -al /usr/bin/sh*
如图所示
1.4.Bash shell的内建命令:type
Bash里面已经内建了很多命令,如cd,pwd等,但是也有很多指令并非bash內建,可以用type指令区别内建指令和外部指令。使用方法为type [-tpa] name
如图所示,返回builtin表示是bash内建命令,返回alias表示是其他命令的别名,返回file表示为外部指令。参数-a表示从PATH变量定义的路径中列出所有含该指令的路径。
如果type指令后面的名称是不可执行的档案名,是无法被type指令找到的。也就是说,type指令的功能主要在于搜寻可执行的档案名,作用类似which,找指令用的。
1.5.指令的下达
(略,太简单了)有个小技巧要说一下,如果指令太长,可以通过\,Enter符号换行数据。如果不加\,Enter按钮在bash里是执行指令的功能,但是\使其后面的一个字符跳脱出来,让Enter有了换行的功能。
2.Shell的变量功能
2.1.什么是变量
变量的定义不说了,略。
2.1.1.变数的可变性与方便性
例如每个用户的邮箱都是通过MAIL这个变量来存取的。当user1这个用户登录时,他会取得MAIL变量,变量内容是var/spool/mail/user1。当user2这个用户登录时,他会取得MAIL变量,变量内容是var/spool/mail/user2。只要将MAIL变量带入不同的内容,就能让不同的用户读取自己的邮箱,是非常方便的。
2.1.2.影响bash环境操作的变量
如PATH变量,用户执行一个指令,系统是通过PATH环境变量里存储的路径顺序来搜寻这个指令的,如果搜寻完PATH变量里所有路径还找不到PATH指令,就会提示command not found。系统需要一些变量来提供他数据的存取,或是一些环境的设定参数值,所以就需要读入一些环境变量,如PATH,HOME,MAIL,SHELL等,与自定义变量不同,环境变量通常用大写字母表示。
2.1.3.脚本程序设计(shell script)的好帮手
变量被广泛地应用于shell script。
2.2.变量的取用与设定:echo,变量设定规则,unset
2.2.1.取用变量
用echo命令取用变量,变量名前加上$符号。如下列代码:echo $PATH
2.2.2.设定和修改变量内容
上图所示,第一个echo设置了一个新变量名newa,第二句代码给newa赋值,第三句代码输入newa变量中存储的值。
2.2.3.变量设定的规则
2.2.3.1.变量设定的规则
变量和内容之间用等号来连接。
如a=1
等号两边不能直接接空格符
a = 1这种写法是错误的
变量名称只能是字符或数字,且开头不能是数字
2a=1这种写法是错误的
可在变量内容外套上单引号或双引号,但是有区别
如图所示
在图中定义的两个变量newa和newb,分别用单引号和双引号赋值,最后输出显示两者值是不一样的。双引号能将引号内的变量用其值代入,LANG变量中的值是en_US.UTF-8,而单引号不能。
可用跳脱字符反斜杠\使特殊字符变成一般字符(如Enter,$,\,空格符等)
如图,对newa和newb变量重新赋值,newa变量中$这个特殊字符使LANG作为变量存在,并将其值带入,而newb变量中,加了\使得$这个特殊字符失去了作用,LANG不再是变量
给变量赋值时,如果需要借由其他指令提供的信息可以用返单引号`指令`或者$(指令)的形式
如图,对newa和newb变量重新赋值,这两种赋值方式得出的结果都是一样的。uname -r指令的作用是显示系统信息
注:反单引号是指数字键1左边的按钮~
当需要扩增变量内容时,可用“$变量名称”累加内容
如图,见newb变量,实现了累加
注:鸟哥书上写的扩增变量内容还有一种写法:${变量}我没实现,得出来的结论是testtest
如图,不知道怎么回事${变量}的写法又可以实现累加了
若该变量需要在其他子程序执行,需要用export把它变成环境变量
执行下列代码:export 变量名
通常系统默认变量名为大写字符,自定义变量名为小写
取消变量的方法为unset 变量名
注:此时unset后面的变量名不需要带$符号
单引号和双引号都必须要成对
如图,给变量name赋值huyan’s name
这三种写法中只有最后一种是对的(失败后要复原按Ctrl+c结束)
2.2.3.2.变量的累加示例
${name}和“$name”两种写法都能成功累加
2.2.3.3.将设定的变量用在下一个shell程序示例
如图,第一行代码,name的变量值是huyan’s nameyesno,用bash指令进入子程序,在子程序中输出name变量的值,为空。退出子程序,用export将name变量设置为环境变了,这样name变量就能应用在子程序了。进入子程序再次输入name变量值,这次有值了。
注:子程序是指,在当前的shell去启用另一个shell,这个shell就是子程序。父程序的变量是无法用在子程序的,但通过export把自定义变量变成环境变量后就可以再子程序使用了。
2.2.3.4.进入到linux核心的模块目录示例
如图,图中提供了2种方式,做了2步操作
先用指令uname -r读取核心版本为3.10.0-862.e17.x86_64
将上述结果代入原指令得指令为:cd /lib/modules/3.10.0-862.e17.x86_64/kernel
注:在一串指令中,返单引号内的内容会先被执行。
2.2.3.5.用locate指令查找文件并列出详细信息示例
Locate指令查找文件,只能查找到档案名,但不会显示创建时间,大小,拥有者,权限等详细信息。
如图,系统先执行返单引号中的指令locate crontab,然后再执行ls -l指令列出这些带有crontab字样的文件的权限等详细信息。
2.2.3.6.扩展:locate命令简介
Linux要使用locate命令,必须先用yum安装mlocate应用程序。Locate命令比find -name命令快的多,原因在于它不搜索目录,而是搜索一个数据库/var/lib/mlocate/mlocate.db。
这个数据库包含本地所有文件信息,Linux自动创建这个数据库,并且每天自动更新一次。因此用locate命令有时会找到已经被删除的文件,或者检索刚刚新建的文件却找不到(因为数据库还没来得及更新)。要避免这种情况,使用locate之前可以先用updatedb命令更新数据库。
2.2.3.7.利用变量快速访问常去的目录示例
如果有一个需要经常访问的目录:/root/dir1/dir2/dir3/dir4/dir5,如何对该目录简化并访问?变量的存在给出了一个比较便捷的途径
下图中,我新建了这一系列目录,我们要进入dir5目录,只需要将路径赋值给work变量,访问目录时直接cd $work就可以了
2.3.环境变量的功能:env与常用环境变量说明,set,export
环境变量可以帮我们达到很多功能,如home目录的变换,执行文件搜寻的路径等,我们可以使用env和export命令来查看shell环境中默认的环境变量。
2.3.1.用env查看所有环境变量及其内容
env是environment(环境)的简写,很好记
简要分析一下里面各个变量的作用
HOME:用户主文件夹
SHELL:系统默认使用的是哪个shell
HISTSIZE:最多可以保存多少历史命令
MAIL:用户的邮箱档案
PATH:执行文件查找的路径,中间用:隔开
LANG:语系数据
RANDOM:随机数的变量,存储了系统0-32767的随机数
如图,只要echo $RANDOM变量就会随机输出0-32767之间的某个数。
如果要随机输出0-9直接的数,可以这样做
2.3.2.用set查看所有变量(包括环境变量和自定义变量)
有一个自定义变量a并赋值
然后用set查看所有变量如图
查询结果中就包含了自定义变量a的信息。用unset命令删除a,再用set命令查看,自定义变量a就消失了。
在set的输出内容中,有一下几个变量是比较重要的
2.3.2.1.PS1(提示符的设置)
这个就是我们的命令提示符,当我们每次按下Enter键去执行某个命令后,系统再次出现提示符时就会自动去读取这个变量值。这个结构应该可以看懂。
\u:目前用户的账户名称
\h:主机名在第一个小数点之前的名字,比如我的主机名时localhost.localdomain
\W:利用basename取得的工作目录的名称
对比发现符合这个结构
可以通过更改PS1变量来修改自己的提示符
如图所示,我将PS1变量的内容修改成[\u@\h \w \t ##]$的格式,命令提示符马上就变了。在这个格式中#3表示第3次执行该命令
2.3.2.2.$(关于shell的本地PID)
$本身也是个变量,代表目前这个shell的线程代号。可以通过echo命令查看shell的PID。如图所示
2.3.2.3.?(上一个命令的回传值)
这个变量中存储的时上一个命令执行完后回传的一个代码,成功执行的命令回传的是0,而执行过程中发生错误,回传的是错误代码。如下图
'#9'的代码返回的是127,这是上个命令发生command not found错误时返回的错误代码。?变量的值只与上一个命令有关。
2.3.2.4.OSTYPE,HOSTTYPE和MACHTYPE
这几个变量存储主机硬件与内核的等级
目前CPU主要分为32位和64位,其中32位又可以分为i386、i586、i686,而64位则被称为x86_64。我的CPU就是64位的。
2.3.3.export:自定义变量转成环境变量
自定义变量和环境变量的差异在于,是否能被子进程引用。
当用户登录Linux之后,会取得一个bash,这个bash就是一个独立进程,拥有一个唯一的PID,接下来你在这个bash下面所执行的任何命令都是由这个bash衍生出来的,那些被执行的命令就是子进程了。如图所示
图中共出现了3个不同的PID。我通过bash命令建立了一个子进程。其中PID为2226的进程就是PID为2122的进程的子进程,而PID为2237的进程是PID为2226的进程的子进程,已经非常明白了。
同时我们可以发现之前修改的提示符变量PS1在子进程中无效。命令提示符在子进程中又变回了原来的样子,PS1修改后的格式只在PID为2122的进程中有效。
PS1并非环境变量,无法被子进程继承,如果想要PS1永久有效,需要用export指令将PS1变成环境变量
如图所示,设置PS1=’[\u@\h \W \t PID:$$]$’,然后进入子进程,发现PS1的设置无效,退出后用export语句将PS1转成环境变量,再次进入子进程,发现PS1设置依然有效。
但是登出Linux后重新登录,PS1又无效了。
仅执行export,后面不跟变量时会把所有环境变量都显示出来。而显示出来的环境变量里没有PS1,所以PS1不是环境变量。
2.4.影响显示结果的语系变量(locale)
可以用locale指令查找Linux支持的所有语系。
直接在终端输入locale –a
太多了,只截图部分。
可以用locale指令设置语言环境,如图所示,不需要加任何参数。
可以修改以上的语系变量参数,但是通常我们只需设置LANG或者时LC_ALL,因为其他语系变量都会被这两个变量所替代。它是主要的设置变量。
2.5.变量的有效范围
环境变量=全局变量,自定义变量=局部变量,自定义变量通过export指令变成环境变量。
环境变量为什么可以被子进程引用呢?
当启动一个shell时,系统分配一块内存给shell使用,环境变量都存储在这块内存中。
利用export指令把自定义变量写到这块内存中。
打开一个子shell时,子shell将父shell的环境变量所在的内存块导入到自己的环境变量块中。
环境变量和bash的操作环境不是一个概念,比如PS1不是环境变量,但是会影响到bash的接口(提示符)。
2.6.变量键盘读取、数组与宣告:read,declare,array
之前提到的变量设置都是由命令行直接设置的,但是我们也可以让用户经由键盘输入,比如某些程序执行过程当中,会等待用户输入yes或no这样的信息。
2.6.1.read
用read指令可以读取来自键盘输入的变量。如下图
第一句read指令是read指令的简单用法,后面直接跟变量名,Enter后下面会主动出现一个空白行等待你输入。
第二句read指令是带参数的,-p后面可以接提示符,-t后面接等待输入的秒数。如果在30s内没输入,该指令就会被自动略过。
注:Read指令后面的变量不需要加$符号
2.6.2.declare/typeset
declare和typeset是一样的功能,都是用于定义变量类型,如果declare后面没有接任何参数,bash将自动列出所有变量名称与内容(和set一样)。如图(太多了只截取一小段)
declare的语法是:declare [-aixr] variable
2.6.2.1.declare的用法
如图,定义一个整型变量sum,计算2+30+40的结果,传入sum中
bash默认变量类型是字符串,因此第一次sum没有用的declare定义类型,bash自动把2+30+40当成字符串处理,原样输出。第二次定义sum为integer类型,所以对计算式进行了计算。
注:bash中的数值运算,默认最多能达到整型,所以1/3的结果是0
declare各个参数的语法:
-a:将变量定义成数组类型(array)
-i:将变量定义成整数类型(integer)
-x:功能类似export,将变量定义成环境变量
-r:将变量设置成只读类型(readonly),不能重设,不能被更改内容
由于bash默认的变量类型是字符串型,如果需要非字符串类型的变量,就需要用declare定义变量类型。
注:如果不小心将变量设置为只读,通常要注销再登录才能复原该变量的类型。
2.6.2.2.数组变量类型(array)
数组的设置方式如图
图中设置了数组型变量arr,并在arr中存储了一串字符串类型的数据,最后输出这些数据。echo指令中用上了${数组}这样的格式,这样读取会比较正确。
2.7.与文件系统及程序的限制关系:ulimit
如果我的Linux同时登录10个人,每个人打开了100个文件,会导致占用内存过大,系统挂掉,为了防止这一点,bash可以用ulimit指令限制用户的某些系统资源,包括可以打开的文件数量,可以使用的CPU时间,可以使用的内存总量等。
ulimit的语法是:ulimit [-SHacdfltu] [配额]
-H:(hard limit)严格设置,必定不能超过这个设置的数值
-S:(soft limit)警告设置,可以超过这个设置的数值,但是超过后会有警告信息。通常soft limit比hard limit小,比如说soft limit设置为80,hard limit设置为100,那么超过80会有警告信息。
-a:后面不接任何参数,可以列出所有限制数据数值
-c:当某些进程发生错误时,系统可能会将该进程在内存中的信息写成文件(排错用),这种文件被称为内核文件(core file),此为限制每个内核文件的最大容量。
-f:此shell可以创建的最大文件容量,单位为KB(一般都为2GB)
-d:系统可使用的最大断裂内存(sagment)容量
-l:可用于锁定(lock)的内存容量
-t:可使用的最大CPU时间,单位为秒
-u:单一用户可使用的最大进程数量
示例1:列出你现在身份的所有限制数据数值(此时应该用-a)
如图所示
上图中,只要是0就代表没设置。
示例2:限制用户仅能创建10MB以下的文件
如图file size从unlimited变成了10240
限制完后,如果我们创建超过10MB的文件,会怎么样呢?
如图,提示file size limit exceeded,创建失败。
注:复原ulimit的设置最简单的方法就是注销再登录,也可以重新以ilimit设置。
示例3:用ulimit指令取消示例2中对file size的设置
如图所示,file size这一项又变成了unlimited
2.8.变量内容的灵活输出
变量除了直接修改原来的内容外,还可以通过简单的操作将其微调。
2.8.1.变量内容的选择输出
示例1:定义一个变量path,内容与环境变量PATH相同,且删除带有qt-3.3的路径
如图所示,输出变量时没有输出/usr/lib64/qt-3.3/bin:这个目录,但是重新输出该变量时该路径还是在,所以这里的删除只是一种选择性输出方案,并不是真正改变变量内容了。
注:并没有删除变量内容,而是选择性输出
在echo ${path#/qt-3.3/bin:}这个命令中,path是选择性输出的变量名,#代表从最前面开始向右删除,并且仅删除最短的那个,/表示从/符号开始删除,表示中间省略无数字,在这个示例中,代表了usr/lib64/这段内容,一直删除到qt-3.3/bin:为止
示例2:删除变量path前面所有的目录,仅保留最后一个目录
如图所示,echo ${path#/:}命令没有达到要求,因为#表示仅删除最短的那个,因此该命令的输出结果中,只有第一个目录被删除了。
Echo ${path##/:}命令达到要求,##符号代表删除最长的那个,整个指令的意思是将path变量中/到:的最长的部分抠掉,输出剩下的部分。
示例3:删除path变量中最后两个目录
如图所示,在echo ${path%:/usr*bin}指令中,%表示从后往前删除,且仅删除最短的符合字符串,同理,%%表示从后往前删除,且仅删除最长的符合字符串。
总结一下,在变量的选择输出中,要记住以下几点:
-
:从前往后删除,仅删除最短的符合字符串
-
:从前往后删除,仅删除最长的符合字符串
- %:从后往前删除,仅删除最短的符合字符串
- %%:从后往前删除,仅删除最长的符合字符串
- *:代表n个被省略的字符串
基本格式为${变量名#或##或%或%%关键字}
2.8.2.变量内容的替换输出
示例1:将path变量中第一个sbin替换成SBIN并输出
如图所示,指令echo ${path/sbin/SBIN}中,第一个/表示第一个旧字符串被替换。第二个/表示旧字符串和新字符串之间用/隔开
示例2:将path变量中所有sbin替换成SBIN并输出
如图所示,指令echo ${path//sbin/SBIN}中,//表示所有旧字符串都被替换,而旧字符串和新字符串之间仍然用/隔开
总结以下,在变量的替换输出中,要记住以下几点:
/:第一个旧字符串被新字符串替换
//:所有旧字符串被新字符串替换
最后,新旧字符串之间用/隔开
基本格式为${变量名/或//旧字符串/新字符串}
2.8.3.测试变量是否存在与内容替换
判断某个变量是否存在,若存在,输出该变量,若不存在,赋予该变量某个值
要如何用一条命令实现上述过程?
示例1:判断username变量是否存在,若存在,输出该变量,若不存在,为该变量赋值root并且输出该变量
如图所示,第一个echo $username返回为空,有可能该变量不存在,有可能存在但是存储空字符串。username=${username-root}命令测试username是否存在,若存在,不重新赋值,若不存在,赋值root。第二个echo $username返回了root,说明username变量确实是不存在的。如果在上述代码的基础上再次测试username变量是否存在,若存在则输出,若不存在,则重新赋值reset
如图所示,会发现输出结果还是root,因为之前的操作已经让username变量存在了。因此不对它做重新赋值。
基本格式为新的变量=${被测试的变量-赋值内容}
注:新的变量跟被测试的变量可以相同,也可以不同。
示例2:判断username变量是否存在或为空,若存在且不为空,输出该变量,若不存在或为空,为该变量赋值root
如图所示,username变量存在,但是被设置为空字符串,通过username=${username-root}语句不能为它重新赋值root,但是通过username=${username:-root}语句可以成功为它赋值root
注:大括号内不加:,对不存在的变量赋值,大括号内加:对不存在或存在但为空字符串的变量赋值。
关于变量的替换,有-,:-,+,:+,=,:=,?,:?这几种
|变量设置方式 |Str变量不存在 |Str变量值为空 |Str值为非空字符串|
| ------------- |:-------------😐: -----😐
|var=${str-expr}| var=expr |var=$str| var=$str|
|var=${str:-expr}| var=expr |var=expr |var=$str|
|var=${str+expr}| var= |var=expr| var=expr|
|var=${str:+expr}| var= |var= |var=expr|
|var=${str=expr}|var=expr str=expr|var=$str str=$str|var=$str str=$str|
|var=${str:=expr}|var=expr str=expr|var=expr str=expr|var=$str str=$str|
|var=${str?expr}| -bash:str:expr| var=$str | var=$str|
|var=${str:?expr}| -bash:str:expr |-bash:str:expr |var=$str|
示例3:试验?和:?的功能
由图中可知,?和:?与expr无关,只涉及是否将str变量值赋值给var
3.命令别名与历史命令
3.1.命令别名设定alias和unalias
当惯用指令特别长的时候,用alias给命令取别名是一种有效而简便的方式。alias的定义规则和变量赋值规则基本相同。
示例1:用alias查找目前哪些命令有别名
如图,上图中就是命令和他们的别名对照表
示例2:用alias为命令取别名
示例3:用unalias取消命令别名
3.2.历史命令:history,HISTSIZE
利用history指令可以查询我们曾经下达过的指令。
3.2.1.history的选项和参数
n:数字,列出最近的n笔命令行表
-c:清除目前的history的全部内容
-a:将目前新增的history指令写入histfile中,如果没有加histfile,则预设写入~/.bash_history
-r:将histfile的内容读到目前这个shell的history记忆中
-w:将目前的history记忆内容写入histfile中
示例1:列出目前内存内所有的history记忆,直接在命令行输入不带参数的history指令
前面的数字是这个指令在这个shell当中的代码,后面是指令本身
示例2:列出最近的3笔资料
如图,输入指令history 3,就能列出最近执行的3条指令,包括它本身。
3.2.2.历史命令的读取和记录
当我们以bash登录linux主机后,系统会主动地从~/.bash_history读取曾经的指令记录,而~/.bash_history记录的指令条数和HISTSIZE变量有关
假设登录linux主机后我下达了210条指令,注销时系统就会将211~1210这1000条历史指令更新到~/.bash_history中。而最先下达的前210条指令就会被丢弃。也就是说,在我注销时,系统会将最近的HISTFILESIZE笔记录记录到~/.history_bash中
~/.history_bash档案中的记录数量永远有HISTSIZE那么多,每次更新都是拿掉最早的旧记录,增加新纪录。
3.2.3.利用history指令执行命令
!number:执行第number笔指令
!command:向上搜寻以command开头的指令并执行
!!:执行上一个指令
示例3:利用history指令执行命令
如图所示,!337指令即找到echo $str1指令输出并执行,!echo向上找出最新的以echo开头的指令,就是用!337指令执行的echo $str1指令,!!执行上一个指令,上一个指令即echo $str1指令。
3.2.4.同一账号同时多次登录的history写入问题
同时开好几个bash窗口,每个bash的身份都是root,每个root都有1000笔history记录在内存中,最后更新到~/.history_bash档案的是最后一个注销的root
4.Bash shell的操作环境
4.1.路径与指令搜寻顺序
当我们执行ls指令时,系统中有很多ls指令,到底是哪个指令被执行了呢?
如图,同样是执行ls指令,执行ls和执行/bin/ls的显示却有区别,ls的显示是有颜色的,而/bin/ls的显示是无颜色的。
基本上,指令的执行顺序可以这样看
以相对路径或者绝对路径来执行,如/bin/ls或者./ls
由alias找到指令来执行
由bash内建的指令来执行
由环境变量PATH中的路径找到指令来执行
就像上面的例子,/bin/ls是直接取用该指令来执行的,而ls会因为别名alias ls=’ls --color=tty’这个指令而执行别名中的指令。
想要了解指令的搜寻顺序,可以通过type指令查询(type指令是用来查找一个指令是否是bash内建命令或外部命令的)
如图,执行ls指令时首先搜寻到的是别名,而后才是/bin/ls,最后才是由环境变量PATH中的路径查找到的指令。
示例1:将echo指令的别名设为echo -n,观察执行echo指令时的搜寻顺序
如图,首先被搜寻到的就是别名echo -n,然后才是bash的内建命令echo,然后是绝对路径,然后是PATH变量路径检索出来的指令。
4.2.Bash的进站与欢迎讯息:etc/essue,etc/motd
在终端机接口tty登录的时候,bash也会有进站和欢迎讯息
这些字符串被写在etc/issue里面,如下图
其中,\S表示操作系统名称,\r表示操作系统版本(相当于uname -r),\m表示硬件等级。而且issue这个档案的内容也是可以使用\作为变量来取用的。
示例1:修改bash的进站和欢迎讯息
如图,利用vim编辑器修改了issue档案的内容,重新登录结果如下
注:在etc目录下,还有一个档案叫issue.net,这个档案是服务于telnet这个远程登录程序的。当用户用远程登录程序telnet登录终端机时,主机的登录画面就会显示成/etc/issue.net,而不是/etc/issue
如果想要使登录者登录终端机后取得一些信息,可以把这些讯息写到/etc/motd这个档案中,比如,告诉登录者系统将会在某个时间段进行维护。
示例1:编辑/etc/motd档案,告诉登录者系统将会在某个时间段进行维护
如图,/etc/motd档案中的内容在登录后才会显示,并且每个用户登录都看得到。
4.3.环境配置文件:login,non-login shell,/etc/profile,/.bash_profile,source,/.bashrc
当我们一进入bash,就有一堆有用的变量可取用,这是因为系统有一些环境配置文件档案在。bash在启动时直接读取这些档案,根据这些档案规划好操作环境。而这些配置文件又可以分为全体系统的配置文件和用户个人偏好的配置文件。
注:前几个小节中的命令别名,自定义变量等在注销bash后就会失效,必须把这些内容写入配置文件中才行。
4.3.1.login shell和non-login shell
login shell和non-login shell的区别在于,是否有登录这个过程。
login shell:取得bash时需要完整的登录流程,要输入账户名和密码,就是login shell
non-login shell:取得bash接口的方法不需要登录举动,比如以X Window登录linux后,再以图形化接口启动终端机,那么不需要再次输入账号和密码,这个bash就称为non-login shell。再比如,在bash中再次下达bash命令,进入子进程,这个过程也不需要再次输入账号和密码,那么作为子进程的bash也是non-login shell
而login shell和non-login shell读取的配置文件是不一样的。
4.3.2.Login shell读取的配置文件
login shell仅读取下面两个配置文件
/etc/profile:系统的整体设定,最好不要修改这个档案
~/.bash_profile或~/.bash_login或~/.profile:属于使用者个人设定,要改自己的数据就写入这里
4.3.2.1./etc/profile
这个配置文件利用使用者的标识符(UID)来决定很多重要的变量数据,是每个使用者登录取得bash时必读的文件,如果你想要帮所有使用者设定整体环境,就必须要改这个文件,但是没事还是不要随便更改这个档案。我是用非root用户登录的,所以这个文件对我是只读模式
/etc/profile内容如图所示
这个档案设定的变量主要有
PATH:根据UID确定PATH变量要不要含有sbin的系统指令目录
MAIL:根据账户设置使用者的mailbox到var/spool/mail/账户名
USER:根据用户的账号设置此变量
HOSTNAME:根据主机的hostname指令设置此变量的内容
HISTSIZE:历史命令记录笔数
/etc/profile还会呼叫外部的设定数据
/etc/inputrc
这个档案的内容为bash的热键,Tab按键要不要有声音这样的数据。这个档案并没有被执行,/etc/profile根据这个档案的内容判断用户有没有自定义输入的按键功能,以决定是否设置INPUTRC=/etc/inputrc这个变量。
/etc/profile.d/*.sh
指的是在/etc/profile.d目录下带有.sh扩展名的很多档案。只要使用者具有读的权限,该档案就会被呼叫进来。这个目录底下的档案规范了接口的颜色,语系,ll和ls的别名,vi的命令别名,which的命令别名等等。如果想帮所有使用者设定一些共享的命令别名,可以在该目录下自行新建.sh扩展名的档案并写入数据。
/etc/sysconfig/i18n
这个文件我的CentOS 7上没有找到,是决定bash预设使用何种语系的文件。根据鸟哥所说,这个文件是被/etc/profile.d/lang.sh呼叫的,/etc/profile.d/lang.sh档案如下
4.3.2.2.~/.bash_profile
bash读完了/etc/profile文件并呼叫了其他配置文件后,会读取用户的个人配置文件。个人配置文件都是在用户的登录默认目录下的。按照下列顺序读取个人配置文件,3个文件中只读取一个,检测到哪个文件存在就读取哪个,一旦读取到后就不再读取其他的了。
/.bash_profile——/.bash_login——~/.profile
比如说当前登录用户huyan的~/.bash_profile内容如下
在上图中,有个if语句,判断~/.bashrc档案是否存在,存在就读取。我们再来看看,在~/.bashrc这个档案中存储了什么内容:
如图所示,~/.bashrc档案中,也有一个if语句,这个语句的作用是判断/etc/bashrc这个档案是否存在,存在就读取。
回到~/.bash_profile文件中来,在该文件中,还提到了变量PATH和变量HOME,并且把PATH变量变成环境变量。在/etc/profile中,已经设定过PATH变量了,这里是把~/.local/bin目录和~/bin目录添加到PATH变量中。并且重新把PATH设置为环境变量。
4.3.2.3.login shell的配置文件读取流程
根据上面的叙述,我们已经能画出login shell配置文件的读取流程了。
实线箭头方向是主线流程,虚线箭头方向是被呼叫的配置文件。
4.3.2.4.source:读入环境配置文件的指令
/etc/profile和~/.bash_profile都是在取得login shell时才会读取的配置文件,如果用户设置完个人配置文件,需要注销后重新登录才会生效。source指令不需要用户重新登录,就能读入用户设置的个人配置文件,使配置生效。
示例1:修改~/.bashrc档案,在其中加上别名ta=’type -a’的设置后,用source指令读入该文件,使修改的配置立刻生效
首先我们来看type -a指令的用法如图所示,在未设置~/.bashrc档案时,ta命令不存在。
修改~/.bashrc档案,在其中加上alias ta=’type -a’,并用source指令读入该配置,如图所示
用source指令读取~/.bashrc档案后,可以使用ta指令了,功能与type -a指令一样。除了source指令,用小数点(.)也可以读取配置文件,写法如图所示
当用户需要用到多种配置环境来处理不同的项目时,编写环境变量配置档案,并且用source导入是非常有效的。
4.3.3.Non-login shell读取的配置文件
non-login shell登录仅会读取~/.bashrc配置文件。~/.bashrc配置文件预设如下
alias ta=’type -a’是上一小节示例1中我自己加的,是我的个人配置。
此外,CentOS 7还会呼叫/etc/bashrc档案。该档案定义了如下数据
依据不同的UID规范出umask的值(前面几章没看,不懂umask是什么,以后补充)
依据不同的UID规范出提示符(PS1变量)的值
呼叫/etc/profile.d/sh的设定
/etc/bashrc是Red Hat系统特有的。其他linux发行版可能会放置在不同的档名。由于~/.bashrc会呼叫/etc/bashrc和/etc/profile.d/sh,当我们不小心删除~/.bashrc档案时,提示符就会显示不正常。如图所示
这时,只要把/etc/skel/.bashrc复制到家目录下,将新文件取名为~/.bashrc,并且输入个人配置,然后用source指令读取一下,提示符就会恢复正常。
4.3.4.其他相关配置文件
/etc/man.config
档案规定了下达man指令的时候该去哪里查看数据的路径设定。档案中设定了MANPATH变量,我们搜寻man page时,会根据MANPATH中的路径进行搜寻。
如果你是以tarball的方式来安装你的应用数据,那么man page可能会被防止在/usr/local/softpackage/man里面,那个softpackage是你的应用套件名称。这时就得以手动方式将该路径加到/etc/man.config里面,否则使用man查找该应用时就会找不到对应的说明文档。
~/.bash_history
这个文件之前提过,是用来记录历史指令的,每次登入bash后,bash会先读取这个档案,将所有的历史指令读入内存。因此我们登录bash后就可以查知上次使用过的历史指令了。
~/.bash_logout
这个档案记录了当我们注销系统时,系统再帮我们做些什么操作再离开。预设情况下,在CentOS 7中,我的该档案是空的。用户可以将清除屏幕信息,备份等重要的工作写在这个档案中(如清空暂存盘)。
4.4.终端机的环境设定:stty,set
4.4.1.Stty指令
stty指令的意思是set tty,tty代表了文字接口的终端机,stty命令就是设置终端机环境的功能。我们可以用退格键删除命令行,输入错误时就会有声音警告,用Ctrl+C就可以强制终止指令的运行,这些都是得益于终端机的环境设定。
示例1:利用stty指令查阅终端机的按键设置
如图所示,stty -a指令列出所有的按键和按键内容,^符号代表了Ctrl按键,比如intr=^C表示利用Ctrl+C达成。有几个重要的代表意义了解一下:
eof:end of file的意思,代表结束输入
erase:向后删除字符
intr:送出一个interrupt(中断讯号)给当前正在run的程序
kill:删除在目前指令列上的所有文字
quit:送出一个quit讯号给当前正在run的程序
strat:在某个程序停止后重新启动它的output
stop:停止目前屏幕的输出
susp:送出一个terminal stop讯号给当前正在run的程序
示例1:用stty修改热键erase=^h
如图所示,将erase的热键改成^h后,我们删除命令行如果只按Back Space键就会显示成这样
必须按Ctrl+Back Space按钮才能删除命令行。
4.4.2.Set指令
除了stty指令之外,bash还有自己的一些设定值,可以用set指令设置。set指令除了可以显示变量之外,还可以帮我们设定整个输出/输入的环境。例如记录历史命令,显示错误内容等。
set指令的参数
-u:预设不启用。启用后,当使用未定义变量时,会显示错误讯息
-v:预设不启用。启用后,当讯息被输出前,会先显示讯息的原始内容
-x:预设不启用。启用后,指令被执行前,会显示指令内容(前面有+符号)
-h:预设启用,与历史命令有关
-H:预设启用,与历史命令有关
-m:预设启用,与工作管理有关
-B:预设启用,与中括号[]有关
-C:预设不启用,若使用>等,则若档案存在时,该档案不会被覆盖
示例2:显示目前所有的set设定值
-变量的内容就是set所有的设定了,bash的默认设定是himBH
示例3:设定若使用未定义变量时,显示错误讯息
Bash默认使用未定义变量时,变量为空,如果设定了set -u,那么使用未定义变量时,会报错unbound variable,若想取消这个参数,设置set +u即可
示例4:设定执行指令前,显示该指令内容
如图所示,设置了-x参数之后,执行!467命令,之前学过历史指令,知道该命令实际执行的是echo $-指令,如图所示。
其实我们还有其他的按键设定功能,就是在/etc/inputrc档案中设置,之前提到过的。
/etc/inputrc档案内容如图所示
还有例如/etc/DIR_COLORS*与/etc/termcap等,也是与终端机有关的环境配置档案
4.4.3.Bash默认的组合键汇整
组合按键 | 执行结果 |
---|---|
Ctrl+C | 终止目前的指令 |
Ctrl+D | 输入结束(EOF),例如邮件结束的时候 |
Ctrl+M | Enter |
Ctrl+S | 暂停屏幕的输出 |
Ctrl+Q | 恢复屏幕的输出 |
Ctrl+U | 在提示字符下将整列命令删除 |
Ctrl+Z | 暂停目前的命令 |
4.5.通配符与特殊符号
符号 | 意义 |
---|
- |代表0到无穷多个任意字符
? |代表1个任意字符
[abcd] |代表abcd中至少有一个字符
[0-9]| 匹配0到9内的所有数字
[^abc] |反向选择,匹配除了abc的任意字符
示例1:找出HOME目录下以.bash开头的档案
示例2:找出HOME目录下刚好6个字符的档案
注:档案名前面的.是不能用?表示的
示例3:找出HOME目录下含有a,b,c任意一个字符的档案
注:在鸟哥的linux私房菜中下列示例
经实际操作在CentOS中无法实现
除上述列表中的常用通配符,bash还有很多其他的符号,其作用不在此一一列举
5.数据流重导向(Redirection)
5.1.何谓数据流重导向
数据流重导向就是执行指令的时候,不在屏幕上输出了,而是把输出结果放进一个系统自动建立的档案中。
用户执行指令,系统根据别名或者PATH路径什么都好,反正找到该指令,调用该指令的档案读取结果,将结果输出到屏幕上,而数据流重导向则是把结果保存到文件中。
指令执行结果有2种:输出正确结果(标准输出)和输出错误信息(标准错误输出)。
我们设置数据流重导向的特殊字符和代表意义如下
|操作名称 |简称 |代码 |符号|
|------------😐:---------😐:------------|
|标准输入| stdin| 0 |<或<<|
|标准输出| stdout| 1| >或>>|
|标准错误输出 |stderr| 2 |2>或2>>|
注:表中<和<<的功能,>和>>的功能是有差异的,下面会讲到
示例1:用ls -al语句查询根目录,把标准输出结果用数据流重导向导入到档案中
使用数据流重导向后,屏幕并无输出,ls -al >/root/myfile语句的意思是,执行ls -al指令,并把标准输出结果放入/root/myfile档案中
现在我们来查看/root/myfile档案的内容,看是否与结果一致
如图所示,经比对结果一致。
注:这个/root/myfile档案是原来系统中不存在的,在这次代码执行过程中被新建的档案。
示例2:用ls -al命令查询家目录下/root/abcd档案,并将标准错误输出放到/root/myfile1档案中
如图,注意看,红框内是在示例1中被建立的/root/myfile档案,图中利用ls -al列出了root目录下的所有档案,说明该文档确实被建立了。
话说回来,在该目录下没有abcd档案的存在,因此会有错误信息。利用ls -al abcd 2>/root/myfile1指令将错误信息输出到myfile1档案中。查看myfile1档案如图所示
如图,错误信息被完整地存放在了myfile1档案中。
这就是数据流重定向的简单应用。
那么如果一个返回错误讯息的指令,我将它的标准输出输出到文档中,会怎么样呢?
示例3:用ls -al命令查询家目录下/root/abcd档案,并将标准输出放到/root/myfile1档案中
指令如图所示,/root/abcd档案在root目录下是不存在的,因此有错误讯息,当我们设置标准输出到myfile2后,发现错误讯息依然被打印出来,而/root/myfile2档案也被建立了。查看/root/myfile2内容为空(空内容太占地方不截图了)。
5.2.>和>>的区别
那么如果将不同命令的结果输出到同一个档案会怎么样呢?
示例4:将示例2中的结果输出到示例1中/root/myfile档案
示例1中,我们已经建立了/root/myfile档案,并且该档案中存放了示例1的标准输出内容。执行下列命令将示例2中的结果也输出到/root/myfile档案
查看/root/myfile档案的结果如下
发现示例1的标准输出结果不见了,仅显示了示例2的标准错误输出结果。
因为档案的建立方式是这样的:
- 若该档案不存在,指令执行时系统自动建立该档案
- 若该档案已存在,系统会将档案内容清空,将数据重新写入(也就是说若以>输出到一个已存在的档案,档案原本的内容就会被覆盖掉)
如果不想档案原内容被覆盖,就需要用到>>
示例5:将示例1的输出结果重新输出到/root/myfile,要求该档案内原内容也存在
执行下列命令将示例1的结果标准输出到/root/myfile,且不覆盖原内容(原内容见上一张图)
此时查看/root/myfile的内容
如图所示,红框内是原内容,原内容还在。标准输入和标准错误输出中单尖括号和双尖括号的区别也是如此,不多举例了。
总结一下: - >:以覆盖的方法将正确输出输出到指定档案或装置上
- >>:以累加的方法将正确输出输出到指定档案或装置上
- 2>:以覆盖的方法将错误讯息输出到指定档案或装置上
- 2>>:以累加的方法将错误讯息输出到指定档案或装置上
一个指令执行,有时会同时返回正确结果和错误讯息,怎么把这两者分存到同一个档案中呢?在/home/huyan,/home/roots和/home/tests目录下,都有一个testrc档案。其中/home/roots/testrc和/home/tests/testrc档案和/home/roots及/home/tests的权限是700,所有者是root,即不能被同组和其他用户读取修改和执行。那么当我用huyan用户登录并用find指令查找/home目录下testrc档案时,就会同时返回正确结果和错误讯息。
如图所示
示例6:将find /home -name testrc指令下的stdout和stderr分存到不同档案中
如图所示,/home/huyan/find_error内容如下
/home/huyan/find_right内容如下
同理,按照这样的代码格式,如果要将一个指令下的正确结果和错误讯息都输出到同一个档案中,只需要将档案名字段改成相同即可,同时设置成累加的形式。
示例7:将find /home -name testrc指令下的stdout和stderr存到/home/huyan/find档案中
如图所示,按照先后顺序,先将find指令的正确结果输出到find,然后将错误讯息输出到find。
注:这里要注意,按照前面提过的档案建立方式,示例7中将错误讯息输出到find时必须用2>>,否则错误讯息将覆盖正确结果,我们打开/home/huyan/find的时候将只能看到错误讯息。
查看/home/huyan/find档案内容
5.3./dev/null垃圾桶黑洞装置与特殊写法
如果我们想将产生的错误信息忽略或者不保存,需要了解一个特殊的装置/dev/null,我们来查看一下它的属性
如图所示,以c开头,表示档案类型为装置文件里的串行端口设备,如键盘,鼠标(一次性读取装置,反正没有存储功能的,这一点我会在以后的读书笔记里补充)
只要将指令产生的错误信息标准错误输出到/dev/null中,错误信息就不会被输出或保存。
示例8:将示例7中产生的错误讯息输出到/dev/null中
如图,这样写就只会输出正确结果。
再用示例9来说明一下示例7的特殊写法
示例9:用特殊写法得到和示例7一样的结果
如图所示,别忘记删除示例7中产生的/home/huyan/find档案
查看新生成的find档案如图所示
还有一种写法也是正确的
5.4.Standard input:<与<<
还有标准输入前面没提到,标准输入指的是,将原本需要用键盘输入的数据用档案内容来取代。
示例10:新建一个文档catfile,并用标准输入将filetest档案中的内容输入到catfile中
原本我们新建一个文档并输入内容,不使用标准输入的操作是这样的
解释一下,cat > catfile指令中,加入>符号后,catfile档案会被主动建立,进入一个输入环境,内容就是下面两行,按Ctrl+d结束输入。然后用cat catfile指令查看该文件内容。
但是如果我们不想手动输入内容,利用标准输入将另一个档案中相同的内容输入到这里,也是可以的。
如图所示,rm catfile删除掉之前建立的catfile档案,为了示例需要我先新建一个档案filetest,在里面存储一些内容,用cat filetest查看了一下确认内容被写入。
cat > catfile <filetest语句实现了用标准输入将filetest的内容输入到新建档案catfile中。
随后我们查看了一下catfile档案和filetest档案,发现两个档案的大小和权限一模一样,就像copy过去的。cat命令查看catfile档案,确认filetest内容被写入到catfile档案中。
用<<设置结束的输入字符,在示例10中,不用标准输入,用cat >指令输入,我提到用Ctrl+d结束输入,而<<可以设置结束的输入字符
示例11:用<<设置结束的输入字符
如图,rm指令先删除示例10中建立的catfile和filetest,cat > catfile <<”aaa”语句中设置了结束字符串aaa
在下面的cat输入环境中,输入aaa表示我要结束输入。
查看catfile
发现结束字符没有被输入到catfile中,它仅表示我要结束输入而已。
什么时候需要使用命令输出重导向?
屏幕输出的信息很重要需要保存下来。
背景执行中的程序不希望它干扰屏幕的输出结果。
一些系统例行命令(例如卸载/etc/crontab中的档案)的执行结果希望能保存下来。
一些命令执行的已知错误信息,希望把它丢掉。
错误讯息与正确讯息需要分别输出。
5.5.命令执行的判断依据:;,&&,||
当我们输入多重指令并希望一次性执行时,需要了解;符号,&&符号,||符号的作用。
;符号不多说,写过代码的都知道,两条语句用;号隔开,执行完一条后再执行另一条。
如图所示
我希望先删除catfile档案再重启linux。两条语句中间如果不用;号隔开,执行时会有错误信息。
$?(指令回传值)与&&或||
每条指令执行结束后都会回传执行正确或错误给?变量。没错,?也是一个变量。之所以在这里说这个指令回传值,是因为它与&&和||的使用有关
指令下达情况 | 说明 |
---|---|
cmd1&&cmd2 | 1.若cmd1执行完毕且执行正确($?=0),开始执行cmd2 2.若cmd1执行完毕且执行错误($?≠0),cmd2不执行 |
cmd1||&cmd2 | 1.若cmd1执行完毕且执行正确($?=0),cmd2不执行 2.若cmd1执行完毕且执行错误($?≠0),开始执行cmd2 |
示例1:判断目录是否存在,若存在,在该目录下新建文件夹mytest,若不存在,则不新建。 | |
如图,/home/huyan目录是存在的,因此在该目录下新建mytest文件夹也应该成功了,观察一下结果 | |
将/home/huyan目录改成一个不存在的目录,结果如下 | |
因为第一条语句中的目录不存在,因此第二条语句也没有被执行。 | |
示例2:判断目录是否存在,若不存在,新建该目录,若存在,则不新建(一个和示例1完全相反的命题) | |
错误信息告诉我们/home/huyantest目录并不存在,那么按照 | |
如果第一条语句中的目录已存在呢? | |
如图,huyantest1是刚刚被新建的,已存在的目录,因此第二条语句没有被执行。 | |
示例3:不管/home/huyantest目录是否存在,都要建立/home/huyantest/test档案 | |
从上面知道,/home/huyantest目录其实是不存在的,代码这样写 | |
如图所示,虽然有错误信息提示目录不存在,但/home/huyantest/test档案到底有没有被建立呢?查看一下 | |
如图所示,/home/huyantest目录已经被建立了 | |
如图所示,/home/huyantest/test档案已经被建立了。 | |
下面来解释一下cd /home/huyantest |
6.管线命令
将命令执行结果经过加工后变成我们想要的格式再输出,需要用到管线命令。管线命令用|界定符。
示例1:查询/etc目录下有多少档案
输入ls /etc查询,结果如下
如图,太多了,一页根本看不完,利用管线命令可以将输出结果变成下图格式
输入ls /etc|less
如图,less的作用是向前或向后随意浏览文件,不会一次性加载所有文件,按回车加载下一个文件。这样看就方便多了。按q按键可以返回到命令提示行。
ls /etc|less的运行逻辑是这样的,ls /etc执行后将结果传递给less命令,less命令经过处理后把最后结果输出。
如图,每个管线后面第一个接的必须是指令,且该指令必须能够接受stdin数据输入,这样的命令才是管线命令。管线命令可以接很多指令,但要注意以下几点:
管线命令中后一个命令仅会处理前一个命令的stdout,对前一个命令的stderr会予以忽略
管线命令中后一个命令必须能够接受前一个指令的数据成为自己的stdin才行。
下面介绍一下常见的能用在管线命令中的命令
6.1.撷取命令:cut,grep
将一段数据经过分析后取出我们想要的部分,或者分析关键词然后取出我们想要的部分。通常是一行一行来分析的,不是整篇分析。
6.1.1.cut
cut的语法如下
cut -d ‘分隔字符’ -f fields:把数据按分隔字符分块,取第fields块
cut -c 字符区间:取出该字符区间的数据
示例1:取出PATH变量中第五个路径
把PATH变量按:分块,取出第5块数据,就是/usr/sbin
示例2:取出export指令输出结果中第12个字符串以后的内容
export指令运行结果如图所示
运行管线命令,结果如图所示
发现每行数据前面的declare -x都不见了。
cut指令主要的用途用于同一行的数据分解。
6.1.2.grep
刚刚cut指令分析整行信息,取出我们想要的,而grep指令时分析整行信息,若其中有我们想要的,取出整行信息。
grep的语法如下:
grep [-acinv] [--color=auto] ‘搜寻字符串’ filename
-a:将binary档案以text档案的方式搜寻数据
-c:计算找到‘搜寻字符串’的次数
-i:搜寻过程忽略大小写不同
-n:顺便输出行号
-v:反向选择,显示出没有‘搜寻字符串’的行
--color=auto:将找到的内容中的关键词加上颜色显示出来
示例1:将last指令的结果中关于huyan用户的信息显示出来并加上颜色
示例2:将last指令的结果中除去reroot和root的其他信息显示出来
示例3:在last的输出讯息中,只要有huyan就取出且只取第一列,并且显示行号
6.2.排序命令:sort,uniq,wc
6.2.1.sort
sort指令可以帮我们排序,并且依据不同数据形态来排序。例如数字和文字的排序就不一样,此外排序的字符与语系的编码有关,所以用sort指令排序前,最好先用LANG=C来让语系统一比较好。
sort指令的语法和参数
-f:忽略大小写的差异
-b:忽略最前面的空格符部分
-M:以月份的名字来排序,例如JAN,DEC等的排序方法
-n:使用纯数字来排序(默认是使用文字形态来排序的)
-r:反向排序
-u:uniq,即相同的数据中只出现一行为代表
-t:分隔符,预设是用Tab键来分隔
-k:以某个区间来排序的意思
示例1:个人账号信息都记录在/etc/passwd下,将账号进行排序
示例2:/etc/passwd的内容是以:为分隔符来分隔的,我想以第三栏来排序
上图所示,发现第三栏是数字,并且没有按照数字来排序,因为sort预设是按文字模式排序的。
示例3:将示例2中的第三栏按数字大小排序
示例4:将/etc/passwd的数据仅取账号排序并输出
6.2.2.uniq
重复的资料仅列出1个显示。
uniq的语法和参数
uniq [-ic]
-i:忽略大小写字符的不同
-c:进行计数
示例1:对last的输出数据进行统计,输出用户登录名和登录次数
注:wtmp和第一行的1都是last的默认字符,可以忽略的。在上图的语句中,uniq指令和sort指令不能互换位置。
6.2.3.wc
我想知道一个档案里有多少字,多少行,多少字符,可以用wc指令进行统计。wc指令用于计算输出的数据的整体信息。
wc指令的语法和参数
wc [-lwm]
-l:仅列出行数
-w:仅列出字数
-m:仅列出字符数
示例1:计算/etc/man_db.conf档案里有多少行,多少字,多少字符
注:wc指令不带参数的时候默认是按行数,字数,字符数来显示的。
示例2:在last指令的输出结果中,取出这个月登录系统的总次数
如图所示,解释一下这行代码,对last指令的输出结果,取出以[a-z]开头和以[A-Z]开头的行(即去除空白行),然后再将结果去除掉wtmp那行,将剩下的结果计算行数,就是所有用户登陆系统的总次数
6.3.双向重导向:tee
前面数据流重定向是将stdout或stderr传送给文档或设备,而不在屏幕上输出。而双向重定向tee会将数据流同时传送给文件与屏幕。
Tee的语法和参数:
Tee [-a] file
-a:以累加的方式将数据加入到file中
注:如果不加-a参数,是将数据以覆盖的方式加入到file中
示例1:将last命令的结果存储到/root/last.list档案中并且将第一栏登陆名输出到屏幕上。
如图,数据输出到屏幕上,那么我们来看看/root/last.list中有没有这些数据。
为什么last.list档案中的内容和屏幕输出内容不一样呢,让我们来分析一下这个管线命令:last|tee –a last.list|cut –d “ ” –f1
last命令的查询结果作为标准输入给tee –a last.list语句,经过这条语句,last查询结果既在屏幕输出,又被传送到last.list档案中,而cut –d “ ” –f1语句是将tee –a last.list的结果取第一栏显示在屏幕上,而不对last.list档案中的内容做修改,因此屏幕显示结果和last.list档案中的内容是不同的。
示例2:将ls –al的数据保存一份到/root/homefile档案,同时屏幕也有输出信息。
6.4.字符转换命令:tr,col,join,paste,expand
我们在vim程序编辑器中提过DOS断行字符与UNIX断行字符不同,需要用dos2unix命令和unix2dos命令互相转换。这章就来介绍一下管线命令中的字符转换命令。
6.4.1.tr
tr命令能删除或替换一段信息中的文字。
tr的语法和参数
tr [-ds] SET1...
-d:删除一段信息中的SET1字符串
-s:替换掉重复的字符
示例1:将ls –al /home中所有的小写字母替换成大写字母
注:’[a-z]’和’[A-Z]’不加单引号也可以执行。
示例2:将etc/passwd中的:号删除
示例3:将/etc/passwd转存成dos断行到/root/passwd
图中所示,先将/etc/passwd档案copy到/root/passwd中,在这一步完成的情况下进行下一步:利用unix2dos指令将/root/passwd转换成dos断行。然后利用file指令查看档案类型,验证结果。其中with CRLF line terminators就是dos断行。
查看/etc/passwd档案和/root/passwd档案,发现两者的大小并不相等,如下图所示
利用tr命令删除dos断行字符,将删除后的档案数据重定向到/root/passwd.li,并再次比较两者大小,如图所示
这样两个档案大小就相等了。这个命令也可以写在正则表达式中,因为它也是由正则表达式的方式来替换数据的。示例3中,可以去除dos文件留下来的断行符。这相当有用,DOS下面会自动在每行行末加入特殊的断行符号^M,这个时候我们可以用tr命令来将^M去除。
6.4.2.col
col的语法和参数
col [-xb]
-x:将Tab键转换成对等的空格键
-b:在文字内有反斜杠/时,仅保留反斜杠最后接的那个字符
示例1:利用cat –A显示出/etc/man_db.conf,最后用col将[Tab]转换成空格键。
输入命令cat –A /etc/man_db.conf,结果如图所示(只取一部分数据比较)
执行下列命令:
结果如图所示(只取一部分数据比较)
虽然col有它特殊的用途,不过很多时候,col指令可以用来做简单处理,如将Tab按键替换为空格键。此外,col经常被利用与将man page转存为纯文本文件以方便查阅的功能。(注:鸟哥的书里此处还有个示例2,但我用CentOS 7操作结果与鸟哥不同,无法做这个示例)
6.4.3.join
用于处理两个文件之间的数据,而且主要是将两个文件当中有相同数据的那一行加在一起。(需要两个档案有数据相关性)
join的语法和参数:
join [-ti12] file1 file2
-t:join默认以空格符分隔数据,并且对比第一个字段的数据,如果两个文件相同,则将两条数据连成一行,且第一个字段放在第一个。
-i:忽略大小写的差异
-1:代表第一个文件要用哪个字段来分析的意思
-2:代表第二个文件要用哪个字段来分析的意思
示例1:利用root的身份将/etc/passwd与/etc/shadow相关数据整合成一行。
首先我们先来看一下这两个文件前三行的内容
可以观察到这两个档案前三行数据每行都是以账号开头,以:号为分隔符
示例2:/etc/passwd第四个字段和/etc/group第三个字段都是GID,将两个档案整合
两个档案前三行如图所示
执行下列语句
如图所示,相同部分会放在行首
注:在使用join前,所需处理的档案应先经过排序(sort)处理
6.4.4.paste
paste指令将两行贴在一起,中间用Tab键隔开,不需要两个档案有数据相关性。
paste指令的语法和参数
paste [-d] file1 file2
-d:后面可以接分隔字符,预设是以Tab键来分隔的
- :如果file部分写成-,表示来自stdin的资料
示例1:将/etc/passwd和/etc/shadow相同行贴在一起且仅显示前三行
示例2:将/etc/group与示例1贴在一起,且仅显示前三行
示例3:先取出(cat)/etc/group档案,与示例1贴在一起,且仅显示前三行
如图所示,-表示数据来自标准输入。
6.4.5.expand
expand指令将Tab键转换成空格键。
expand的语法和参数
expand [-t] file
-t:后面可以接数字。一个Tab键可以用8个空格键替代。我们也可以自定义一个Tab键代表多少个字符。
示例1:将/etc/man_db.conf内行首为MANPATH的字样取出,且仅取前三行
示例2:将示例1中结果中所有符号都列出来
如图所示,Tab键可以被cat -A显示成^I
示例3:将示例2中tab键设置成6个字符
如图所示,同paste指令,-代表数据来自stdin,将tab键设置为6个字符,结果中MANPATH中的MANPATH中的首字母M和/bin隔了12个字符。
修改一下tab键的字符长度,如下图所示:
注:还可以通过unexpand将空格键转换成tab键,不细说了
6.5.分割命令:split
如果档案太大导致无法复制到便携式装置,可以利用split指令分割档案,split指令能根据行数或档案大小将大档案分割成小档案。
split的语法和参数
split [-bl] file PREFIX
-b:后面可接欲分割成的档案大小,可接单位,如b,k,m等
-l:以行数来进行分割
示例1:将/root/vimrx以30行分割,并存储到/root/vimrxtest中
原文件大小如图所示
执行下列语句
可以看到,分割后文件名以aa,ab,ac...编号
示例2:将示例1中3个小档案合成一个档案命名为vimrxtest
如图,使用数据流重定向就好了。
示例3:使用ls -al /输出的数据中,每20行记录成一个档案存储在/tmp中,命名为test*
6.6.参数代换:xargs
xargs指令能读入stdin的数据,并且以空格符为分隔号将stdin的数据分隔成参数
xargs的语法和参数
xargs [-0epn] command
-0:如果stdin中含有特殊字符如`,/,空格符等,-0可以将它还原成一般字符
-e:EOF,end of file的意思,后面可以跟一个字符串,当xargs分析到这个字符串时就停止工作
-p:在执行每个指令的参数时都会询问使用者的意思
-n:接次数,每次执行command指令时,要使用几个参数的意思
示例1:将/etc/passwd中的第一栏取出,仅取3行,然后用finger指令将每个账号内容显示到屏幕上
xargs finger是将三个账号的名称变成finger指令后面要跟的三个参数。
示例2:同示例1,单每次执行finger指令时都要询问使用者是否执行
示例3:将所有/etc/passwd中的账号都以finger指令查询,但一次只查询2个账号
如图,会每显示2个账号询问1次
注:某些指令后可以接的参数是有限制的,不能无限制累加,因此可以使用-n参数将指令的参数分成数个部分,每个部分分别以指令来执行。
示例4:同上,但当分析到lp时就结束这个指令
如图,该指令执行错误了,为什么呢,因为-e和’lp’之间是没有空格键的。
正确指令如下
lp用户本身不被输出到屏幕。
xargs的用途在于,很多命令并不支持管线命令,我们可以使用xargs指令来提供该指令的stdin。
示例5:找出/sbin下具有特殊权限的档名,并使用ls -al列出详细属性
执行下列语句
结果如图所示
结果竟然列出了当前目录下所有档名,语句并没有被顺利执行,这是因为ls -al不是管线命令的关系。
在ls -al前加上xargs就可以顺利执行了。(按照鸟哥的语句查找/sbin目录下具有特殊权限的文件,一直查找不出来,并且因为findutils版本关系,在CentOS 7中不能用-perm +omode这种写法了,要改成-perm /omode,但即使如此我依然没能实现)
示例6:查找/root下含有.bash*的档名并且用ls -al列出属性
如图所示,因为ls -al不是管线命令,因此在前面加上xargs就可以成功执行了。
6.7.关于减号-的用途
管线命令在bash的连续处理程序上是十分重要的,在管线命令中常常会使用前一个命令的stdout作为下一个命令的stdin,某些指令需要利用到文件名来进行处理时,可以用-号代替。
例子不举了前面有。