Linux(3):Shell基础
Linux(1):简介与系统安装:https://mp.****.net/postedit/88633915
Linux(2):常用命令:https://mp.****.net/postedit/88639718
鸟哥的Linux私房菜:https://wizardforcel.gitbooks.io/vbird-linux-basic-4e/content/86.html
目录
9.2.6 变量键盘读取、数组与声明:read,array,declare
9.6.3 输入重定向(standard input : < 与 <<)
9.9.2 bash 的登录与欢迎信息:/etc/issue,/etc/motd
9 Shell基础
9.1 shell概述
9.1.1 shell概述
管理整个计算机硬件的其实是操作系统的内核(kernel),这个内核是需要被保护的,所以我们一般用户就只能通过shell来跟内核通信,以让内核达到我们线索想要达到的工作。我们必须要通过“shell”(内核)将我们输入的命令与内核通信,好让内核可以控制硬件来正确无误地工作。
Linux中的标准的默认的 shell 是 Bash 。
已进入系统就用到的shell就是父shell,可以用base命令,调用子shell。
9.1.2 bash shell 的内置命令:type
type命令 用来显示指定命令的类型,判断给出的指令是内部指令还是外部指令。
命令类型:
- alias:别名。
- keyword:关键字,Shell保留字。
- function:函数,Shell函数。
- builtin:内建命令,Shell内建命令。
- file:文件,磁盘文件,外部命令。
- unfound:没有找到。
语法:
type(选项)(参数)
选项:
-t:输出“file”、“alias”或者“builtin”,分别表示给定的指令为“外部指令”、“命令别名”或者“内部指令”;
-p:如果给出的指令为外部指令,则显示其绝对路径;
-a:在环境变量“PATH”指定的路径中,显示给定指令的信息,包括命令别名。
参数:
指令:要显示类型的指令。
通过 type 这个命令我们可以知道每个命令是否为 bash的内置命令。此外由于利用 type 找到后面的名称时,如果后面接的名称并不能以执行文件的状态被找到,那么该名称是不会被显示出来的。就是说type主要在找出“执行文件”而不是一般文件名。所以,这个 type 也可以用来作为类似 which命令 的用途了。
如果命令太长,可以使用 “\[enter]” 来将 [enter] 这个按键“转义”,让 [enter] 不再具有“开始执行”的功能,从而进行换行。如果顺利转义[enter] 后,下一行最前面就会出现>的符号,可以继续输入命令。
9.2 shell的变量功能
9.2.1 什么是变量
变量是以一组文字或者符号等,来替代一些设置或者是一串保留的数据。例如,我们每个账号的邮件信箱默认是以 MAIL 这个变量来进行访问的,当 dnteal 这个用户登录时,他便会取得 MAL 这个变量,而这个变量的内容其实就是var/spool/mail/dmtsai,那如果 vbird 登录呢?他取得的MAIL这个变量的内容其实就是 /var/spoonaiwbird 。而我们使用信件读取命令 mail 来读取自己的邮件信箱时,这个程序可以直接读取 MAIL 这个变量的内容,就能够自动分辨出属于自己的信箱信件了。
9.2.2 变量的显示与设置:echo、unset
变量的显示可以使用 echo,只需要在变量名称前面加上$,或者以${变量}的方式显示都可以:
变量的设置规则:
1. 变量与内容以一个等号 [=] 来连接,如下所示:
[myname=VBird]
2. 等号两边不能直接接空格符,如下所示为错误:
[myname = VBird] 或 [myname=VBird Tsai]
3. 变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误:
[2myname=VBird]
4. 变量内容若有空格符可使用双引号["]或单引号[']将变量内容结合起来,但
- 双引号内的特殊字符如$等,可以保有原来的特性,如下所示:
[var="lang is $LANG"] 则 [echo $var] 可得 [lang is zh_TW.UTF-8]
- 单引号内的特殊字符则仅为一般字符(纯文本),如下所示:
[var='lang is $LANG'] 则 [echo $var] 可得 [lang is $LANG]
5. 可用转义字符[ \ ]将特殊符号(如 [Enter],$,\,空格符,' 等)变成一般字符,如:
[myname=VBird\ Tsai]
6. 在一串指令的执行中,还需要通过其他的命令提供的信息时,可以使用反单引号 [`指令`] 或 [$(指令)] 。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号!在一串命令中,在 ` 之内的命令将会被先执行,而其执行出来的结果将作为外部的输入信息。例如想要取得核心版本的设定:
[version=$(uname -r)] 再 [echo $version] 可得 [3.10.0-229.e17.x86_64]
7. 若该变量为了增加变量内容时,则可用 “$变量名称” 或 ${变量} 累加内容,如下所示:
[PATH="$PATH":/home/bin] 或 [PATH=${PATH}:/home/bin]
8. 若该变量需要在其他子程序执行,则需要以 export 来使变量变成环境变量:
[export PATH]
9. 通常大写字符为系统默认变量,自行设定变量可以使用小写字符,方便判断(纯粹依照使用者兴趣与嗜好);
10.取消变量的方法为使用 unset : [uset 变量名称] 例如取消 myname 的设定:
[unset myname]
例如:
① 如何让我刚才设置的name=VBird可以用在下个shell的程序?
[[email protected] ~]# name=VBird
[[email protected] ~]# bash <==进入到所谓的子进程
[[email protected] ~]# echo $name <==子进程:再次echo 一下;
<==结果是空的,并没有刚才设置的内容。
[[email protected] ~]# exit
[[email protected] ~]# export name
[[email protected] ~]# bash <==进入到所谓的子进程
[[email protected] ~]# echo $name <==子进程:在此执行
VBird <==看吧!出现设置值了
[[email protected] ~]# exit <==子进程:离开这个子进程
什么是“子进程”呢?就是说在我目前这个shell的情况下,去打开另一个新的shell,新那个shell就是子进程。在一般的状态下,父进程的自定义变量是无法在子进程内使用的。但是通过 export 将变量变成环境变量后,就能够在子进程下面应用了。
② 知何进入到你目前内核的模块目录?
[[email protected] ~]# cd /1ib/modules/"uname -r"/kernel
[[email protected] ~]# ed /1tb/aodulas/$(uname -r)/kornel
每个Unux都能够拥有多个内核版本,且几乎distribution的所有内核版本都不相同。
③ 每个crontab相关文件名的权限
ls -l `locate crontab`
9.2.3 环境变量的功能(env、set、export)
(1)env
使用 env 列出目前的 shell 环境下的所有环境变量与其内容。env 是environment(环境)的缩写。
- HOME:代表用户的主文件夹,可以使用cd~、cd 直接回到用户主文件夹。
- SHELL:告知我们,目前这个环境使用的 SHELL 是哪个程序? Linux 默认使用 /bin/bash 的。
- HISTSIZE:这个与『历史命令』有关,亦即是我们曾经下达过的命令可以被系统记录下来,而记录的 “条数” 则是由这个值来配置的。
- MAIL:当我们使用 mail 这个命令在收信时系统会去读取的邮件信箱文件(mailbox)。
- PATH:就是执行文件查找的路径,目录与目录中间以冒号(:)分隔,由于文件的查找是依序由PATH变量内的目录来查询,所以目录的顺序也是重要的。
- LANG:这个重要。就是语系数据,很多信息都会用到它。举例来说,当我们在启动某些 Perl 的程序语言文件时,它会主动去分析语系数据文件,如果发现有它无法解析的编码语系,可能会产生错误。一般来说,我们中文编码通常是zh_CN.gb2312或者是zhCN.UTF-8,这两个编码偏偏不容易被解译出来,所以有的时候,可能需要修改一下语系数据。
- HRANDOM:这是“随机数”的变量。目前大多数的distributions都会有随机数生成器,那就是 /dov/random 这个文件。我们可以通过这个随机数文件相关的变量($RANDOM)来随机取得随机数值。BASH的环境下,这个RANDOM变量的内容介于0~32767之间,所以你只要 echo $RANDOM时,系统就会主动随机取出一个介于0~32767的数值。万一我想要使用0~9
间的数值呢?利用declare声明数值类型,然后这样做就可以了;
[[email protected] ~]# declare -i number=$RANDOM*10/32768;echo $number
8 <==此时会随机取出0~9之间的数值。
(2)set
用 set 观察所有变量 (含环境变量与自定义变量)
一般来说,不论是否为环境变量,只要跟我们目前这个 shell 的操作接口有关的变量,通常都会被设置为大写字符,也就是说,基本上,在Linux 默认的情况下,使用 {大写的字母} 来设置的变量一般为系统内定需要的变量。
一些较为重要的系统内定变量:
① PS1:(提示字符的配置)
这是 PS1 (数字的 1 不是英文字母),这个东西就是我们的“命令提示字符”。当我们每次按下 [Enter] 按键去运行某个命令后,最后要再次出现提示字符时, 就会主动去读取这个变量值了。上面 PS1 内显示的是一些特殊符号,这些特殊符号可以显示不同的信息, 每个 distributions 的 bash 默认的 PS1 变量内容可能有些许的差异,你可以用 man bash 去查询一下 PS1 的相关说明,以理解下面的一些符号意义。
- \d :可显示出『星期 月 日』的日期格式,如:"Mon Feb 2"
- \H :完整的主机名。举例来说,鸟哥的练习机为『www.vbird.tsai』
- \h :仅取主机名在第一个小数点之前的名字,如鸟哥主机则为『www』后面省略
- \t :显示时间,为 24 小时格式的『HH:MM:SS』
- \T :显示时间,为 12 小时格式的『HH:MM:SS』
- \A :显示时间,为 24 小时格式的『HH:MM』
- \@ :显示时间,为 12 小时格式的『am/pm』样式
- \u :目前使用者的账号名称,如『root』;
- \v :BASH 的版本信息,如鸟哥的测试主板本为 3.2.25(1),仅取『3.2』显示
- \w :完整的工作目录名称,由根目录写起的目录名称。但主文件夹(家目录)会以 ~ 取代;
- \W :利用 basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
- \# :执行的第几个命令。
- \$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $ 啰~
例如:更改PS1变量的值
[[email protected] ~ ]# cd /home
[[email protected] home]# PS1='[\[email protected]\h \w \A #\#]\$ '
[[email protected] /home 17:02 #85]#
② $:(关于本 shell 的 PID)
“$”本身也是个变量。这个代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。想要知道我们的 shell 的 PID ,就可以用: echo $$ 即可!出现的数字就是你的 PID 号码。
③ ?:(关于上个运行命令的回传值)
问号也是一个特殊的变量。 这个变量是:『上一个运行的命令所回传的值』, 上面这句话的重点是『上一个命令』与『回传值』两个地方。当我们运行某些命令时, 这些命令都会回传一个运行后的代码。一般来说,如果成功的运行该命令, 则会回传一个 0 值,如果运行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。
例如:
[[email protected] ~]# echo $SHELL
/bin/bash <==可顺利显示!没有错误!
[[email protected] ~]# echo $?
0 <==因为没问题,所以回传值为 0
[[email protected] ~]# 12name=VBird
-bash: 12name=VBird: command not found <==发生错误了!bash回报有问题
[[email protected] ~]# echo $?
127 <==因为有问题,回传错误代码(非为0)
# 错误代码回传值依据软件而有不同,我们可以利用这个代码来搜寻错误的原因喔!
[[email protected] ~]# echo $?
0
# 咦!怎么又变成正确了?这是因为 "?" 只与『上一个运行命令』有关,
# 所以,我们上一个命令是运行『 echo $? 』,当然没有错误,所以是 0 没错!
④ OSTYPE, HOSTTYPE, MACHTYPE:(主机硬件与核心的等级)
目前个人计算机的 CPU 主要分为 32/64 位,其中 32 位又可分为 i386, i586, i686,而 64 位则称为 x86_64。 由于不同等级的 CPU 命令集不太相同,因此你的软件可能会针对某些 CPU 进行优化,以求取较佳的软件性能。你可以在 x86_64 的硬件上安装 i386 的 Linux 操作系统,但是你无法在 i686 的硬件上安装 x86_64 的 Linux 操作系统。
(3) export: 自定义变量转成环境变量
因为子进程仅会继承父进程的环境变量,子进程不会继承父进程的自定义变量,所以你原本 bash 中的自定义变量在进入了子进程之后就会消失不见,一直到你离开了子进程并回到原本的父进程后,这个变量才会又出现。如你想要让变量内容继续的在子程序中使用,那么可以用:
[[email protected] ~]# export 变量名称
如果仅执行 export 而没有接变量时,那么此时将会把所有的“环境变量”显示出来。
9.2.4 影响显示结果的语系变量(locale)
(1)查看Linux支持的语言:
(2)如何修订这些编码
当使用 locale 时,系统时列出目前Linux 主机内保有的语系文件,这些语系文件都放置在 /usr/lib/locale/ 这个目录中。
[[email protected] ~]# locale <==后面不加任何选项与参数即可!
LANG=en_US <==主语言的环境
LC_CTYPE="en_US" <==字符(文字)辨识的编码
LC_NUMERIC="en_US" <==数字系统的显示信息
LC_TIME="en_US" <==时间系统的显示数据
LC_COLLATE="en_US" <==字符串的比较与排序等
LC_MONETARY="en_US" <==币值格式的显示等
LC_MESSAGES="en_US" <==信息显示的内容,如菜单、错误信息等
LC_ALL= <==整体语系的环境
如果其他语系变量都未设置,且你有设置 LANG 或者是LC_ALL时,则其他的语系变量就会被这两个变量所替代。这也是为什么我们在 Linux 当中,通常说明仅设置 LANG 这个变量而已,因为它是最主要的设置变量。
默认的语系定义在:
[[email protected] ~]# cat /etc/sysconfig/i18n
LANG="zh_TW.UTF-8"
9.2.5 变量的有效范围
被 export 后的变量,我们可以称他为『环境变量』! 环境变量可以被子程序所引用,但是其他的自定义变量内容就不会存在于子程序中。
为什么环境变量的数据可以被子进程所引用呢?这是因为内存配置的关系。理论上是这样的:
- 当启动一个shell,操作系统会分配一记忆块给shell使用,此内存内的变量可让子进程取用;
- 若在父进程利用export功能,可以让自定义变量的内容写到上述的记忆块当中(环境变量);
- 当加载另一个shell时(即启动子进程,而离开原本的父进程了),子shell可以将父shell的环境变量所在的记忆块导入自己的环境变量块当中。
通过这样的关系,我们就可以让某些安量在相关的进程之间存在,以帮助自己更方便地操作环境。不对要提醒的是,这个 “环境发量” 与 “bash的操作环境” 意思不大一样。举例来说,PS1并不是环境变量,但是过个PS1会影响到比bash的接口(提示符)。
9.2.6 变量键盘读取、数组与声明:read,array,declare
(1)read
要读取来自键盘输入的变量,就是用 read 这个命令:
[[email protected] ~]# read [-pt] variable
选项与参数:
- -p :后面可以接提示字符!
- -t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者!
例如:
① 让用户由键盘输入一内容,将该内容变成名为 atest 的变量
[[email protected] ~]# read atest
This is a test <==此时光标会等待你输入!请输入左侧文字看看
[[email protected] ~]# echo $atest
This is a test <==你刚刚输入的数据已经变成一个变量内容!
② 提示使用者 30 秒内输入自己的大名,将该输入字符串作为名为 named 的变量内容
[[email protected] ~]# read -p "Please keyin your name: " -t 30 named
Please keyin your name: VBird Tsai <==注意看,会有提示字符喔!
[[email protected] ~]# echo $named
VBird Tsai <==输入的数据又变成一个变量的内容了!
(2)declare / typeset
declare 或 typeset 是一样的功能,就是声明量的类型。如果使用 declare 后面并没有接任何参数,那么 bash 就会主动将所有的变量名称与内容全部调出来,就好像使用 set 一样。
[[email protected] ~]# declare [-aixr] variable
选项与参数:
- -a :将后面名为 variable 的变量定义成为数组 (array) 类型
- -i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
- -x :用法与 export 一样,就是将后面的 variable 变成环境变量;
- -r :将变量配置成为 readonly 类型,该变量不可被更改内容,也不能 unset
范例一:让变量 sum 进行 100+300+50 的加总结果
[[email protected] ~]# sum=100+300+50
[[email protected] ~]# echo $sum
100+300+50 <==咦!怎么没有帮我计算加总?因为这是文字型态的变量属性啊!
[[email protected] ~]# declare -i sum=100+300+50
[[email protected] ~]# echo $sum
[[email protected] ~]# 450
bash 对于变量有几个基本的定义:
- 变量类型默认为“字符串”,所以若不指定变量类型,则1+2为一个“字符串”而不是“计算式”,所以上述第一个执行的结果才会出现那个情况;
- bash 环境中的数值运算,默认最多仅能到达整数形态,所以 1/3 结果是 0
如果不小心将变量设置为“只读”,通常得要注销再登录才能复原该变量的类型。
如果需要非字符串类型的变量,那就得要进行变量的声明才行。
范例二:将 sum 变成环境变量
[[email protected] ~]# declare -x sum
[[email protected] ~]# export | grep sum
declare -ix sum="450" <==果然出现了!包括有 i 与 x 的宣告!
范例三:让 sum 变成只读属性,不可更动!
[[email protected] ~]# declare -r sum
[[email protected] ~]# sum=tesgting
-bash: sum: readonly variable <==不能改这个变量了!
范例四:让 sum 变成非环境变量的自定义变量吧!
[[email protected] ~]# declare +x sum <== 将 - 变成 + 可以进行『取消』动作
[[email protected] ~]# declare -p sum <== -p 可以单独列出变量的类型
declare -ir sum="450" <== 只剩下 i, r 的类型,不具有 x
(3)数组 (array)
数组 (array) 变量类型,在bash中,数组的设置方式是:var[index] = content 。
我有一个数组名为 var ,而这个数组的内容为 var[1]=小明,var[2]=大明, var[3]=好明 .... 等等,那个 index 就是一些数字,重点是用中刮号 ([ ]) 来配置的。
范例:配置上面提到的 var[1] ~ var[3] 的变量。
[[email protected] ~]# var[1]="small min"
[[email protected] ~]# var[2]="big min"
[[email protected] ~]# var[3]="nice min"
[[email protected] ~]# echo "${var[1]}, ${var[2]}, ${var[3]}"
small min, big min, nice min
数组的变量类型比较有趣的地方在于“读取”,一般来说,建议直接以 ${数组} 的方式来读取,会比较正确无误。
9.2.7 与文件系统及程序的限制关系:ulimit
Linux对于每个用户,系统限制其最大进程数。为提高性能,可以根据设备资源情况,设置各linux 用户的最大进程数。
ulimit主要是用来限制进程对资源的使用情况的,它支持各种类型的限制,常用的有:
- 内核文件的大小限制
- 进程数据块的大小限制
- Shell进程创建文件大小限制
- 可加锁内存大小限制
- 常驻内存集的大小限制
- 打开文件句柄数限制
- 分配堆栈的最大大小限制
- CPU占用时间限制用户最大可用的进程数限制
- Shell进程所能使用的最大虚拟内存限制
ulimit使用的基本格式为:ulimit [options] [limit]
具体的options参数含义如下表所示:
选项 含义:
- -a 显示当前系统所有的limit资源信息。
- -H 设置硬资源限制,一旦设置不能增加。
- -S 设置软资源限制,设置后可以增加,但是不能超过硬资源设置。
- -c 最大的core文件的大小,以 blocks 为单位。
- -f 进程可以创建文件的最大值,以blocks 为单位.
- -d 进程最大的数据段的大小,以Kbytes 为单位。
- -m 最大内存大小,以Kbytes为单位。
- -n 查看进程可以打开的最大文件描述符的数量。
- -s 线程栈大小,以Kbytes为单位。
- -p 管道缓冲区的大小,以Kbytes 为单位。
- -u 用户最大可用的进程数。
- -v 进程最大可用的虚拟内存,以Kbytes 为单位。
- -t 最大CPU占用时间,以秒为单位。
- -l 最大可加锁内存大小,以Kbytes 为单位。
其中ulimit -n用于限制进程能够打开的文件描述符的最大数目。因为任何设备在linux下都是文件,通信的接口也有专门的接口文件负责,所以linux下进程tcp链接的最大并发量也受限于该值。
想要复解ulimit的设置最简单的方法就是注销再登录,否则就是得要重新以 ulimit 设置才行,不让要注意的是一般身份用户如果以ulimit设置了 -f 的文件大小,那么他只能继续减小文件容量,不能增加文件容量。
dd命令:
Linux dd命令用于读取、转换并输出数据。dd可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。
参数说明:
- if=文件名:输入文件名,缺省为标准输入。即指定源文件。
- of=文件名:输出文件名,缺省为标准输出。即指定目的文件。
- ibs=bytes:一次读入bytes个字节,即指定一个块大小为bytes个字节。
obs=bytes:一次输出bytes个字节,即指定一个块大小为bytes个字节。
bs=bytes:同时设置读入/输出的块大小为bytes个字节。 - cbs=bytes:一次转换bytes个字节,即指定转换缓冲区大小。
- skip=blocks:从输入文件开头跳过blocks个块后再开始复制。
- seek=blocks:从输出文件开头跳过blocks个块后再开始复制。
- count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数。
- conv=<关键字>,关键字可以有以下11种:
- conversion:用指定的参数转换文件。
- ascii:转换ebcdic为ascii
- ebcdic:转换ascii为ebcdic
- ibm:转换ascii为alternate ebcdic
- block:把每一行转换为长度为cbs,不足部分用空格填充
- unblock:使每一行的长度都为cbs,不足部分用空格填充
- lcase:把大写字符转换为小写字符
- ucase:把小写字符转换为大写字符
- swab:交换输入的每对字节
- noerror:出错时不停止
- notrunc:不截短输出文件
- sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
- --help:显示帮助信息
- --version:显示版本信息
示例:
① 在Linux 下制作启动盘,可使用如下命令:
dd if=boot.img of=/dev/fd0 bs=1440k
② 将testfile文件中的所有英文字母转换为大写,然后转成为testfile_1文件,在命令提示符中使用如下命令:
dd if=testfile_2 of=testfile_1 conv=ucase
9.2.8 变量内容的删除、替代与替换
(1)变量内容的删除
范例一:先让小写的 path 自定义变量配置的与 PATH 内容相同
[[email protected] ~]# path=${PATH}
[[email protected] ~]# echo $path
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
范例二:假设我不喜欢 kerberos,所以要将前两个目录删除掉,如何显示?
[[email protected] ~]# echo ${path#/*kerberos/bin:}
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
上面范例的重点如下:
${variable#/*kerberos/bin:}
上面的特殊字体部分是关键词!用在这种删除模式所必须存在的
${variable#/*kerberos/bin:}
这就是原本的变量名称,以上面范例二来说,这里就填写 path 这个『变量名称』!
${variable#/*kerberos/bin:}
这是重点!代表『从变量内容的最前面开始向右删除』,且仅删除最短的那个
${variable#/*kerberos/bin:}
代表要被删除的部分,由于 # 代表由前面开始删除,所以这里便由开始的 / 写起。
需要注意的是,我们还可以透过通配符 * 来取代 0 到无穷多个任意字符。
范例三:我想要删除前面所有的目录,仅保留最后一个目录
[[email protected] ~]# echo ${path#/*:}
/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
# 由于一个 # 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看: /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[[email protected] ~]# echo ${path##/*:}
/root/bin
# 多加了一个 # 变成 ## 之后,他变成『删除掉最长的那个数据』。亦即是: /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
因为在 PATH 这个变量的内容中,每个目录都是以冒号“:”隔开的, 所以要从头删除掉目录就是介于斜线 (/) 到冒号 (:) 之间的数据!
但是 PATH 中不止一个冒号 (:) 啊! 所以 # 与 ## 就分别代表:
- # :符合取代文字的『最短的』那一个;
- ##:符合取代文字的『最长的』那一个。
上面谈到的是从前面开始删除变量内容,那么如果想要从后面向前删除变量内容呢?这个时候就要用上百分比(%)符号了。如下。
范例四:想要删除最后面那个目录,亦即从 : 到 bin 为止的字符串
[[email protected] ~]# echo ${path%:*bin}
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# 这个 % 符号代表由最后面开始向前删除!所以上面得到的结果其实是来自如下:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
范例五:那如果我只想要保留第一个目录呢?
[[email protected] ~]# echo ${path%%:*bin}
/usr/kerberos/sbin
# 同样的, %% 代表的则是最长的符合字符串,所以结果其实是来自如下:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
(2)变量内容的替换
范例六:将 path 的变量内容内的 sbin 取代成大写 SBIN:
[[email protected] ~]# echo ${path/sbin/SBIN}
/usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
# 关键词在于那两个斜线,两斜线中间的是旧字符串,后面的是新字符串,所以结果就会出现如上述的特殊字体部分。
[[email protected] ~]# echo ${path//sbin/SBIN}
/usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/SBIN:/usr/local/bin:/SBIN:/bin:
/usr/SBIN:/usr/bin:/root/bin
# 如果是两条斜线,那么就变成所有符合的内容都会被取代!
总结:
(3)变量的测试与内容替换
在某些时刻我们常常需要『判断』某个变量是否存在,若变量存在则使用既有的配置,若变量不存在则给予一个常用的配置。
我们举下面的例子来说明。
范例一:测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root
[[email protected] ~]# echo $username
<==由于出现空白,所以 username 可能不存在,也可能是空字符串
[[email protected] ~]# username=${username-root}
[[email protected] ~]# echo $username
root <==因为 username 没有配置,所以主动给予名为 root 的内容。
[[email protected] ~]# username="vbird tsai" <==主动配置 username 的内容
[[email protected] ~]# username=${username-root}
[[email protected] ~]# echo $username
vbird tsai <==因为 username 已经配置了,所以使用旧有的配置而不以 root 取代
在上面的范例中,重点在于减号“-”后面接的关键字。
new_var=${old_var-content}
新的变量,主要用来取代旧变量。新旧变量名称其实常常是一样的
new_var=${old_var-content}
这是本范例中的关键词部分!必须要存在的哩!
new_var=${old_var-content}
旧的变量,被测试的项目!
new_var=${old_var-content}
变量的『内容』,在本范例中,这个部分是在『给予未配置变量的内容』
不过这还是有点问题!因为 username 可能已经被配置为『空字符串』了!果真如此的话,那你还可以使用底下的范例来给予 username 的内容成为 root !
范例二:若 username 未配置或为空字符串,则将 username 内容配置为 root
[[email protected] ~]# username=""
[[email protected] ~]# username=${username-root}
[[email protected] ~]# echo $username
<==因为 username 被配置为空字符串了!所以当然还是保留为空字符串!
[[email protected] ~]# username=${username:-root}
[[email protected] ~]# echo $username
root <==加上“:”后若变量内容为空或者是未配置,都能够以后面的内容替换!
测试:先假设 str 不存在 (用 unset) ,然后测试一下等号 (=) 的用法:
[[email protected] ~]# unset str; var=${str=newvar}
[[email protected] ~]# echo var="$var", str="$str"
var=newvar, str=newvar <==因为 str 不存在,所以 var/str 均为 newvar
测试:如果 str 已存在了,测试一下 var 会变怎样?
[[email protected] ~]# str="oldvar"; var=${str=newvar}
[[email protected] ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容
如果旧变量不存在,整个测试旧告诉我“有错误”,此时就能够使用问好“?”,如下所示。
测试:若 str 不存在时,则 var 的测试结果直接显示 "无此变量"
[[email protected] ~]# unset str; var=${str?无此变量}
-bash: str: 无此变量 <==因为 str 不存在,所以输出错误信息
测试:若 str 存在时,则 var 的内容会与 str 相同!
[[email protected] ~]# str="oldvar"; var=${str?novar}
[[email protected] ~]# echo var="$var", str="$str"
var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容
9.3 脚本执行方式
9.3.1 echo
echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。
该命令的一般格式为:
echo [-ne][字符串]或 echo [--help][--version]
其中选项n表示输出文字后不换行;字符串能加引号,也能不加引号。用echo命令输出加引号的字符串时,将字符串原样输出;用echo命令输出不加引号的字符串时,将字符串中的各个单词作为字符串输出,各字符串之间用一个空格分割。
补充说明:echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开, 并在最后加上换行号。
参 数:
- -n 不要在最后自动换行
- -e 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\\ 插入\字符;
echo支持修改颜色。
变量的显示:echo。变量的显示能利用echo读出,只是需要在变量名称前面加上$,或者是以${变量}的方式来显示都可以。
9.3.2 第一个脚本
在脚本中第一行要写 #!/bin/bash ,以说明接下来的是标准的Linux shell脚本。
执行脚本的方法:
- 直接命令执行:先赋予执行权限,再使用相对路径或绝对路径调用;或者使用变量“PATH”功能,将hello.sh 放在PATH指定的目录内,例如:~/bin/
- 以bash进程来执行:通过 “bash hello.sh” 或 “sh hello.sh” 调用执行脚本。
如下图所示。
命令执行时输入命令太长时,利用“\[Enter]”来换行。
9.4 别名与快捷键
9.4.1 别名
Linux的标准shell是Bash。
如何知道目前有哪些的命令别名呢?就使用 alias :
也可使用alias进行修改:
修改是临时修改的,重启系统就会失效。
对每个用户单独生效。
通过source .bashrc可立即生效。
unalias也是临时删除,永久删除也需要修改 ~/.bashrc。
/bin/ls 为绝对路径,直接用该命令来执行; ls 为命令别名:alias ls='ls --color=auto'。
9.4.2 快捷键
9.5 历史命令
9.5.1 history
[[email protected] ~]# history [n]
[[email protected] ~]# history [-c]
[[email protected] ~]# history [-raw] histfiles
选项与参数:
- n :数字,意思是『要列出最近的 n 笔命令行表』的意思!
- -c :将目前的 shell 中的所有 history 内容全部消除
- -a :将目前新增的 history 命令新增入 histfiles 中,若没有加 histfiles ,则默认写入 ~/.bash_history
- -r :将 histfiles 的内容读到目前这个 shell 的 history 记忆中;
- -w :将目前的 history 记忆内容写入 histfiles 中!
范例一:列出目前内存内的所有 history 记忆
[[email protected] ~]# history
# 前面省略
1017 man bash
1018 ll
1019 history
1020 history
# 列出的信息当中,共分两栏,第一栏为该命令在这个 shell 当中的代码,另一个则是命令本身的内容喔!至于会显示几笔命令记录,则与 HISTSIZE 有关!
范例二:列出目前最近的 3 笔数据
[[email protected] ~]# history 3
1019 history
1020 history
1021 history 3
范例三:立刻将目前的数据写入 histfile 当中
[[email protected] ~]# history -w
# 在默认的情况下,会将历史纪录写入 ~/.bash_history 当中!
[[email protected] ~]# echo $HISTSIZE
1000
在正常的情况下,历史命令的读取与记录是这样的:
当我们以 bash 登陆 Linux 主机之后,系统会主动的由家目录的 ~/.bash_history 读取以前曾经下过的命令,那么 ~/.bash_history 会记录几笔数据呢?这就与你 bash 的 HISTFILESIZE 这个变量配置值有关了!
假设我这次登陆主机后,共下达过 100 次命令,『等我注销时, 系统就会将 101~1100 这总共 1000 笔历史命令升级到 ~/.bash_history 当中。』 也就是说,历史命令在我注销时,会将最近的 HISTFILESIZE 笔记录到我的纪录文件当中啦!当然,也可以用 history -w 强制立刻写入的!那为何用『升级』两个字呢? 因为 ~/.bash_history 记录的笔数永远都是 HISTFILESIZE 那么多,旧的信息会被主动的拿掉! 仅保留最新的!
vi /etc/profile
~/.bash_history 记录的是前一次登录以前所执行过的命令,而至于这一次登录所执行的命令都被暂存在临时内存中,当你成功注销系统后,该命令记忆才会记录到.bash_history中。
9.5.2 历史命令的调用
那么 history 这个历史命令只可以让我查询命令而已吗?当然不止,。 我们可以利用相关的功能来帮我们运行命令!
[[email protected] ~]# !number
[[email protected] ~]# !command
[[email protected] ~]# !!
选项与参数:
number :运行第几笔命令的意思;
command :由最近的命令向前搜寻『命令串开头为 command』的那个命令,并运行;
!! :就是运行上一个命令(相当于按↑按键后,按 Enter)
[[email protected] ~]# history
66 man rm
67 alias
68 man history
69 history
[[email protected] ~]# !66 <==运行第 66 笔命令
[[email protected] ~]# !! <==运行上一个命令,本例中亦即 !66
[[email protected] ~]# !al <==运行最近以 al 为开头的命令(上头列出的第 67 个)
9.5.3 命令和文件补全
系统命令的补全也依赖于$PATH环境变量。这个按键的功能就是在bash里面才有的。
命令与文件补全功能([Tab]按键的好处)
经常在bash环境中使用[Tab]是个很好的习惯,因为至少可以让你少打很多字,并且确定输入的数据是正确的。使用[Tab]按键的时机依据[Tab]接在命令后或参数后而有所不同。
- [Tab] 接在一串命令的第一个字的后面,则为命令补全;
- [Tab] 接在一串命令的第二个字以后时,则为文件补齐。
所以说,如果我想要知道我的环境中所有可以执行的命令有几个,就直接在bash的提示符后面连续按两次[Tab]按键就能够显示所有的可执行命令了。那如果想要知道系统当中所有以c为开头的命令呢?就按下“c[Tab][Tab]”就好。
9.6 数据流重定向
数据流重导向 (redirect) 由字面上的意思来看,好像就是将“数据给他传导到其他地方去”的样子? 没错~数据流重导向就是将某个指令执行后应该要出现在屏幕上的数据, 给他传输到其他的地方,例如文件或者是设备 (例如打印机之类的)!这在 Linux 的文字模式下面可重要的! 尤其是如果我们想要将某些数据储存下来时,就更有用了!
一般来说,如果你要执行一个指令,通常他会是这样的:
图1 指令执行过程的数据传输情况
我们执行一个指令的时候,这个指令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。 在上图当中, standard output 与 standard error output 分别代表“标准输出 (STDOUT)”与“标准错误输出 (STDERR)”, 这两个默认都是输出到屏幕上面来的。
standard output 与 standard error output
简单的说,标准输出指的是“指令执行所回传的正确的讯息”,而标准错误输出可理解为“ 指令执行失败后,所回传的错误讯息”。举个简单例子来说,我们的系统默认有 /etc/crontab 但却无 /etc/vbirdsay, 此时若下达“ cat /etc/crontab /etc/vbirdsay ”这个指令时,cat 会进行:
- 标准输出:读取 /etc/crontab 后,将该文件内容显示到屏幕上;
- 标准错误输出:因为无法找到 /etc/vbirdsay,因此在屏幕上显示错误讯息
9.6.1 标准输入输出
在Linux中,一切皆文件。
不管正确或错误的数据都是默认输出到屏幕上,所以屏幕当然是乱乱的!那能不能通过某些机制将这两股数据分开呢? 当然可以!那就是数据流重导向的功能啊!数据流重导向可以将 standard output (简称 stdout) 与 standard error output (简称 stderr) 分别传送到其他的文件或设备去,而分别传送所用的特殊字符则如下所示:
- 标准输入 (stdin) :代码为 0 ,使用 < 或 << ;
- 标准输出 (stdout):代码为 1 ,使用 > 或 >> ;
- 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;
9.6.2 输出重定向
- 1> :以覆盖的方法将“正确的数据”输出到指定的文件或设备上;
- 1>>:以累加的方法将“正确的数据”输出到指定的文件或设备上;
- 2> :以覆盖的方法将“错误的数据”输出到指定的文件或设备上;
- 2>>:以累加的方法将“错误的数据”输出到指定的文件或设备上;
要注意,“ 1>> ”以及“ 2>> ”中间是没有空格的。
Windows和Linux中都有定时任务。
此时错误输出时,大于号的右边没有空格。
例如:
(1)如果想要将正确的与错误的数据分别存入不同的文件中需要怎么做?
范例:将 stdout 与 stderr 分存到不同的文件去
[[email protected] ~]$ find /home -name .bashrc > list_right 2> list_error
(2)/dev/null 垃圾桶黑洞设备与特殊写法
/dev/null 可以吃掉任何导向这个设备的信息,结果不显示在屏幕,也不保存在文件里:
范例四:承范例三,将错误的数据丢弃,屏幕上显示正确的数据
[[email protected] ~]$ find /home -name .bashrc 2> /dev/null
/home/dmtsai/.bashrc <==只有 stdout 会显示到屏幕上, stderr 被丢弃了
(3)要将正确与错误数据通通写入同一个文件去呢?
范例五:将指令的数据全部写入名为 list 的文件中
[[email protected] ~]$ find /home -name .bashrc > list 2> list <==错误
[[email protected] ~]$ find /home -name .bashrc > list 2>&1 <==正确
[[email protected] ~]$ find /home -name .bashrc &> list <==正确
上述表格第一行错误的原因是,由于两股数据同时写入一个文件,又没有使用特殊的语法, 此时两股数据可能会交叉写入该文件内,造成次序的错乱。所以虽然最终 list 文件还是会产生,但是里面的数据排列就会怪怪的,而不是原本屏幕上的输出排序。 至于写入同一个文件的特殊语法如上表所示,你可以使用 2>&1 也可以使用 &> 。
为何要使用命令输出重导向呢?
- 屏幕输出的信息很重要,而且我们需要将他存下来的时候;
- 背景执行中的程序,不希望他干扰屏幕正常的输出结果时;
- 一些系统的例行命令 (例如写在 /etc/crontab 中的文件) 的执行结果,希望他可以存下来时;
- 一些执行命令的可能已知错误讯息时,想以“ 2> /dev/null ”将他丢掉时;
- 错误讯息与正确讯息需要分别输出时。
9.6.3 输入重定向(standard input : < 与 <<)
执行wc命令后,点“Ctrl+D”退出输入。
行数、单词数、字符数。
软件安装打补丁时可能会用到输入重定向。
以最简单的说法来说,“<” 就是“将原本需要由键盘输入的数据,改由文件内容来取代”的意思。
cat:
cat命令是linux下的一个文本输出命令,通常是用于观看某个文件的内容的;
cat主要有三大功能:
- 一次显示整个文件。$ cat filename;
- 从键盘创建一个文件。$ cat > filename 只能创建新文件,不能编辑已有文件;
- 将几个文件合并为一个文件。$cat file1 file2 > file
cat具体命令格式为 : cat [-AbeEnstTuv] [--help] [--version] fileName
参数说明:
- -n 或 --number:由 1 开始对所有输出的行数编号。
- -b 或 --number-nonblank:和 -n 相似,只不过对于空白行不编号。
- -s 或 --squeeze-blank:当遇到有连续两行以上的空白行,就代换为一行的空白行。
- -v 或 --show-nonprinting:使用 ^ 和 M- 符号,除了 LFD 和 TAB 之外。
- -E 或 --show-ends : 在每行结束处显示 $。
- -T 或 --show-tabs: 将 TAB 字符显示为 ^I。
- -A, --show-all:等价于 -vET。
- -e:等价于"-vE"选项;
- -t:等价于"-vT"选项;
示例:
清空 /etc/test.txt 文档内容:
cat /dev/null > /etc/test.txt
cat 也可以用来制作镜像文件。例如要制作软盘的镜像文件,将软盘放好后输入:
cat /dev/fd0 > OUTFILE
相反的,如果想把 image file 写到软盘,输入:
cat IMG_FILE > /dev/fd0
注:
- 1. OUTFILE 指输出的镜像文件名。
- 2. IMG_FILE 指镜像文件。
- 3. 若从镜像文件写回 device 时,device 容量需与相当。
- 4. 通常用制作开机磁片。
然后由下面的 cat 指令操作来了解一下什么叫做“键盘输入”:
范例六:利用 cat 指令来创建一个文件的简单流程
[[email protected] ~]$ cat > catfile
testing
cat file test
<==这里按下 [ctrl]+d 来离开
[[email protected] ~]$ cat catfile
testing
cat file test
由于加入 > 在 cat 后,所以那个 catfile 会被主动的创建,而内容就是刚刚键盘上面输入的那两行数据了。
能不能用纯文本文件取代键盘的输入,也就是说,用某个文件的内容来取代键盘的敲击呢? 如下所示:
范例七:用 stdin 取代键盘的输入以创建新文件的简单流程
[[email protected] ~]$ cat > catfile < ~/.bashrc
[[email protected] ~]$ ll catfile ~/.bashrc
-rw-r--r--. 1 dmtsai dmtsai 231 Mar 6 06:06 /home/dmtsai/.bashrc
-rw-rw-r--. 1 dmtsai dmtsai 231 Jul 9 18:58 catfile
# 注意看,这两个文件的大小会一模一样!几乎像是使用 cp 来复制一般!
<< 这个连续两个小于的符号,代表的是“结束的输入字符”的意思。举例来讲:“我要用 cat 直接将输入的讯息输出到 catfile 中, 且当由键盘输入 eof 时,该次输入就结束”,那我可以这样做:
[[email protected] ~]$ cat > catfile << "eof"
> This is a test.
> OK now stop
> eof <==输入这关键字,立刻就结束而不需要输入 [ctrl]+d
[[email protected] ~]$ cat catfile
This is a test.
OK now stop <==只有这两行,不会存在关键字那一行!
利用 << 右侧的控制字符,我们可以终止一次输入, 而不必输入 [crtl]+d 来结束。
9.7 管道符
9.7.1 多命令顺序执行
(1)cmd ; cmd (不考虑指令相关性的连续指令下达)
在指令与指令中间利用分号 (;) 来隔开,这样一来,分号前的指令执行完后就会立刻接着执行后面的指令了。
简单地检测命令是否正确执行。
(2)$? (指令回传值) 与 && 或 ||
如同上面谈到的,两个指令之间有相依性,而这个相依性主要判断的地方就在于前一个指令执行的结果是否正确。 指令回传值,“若前一个指令执行的结果为正确,在 Linux 下面会回传一个 $? = 0 的值”。 那么我们怎么通过这个回传值来判断后续的指令是否要执行呢?这就得要借由“ && ”及“ || ”的帮忙了! 注意喔,两个 & 之间是没有空格的!那个 | 则是 [Shift]+[] 的按键结果。
例如:我不清楚 /tmp/abc 是否存在,但就是要创建 /tmp/abc/hehe 文件
[[email protected] ~]$ ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe
上面这个范例总是会尝试创建 /tmp/abc/hehe 的喔!不论 /tmp/abc 是否存在。 由于Linux 下面的指令都是由左往右执行的,所以范例三有几种结果我们来分析一下:
- (1)若 /tmp/abc 不存在故回传 $?≠0,则 (2)因为 || 遇到非为 0 的 $? 故开始 mkdir /tmp/abc,由于 mkdir /tmp/abc 会成功进行,所以回传 $?=0 (3)因为 && 遇到 $?=0 故会执行 touch /tmp/abc/hehe,最终 hehe 就被创建了;
- (1)若 /tmp/abc 存在故回传 $?=0,则 (2)因为 || 遇到 0 的 $? 不会进行,此时 $?=0 继续向后传,故 (3)因为 && 遇到 $?=0 就开始创建 /tmp/abc/hehe 了!最终 /tmp/abc/hehe 被创建起来。
整个流程图示如下:
图 指令依序执行的关系示意图
例题:以 ls 测试 /tmp/vbirding 是否存在,若存在则显示 "exist" ,若不存在,则显示 "not exist"!这又牵涉到逻辑判断的问题,如果存在就显示某个数据,若不存在就显示其他数据,那我可以这样做:
ls /tmp/vbirding && echo "exist" || echo "not exist"
由于指令是一个接着一个去执行的,因此,如果真要使用判断, 那么这个 && 与 || 的顺序就不能搞错。一般来说,假设判断式有三个,也就是:
command1 && command2 || command3
而且顺序通常不会变,因为一般来说, command2 与 command3 会放置肯定可以执行成功的指令
不能将&&与|| 弄反,应该先写逻辑与,再写逻辑或:
9.7.2 管道符
就如同前面所说的, bash 命令执行的时候有输出的数据会出现! 那么如果这群数据必需要经过几道手续之后才能得到我们所想要的格式,应该如何来设置? 这就牵涉到管线命令的问题了 (pipe) ,管线命令使用的是“ | ”这个界定符号! 另外,管线命令与“连续下达命令”是不一样的! 下面我们先举一个例子来说明一下简单的管线命令。
假设我们想要知道 /etc/ 下面有多少文件,那么可以利用 ls /etc 来查阅,不过, 因为 /etc 下面的文件太多,导致一口气就将屏幕塞满了~不知道前面输出的内容是啥?此时,我们可以通过 less 指令的协助,利用:
[[email protected] ~]# ls -al /etc | less
如此一来,使用 ls 指令输出后的内容,就能够被 less 读取,并且利用 less 的功能,我们就能够前后翻动相关的信息了。我们就来了解一下这个管线命令“ | ”的用途吧! 其实这个管线命令“ | ”仅能处理经由前面一个指令传来的正确信息,也就是 standard output 的信息,对于 stdandard error 并没有直接处理的能力。那么整体的管线命令可以使用下图表示:
图 管线命令的处理示意图
在每个管线后面接的第一个数据必定是“指令”喔!而且这个指令必须要能够接受 standard input 的数据才行,这样的指令才可以是为“管线命令”,例如 less, more, head, tail 等都是可以接受 standard input 的管线命令啦。至于例如 ls, cp, mv 等就不是管线命令了!因为 ls, cp, mv 并不会接受来自 stdin 的数据。 也就是说,管线命令主要有两个比较需要注意的地方:
- 管线命令仅会处理 standard output,对于 standard error output 会予以忽略
- 管线命令必须要能够接受来自前一个指令的数据成为 standard input 继续处理才行。
如果要让 standard error 可以被管线命令所使用,那该如何处理?其实就是通过上一小节的数据流重导向。
(1)选取命令: cut, grep
一般来说,选取讯息通常是针对“一行一行”来分析的, 并不是整篇讯息分析的喔~下面我们介绍两个很常用的讯息选取命令:
① cut:
[[email protected] ~]$ cut -d '分隔字符' -f fields <==用于有特定分隔字符
[[email protected] ~]$ cut -c 字符区间 <==用于排列整齐的讯息
选项与参数:
- -d :后面接分隔字符。与 -f 一起使用;
- -f :依据 -d 的分隔字符将一段讯息分区成为数段,用 -f 取出第几段的意思;
- -c :以字符 (characters) 的单位取出固定字符区间;
例如:将 export 输出的讯息,取得第 12 字符以后的所有字串
[[email protected] ~]$ export | cut -c 12-
HISTCONTROL="ignoredups"
HISTSIZE="1000"
HOME="/home/dmtsai"
HOSTNAME="study.centos.vbird"
.....(其他省略).....
# 知道怎么回事了吧?用 -c 可以处理比较具有格式的输出数据!
# 我们还可以指定某个范围的值,例如第 12-20 的字符,就是 cut -c 12-20 等等!
cut 在处理多空格相连的数据时,可能会比较吃力一点,所以某些时刻可能会使用 awk 来取代!
② grep
刚刚的 cut 是将一行讯息当中,取出某部分我们想要的,而 grep 则是分析一行讯息, 若当中有我们所需要的信息,就将该行拿出来~简单的语法是这样的:
[[email protected] ~]$ grep [-acinv] [--color=auto] '搜寻字串' filename
选项与参数:
- -a :将 binary 文件以 text 文件的方式搜寻数据
- -c :计算找到 '搜寻字串' 的次数
- -i :忽略大小写的不同,所以大小写视为相同
- -n :顺便输出行号
- -v :反向选择,亦即显示出没有 '搜寻字串' 内容的那一行!
- --color=auto :可以将找到的关键字部分加上颜色的显示喔!
范例:在 last 的输出讯息中,只要有 root 就取出,并且仅取第一列
CentOS 7 当中,默认的 grep 已经主动加上 --color=auto 在 alias 内了!
(2)排序命令: sort, wc, uniq
下面我们介绍几个好用的排序与统计指令:
① sort
sort 是很有趣的指令,他可以帮我们进行排序,而且可以依据不同的数据类型来排序喔! 例如数字与文字的排序就不一样。此外,排序的字符与语系的编码有关,因此, 如果您需要排序时,建议使用 LANG=C 来让语系统一,数据排序比较好一些。[[email protected] ~]$ sort [-fbMnrtuk] [file or stdin]
选项与参数:
- -f :忽略大小写的差异,例如 A 与 a 视为编码相同;
- -b :忽略最前面的空白字符部分;
- -M :以月份的名字来排序,例如 JAN, DEC 等等的排序方法;
- -n :使用“纯数字”进行排序(默认是以文字体态来排序的);
- -r :反向排序;
- -u :就是 uniq ,相同的数据中,仅出现一行代表;
- -t :分隔符号,默认是用 [tab] 键来分隔;
- -k :以那个区间 (field) 来进行排序的意思
示例:
范例:/etc/passwd 内容是以 : 来分隔的,我想以第三栏来排序,该如何?
[[email protected] ~]$ cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash
alex:x:1001:1002::/home/alex:/bin/bash
arod:x:1002:1003::/home/arod:/bin/bash
# 看到特殊字体的输出部分了吧?怎么会这样排列啊?
# 如果是以文字体态来排序的话,原本就会是这样,想要使用数字排序:
# cat /etc/passwd | sort -t ':' -k 3 -n
# 这样才行啊!用那个 -n 来告知 sort 以数字来排序啊!
范例:利用 last ,将输出的数据仅取帐号,并加以排序
[[email protected] ~]$ last | cut -d ' ' -f1 | sort
② uniq
如果我排序完成了,想要将重复的数据仅列出一个显示,可以怎么做呢?
[[email protected] ~]$ uniq [-ic]
选项与参数:
- -i :忽略大小写字符的不同;
- -c :进行计数
使用 last 将帐号列出,仅取出帐号栏,进行排序,还想要知道每个人的登陆总次数
[[email protected] ~]# last | cut -d ' ' -f 1 | sort | uniq -c
1
9 reboot
20 root
1 wtmp
③ wc
如果我想要知道 /etc/man_db.conf 这个文件里面有多少字?多少行?多少字符的话, 可以怎么做呢?其实可以利用 wc 这个指令来达成喔!他可以帮我们计算输出的讯息的整体数据。
[[email protected] ~]$ wc [-lwm]
选项与参数:
- -l :仅列出行;
- -w :仅列出多少字(英文单字);
- -m :多少字符;
范例:我知道使用 last 可以输出登陆者,但是 last 最后两行并非帐号内容,那么请问,我该如何以一行指令串取得登陆系统的总人次?
[[email protected] ~]# last |grep [a-zA-Z] | grep -v 'wtmp' | wc -l
29
# 由于 last 会输出空白行, wtmp, unknown, reboot 等无关帐号登陆的信息,因此,我利用
# grep 取出非空白行,以及去除上述关键字那几行,再计算行数,就能够了解啰!
(3)双向重定向: tee
我们由前一节知道 > 会将数据流整个传送给文件或设备,因此我们除非去读取该文件或设备, 否则就无法继续利用这个数据流。万一我想要将这个数据流的处理过程中将某段讯息存下来,应该怎么做? 利用 tee 就可以~我们可以这样简单的看一下:
图 tee 的工作流程示意图
tee 会同时将数据流分送到文件去与屏幕 (screen);而输出到屏幕的,其实就是 stdout ,那就可以让下个指令继续处理喔!
[[email protected] ~]$ tee [-a] file
选项与参数:
- -a :以累加 (append) 的方式,将数据加入 file 当中!
例如:让我们将 last 的输出存一份到 last.list 文件中,并在屏幕显示第一列;
(4)字符转换命令: tr, col, join, paste, expand
① tr
我们在 vim 程序编辑器当中,提到过 DOS 断行字符与 Unix 断行字符的不同,并且可以使用 dos2unix 与 unix2dos 来完成转换。
tr 可以用来删除一段讯息当中的文字,或者是进行文字讯息的替换!
[[email protected] ~]$ tr [-ds] SET1 ...
选项与参数:
- -d :删除讯息当中的 SET1 这个字串;
- -s :取代掉重复的字符!
范例:将 /etc/passwd 转存成 dos 断行到 /root/passwd 中,再将 ^M 符号删除
[[email protected] ~]$ cp /etc/passwd ~/passwd && unix2dos ~/passwd
[[email protected] ~]$ file /etc/passwd ~/passwd
/etc/passwd: ASCII text
/home/dmtsai/passwd: ASCII text, with CRLF line terminators <==就是 DOS 断行
[[email protected] ~]$ cat ~/passwd | tr -d '\r' > ~/passwd.linux
# 那个 \r 指的是 DOS 的断行字符,关于更多的字符,请参考 man tr
[[email protected] ~]$ ll /etc/passwd ~/passwd*
-rw-r--r--. 1 root root 2092 Jun 17 00:20 /etc/passwd
-rw-r--r--. 1 dmtsai dmtsai 2133 Jul 9 22:13 /home/dmtsai/passwd
-rw-rw-r--. 1 dmtsai dmtsai 2092 Jul 9 22:13 /home/dmtsai/passwd.linux
# 处理过后,发现文件大小与原本的 /etc/passwd 就一致了!
② col
[[email protected] ~]$ col [-xb]
选项与参数:
- -x :将 tab 键转换成对等的空白键
范例一:利用 cat -A 显示出所有特殊按键,最后以 col 将 [tab] 转成空白
[[email protected] ~]$ cat -A /etc/man_db.conf <==此时会看到很多 ^I 的符号,那就是 tab
[[email protected] ~]$ cat /etc/man_db.conf | col -x | cat -A | more
# 嘿嘿!如此一来, [tab] 按键会被取代成为空白键,输出就美观多了!
虽然 col 有他特殊的用途,不过,很多时候,他可以用来简单的处理将 [tab] 按键取代成为空白键! 例如上面的例子当中,如果使用 cat -A 则 [tab] 会以 ^I 来表示。 但经过 col -x 的处理,则会将 [tab] 取代成为对等的空白键!
③ join
join 看字面上的意义 (加入/参加) 就可以知道,他是在处理两个文件之间的数据, 而且,主要是在处理“两个文件当中,有 "相同数据" 的那一行,才将他加在一起”的意思。
[[email protected] ~]$ join [-ti12] file1 file2
选项与参数:
- -t :join 默认以空白字符分隔数据,并且比对“第一个字段”的数据,
- 如果两个文件相同,则将两笔数据联成一行,且第一个字段放在第一个!
- -i :忽略大小写的差异;
- -1 :这个是数字的 1 ,代表“第一个文件要用那个字段来分析”的意思;
- -2 :代表“第二个文件要用那个字段来分析”的意思。
我们利用下面的简单例子来说明:
我们知道 /etc/passwd 第四个字段是 GID ,那个 GID 记录在
/etc/group 当中的第三个字段,请问如何将两个文件整合?
[[email protected] ~]# join -t ':' -1 4 /etc/passwd -2 3 /etc/group | head -n 3
0:root:x:0:root:/root:/bin/bash:root:x:
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:
# 同样的,相同的字段部分被移动到最前面了!所以第二个文件的内容就没再显示。
需要特别注意的是,在使用 join 之前,你所需要处理的文件应该要事先经过排序 (sort) 处理! 否则有些比对的项目会被略过!
④ paste
这个 paste 就要比 join 简单多了!相对于 join 必须要比对两个文件的数据相关性, paste 就直接“将两行贴在一起,且中间以 [tab] 键隔开”而已!简单的使用方法:
[[email protected] ~]$ paste [-d] file1 file2
选项与参数:
- -d :后面可以接分隔字符。默认是以 [tab] 来分隔的!
- - :如果 file 部分写成 - ,表示来自 standard input 的数据的意思。
例如:用 root 身份,先将 /etc/group 读出(用 cat),然后将 /etc/passwd 与 /etc/shadow 同一行贴在一起:
⑤ expand
就是在将 [tab] 按键转成空白键啦~可以这样玩:
[[email protected] ~]$ expand [-t] file
选项与参数:
- -t :后面可以接数字。一般来说,一个 tab 按键可以用 8 个空白键取代。我们也可以自行定义一个 [tab] 按键代表多少个字符呢!
将 /etc/man_db.conf 内行首为 MANPATH 的字样就取出;仅取前三行;将所有的符号都列出来;将 [tab] 按键设置成 6 个字符
[[email protected] ~]$ grep '^MANPATH' /etc/man_db.conf | head -n 3 | expand -t 6 - | cat -A
MANPATH_MAP /bin /usr/share/man$
MANPATH_MAP /usr/bin /usr/share/man$
MANPATH_MAP /sbin /usr/share/man$
123456123456123456123456123456123456123456123456...
# 仔细看一下上面的数字说明,因为我是以 6 个字符来代表一个 [tab] 的长度,所以,MAN... 到 /usr 之间
会隔 12 (两个 [tab]) 个字符喔!如果 tab 改成 9 的话,情况就又不同了!
此外,您也可以参考一下 unexpand 这个将空白转成 [tab] 的指令功能。
(5)切割命令: split
如果你有文件太大,导致一些携带式设备无法复制的问题,找 split 就对了! 他可以帮你将一个大文件,依据文件大小或行数来分区,就可以将大文件分区成为小文件了!
[[email protected] ~]$ split [-bl] file PREFIX
选项与参数:
- -b :后面可接欲分区成的文件大小,可加单位,例如 b, k, m 等;
- -l :以行数来进行分区。
- PREFIX :代表前置字符的意思,可作为分区文件的前导文字。
范例一:我的 /etc/services 有六百多K,若想要分成 300K 一个文件时?
[[email protected] ~]$ cd /tmp; split -b 300k /etc/services services
[[email protected] tmp]$ ll -k services*
-rw-rw-r--. 1 dmtsai dmtsai 307200 Jul 9 22:52 servicesaa
-rw-rw-r--. 1 dmtsai dmtsai 307200 Jul 9 22:52 servicesab
-rw-rw-r--. 1 dmtsai dmtsai 55893 Jul 9 22:52 servicesac
# 那个文件名可以随意取的啦!我们只要写上前导文字,小文件就会以xxxaa, xxxab, xxxac 等方式来创建小文件的!
范例二:如何将上面的三个小文件合成一个文件,文件名为 servicesback
[[email protected] tmp]$ cat services* >> servicesback
# 就用数据流重导向就好啦!
范例三:使用 ls -al / 输出的信息中,每十行记录成一个文件
[[email protected] tmp]$ ls -al / | split -l 10 - lsroot
[[email protected] tmp]$ wc -l lsroot*
10 lsrootaa
10 lsrootab
4 lsrootac
24 total
# 重点在那个 - 啦!一般来说,如果需要 stdout/stdin 时,但偏偏又没有文件,
# 有的只是 - 时,那么那个 - 就会被当成 stdin 或 stdout ~
(6)参数代换: xargs
xargs 是在做什么的呢?就以字面上的意义来看, x 是加减乘除的乘号,args 则是 arguments (参数) 的意思,所以说,这个玩意儿就是在产生某个指令的参数的意思! xargs 可以读入 stdin 的数据,并且以空白字符或断行字符作为分辨,将 stdin 的数据分隔成为 arguments 。 因为是以空白字符作为分隔,所以,如果有一些文件名或者是其他意义的名词内含有空白字符的时候, xargs 可能就会误判了。
[[email protected] ~]$ xargs [-0epn] command
选项与参数:
- -0 :如果输入的 stdin 含有特殊字符,例如 `, \, 空白键等等字符时,这个 -0 参数可以将他还原成一般字符。这个参数可以用于特殊状态喔!
- -e :这个是 EOF (end of file) 的意思。后面可以接一个字串,当 xargs 分析到这个字串时,就会停止继续工作!
- -p :在执行每个指令的 argument 时,都会询问使用者的意思;
- -n :后面接次数,每次 command 指令执行时,要使用几个参数的意思。
当 xargs 后面没有接任何的指令时,默认是以 echo 来进行输出喔!
例如:将所有的 /etc/passwd 内的账号都以finger查询,但一次查询5个账户
这个 -p 的选项可以让使用者的使用过程中,被询问到每个指令是否执行!
- finger:
finger命令可以让使用者查询一些其他使用者的资料。会列出来的资料有:
- Login Name
- User Name
- Home directory
- Shell
- Login status
- mail status
- .plan
- .project
- .forward
其中 .plan、.project 和 .forward 就是使用者在他的 Home Directory 里的 .plan , .project 和 .forward 等档案里的资料。如果没有就没有。finger 指令并不限定于在同一服务器上查询,也可以寻找某一个远端服务器上的使用者。只要给一个像是 E-mail address 一般的地址即可。
使用权限:所有使用者。
语法:
finger [options] user[@address]
参数说明:
- -l 多行显示。
- -s 单行显示。这个选项只显示登入名称、真实姓名、终端机名称、闲置时间、登入时间、办公室号码及电话号码。如果所查询的使用者是远端服务器的使用者,这个选项无效。
会使用 xargs 的原因是, 很多指令其实并不支持管线命令,因此我们可以通过 xargs 来提供该指令引用 standard input 之用!
范例四:找出 /usr/sbin 下面具有特殊权限的文件名,并使用 ls -l 列出详细属性
[[email protected] ~]$ find /usr/sbin -perm +7000 | xargs ls -l
-rwx--s--x. 1 root lock 11208 Jun 10 2014 /usr/sbin/lockdev
-rwsr-xr-x. 1 root root 113400 Mar 6 12:17 /usr/sbin/mount.nfs
-rwxr-sr-x. 1 root root 11208 Mar 6 11:05 /usr/sbin/netreport
.....(下面省略).....
# 应该会想到使用“ ls -l $(find /usr/sbin -perm /7000) ”来处理这个范例
(7)关于减号 - 的用途
管线命令在 bash 的连续的处理程序中是相当重要的!另外,在 log file 的分析当中也是相当重要的一环, 所以请特别留意!另外,在管线命令当中,常常会使用到前一个指令的 stdout 作为这次的 stdin , 某些指令需要用到文件名称 (例如 tar) 来进行处理时,该 stdin 与 stdout 可以利用减号 "-" 来替代, 举例来说:
上面这个例子是说:“我将 /home 里面的文件给他打包,但打包的数据不是纪录到文件,而是传送到 stdout; 经过管线后,将 tar -cvf - /home 传送给后面的 tar -xvf - ”。后面的这个 - 则是取用前一个指令的 stdout, 因此,我们就不需要使用 filename 了!
more 是用来查看文件中的内容,分屏显示。
使用管道符,命令2一定要能操作命令1。
netstat -a:查看系统中所有的网络连接。
LISTEN:这个端口被监听,等待有人访问; ESTABLISHED:有人正在连接。
9.8 通配符与特殊符号
9.8.1 通配符
一般查找目录。
9.8.2 Bash中其他特殊符号
以上为 bash 环境中常见的特殊符号汇整!理论上,你的“文件名”尽量不要使用到上述的字符!
反引号:在键盘1旁边。
9.9 Bash Shell 的操作环境
9.9.1 路径与命令查找顺序
9.9.2 bash 的登录与欢迎信息:/etc/issue,/etc/motd
bash的登录和欢迎信息设置。
issue内的各代码的意义:
- \d 本地端时间的日期
- \l 显示第几个终端机接口
- \m 显示硬件的等级(i386/i486/i586)
- \n 显示主机的网络名称
- \o 显示domain name
- \r 操作系统的版本(uname -r)
- \t 显示本地端时间的时间
- \s 操作系统时间
- \v 操作系统的版本
1、/etc/issue 本地登陆显示的信息,本地登录前
2、/etc/issue.net 网络登陆显示的信息,登录后显示,需要由sshd配置
3、/etc/motd 常用于通告信息,如计划关机时间的警告等,登陆后的提示信息
至于文件 /etc/motd,(motd即motd即message of today布告栏信息的缩写) 则是在每次用户登录时,motd文件的内容会显示在用户的终端。系统管理员可以在文件中编辑系统活动消息,例如:管理员通知用户系统何时进行软件或硬件的升级、何时进行系统维护等。如果shell支持中文,还可以使用中文,这样看起来更易于了解。/etc/motd缺点是,现在许多用户登录系统时选择自动进入图形界面,所以这些信息往往看不到。
issue与motd文件主要区别在于:当一个用户过本地文本设备(如本地终端,本地虚拟控制台等)登录 /etc/issue 的文件内容显示在login提示符之前,而 /etc/motd 内容显示在用户成功登录系统之后。
issue.net文件(只针对网络用户)--若通过远程本文设备(如通过ssh或telnet等)登录,则显示该文件的内容。使用ssh登录时,会不会显示issue信息由sshd服务的sshd_config的Banner属性配置决定。
其中上面的三个文件,issue.net和motd文件都是在登录后显示,那么顺序是怎么样的呢,如下图所示:
在这里使用的是Putty进行登录,我们可以发现先显示的是issue.net文件中的内容,最后才显示motd文件中的内容。又使用Xshell 进行登录,得出的结果一样、都是先显示issue.net文件内容,最后显示motd文件的内容。中间隔着上次登录信息。
其中不同的是:
- 使用Putty登录时,当我们输入用户名root后,就显示了issue.net的内容,输入密码后,再显示的motd的内容。
- 使用Xshell登录时,只有当我们输入用户名和密码后,验证成功了,才会依次显示issue.net和motd文件的内容。
9.9.3 bash 的环境配置文件
在Linux中系统有一些环境配置文件,让bash在启动是直接读取这些配置文件,以规划好bash的操作环境。配置文件可分为全体的配置文件以及用户个人偏好配置文件。
一、login 与 non-login shell
- login shell: 取得 bash 是需要完整的登录流程,就称为 login shell。例如,你要由 tty1~tty6 登入,需要输入用户的账号与密码,此时取得的 bash 就称为 login shell
- non-login shell:取得 bash 接口的方法不需要重复登入的操作。例如,①你以 X widow 登入 Linux后,在以 X 图形化接口启动终端机,此时那个终端并没有需要再次输入账号和密码,那个 bash 的环境就称 non-login shell 。② 你原本的 bash 环境下再次下达 bash 这个指令,同样的也没有输入账号和密码,那第二个 bash(子程序)也是 non-login shell。
介绍 login 和 non-login shell 是因为这两个取得 bash 的情况下,读取的配置文件数据并不一样所致。
(1)login shell
我们先来谈谈 login shell 。一般来说 login shell 会读取两个配置文件:
- /etc/profile:这是系统整体的设定,最好不要修改这个文件;
- ~/.bash_profile 或 ~/.bash_login 或 ~/.profile:属于使用者个人设定,你要改自己的数据,可以写在这里。
① /etc/profile(login shell 才会读)
可以使用 vim 去阅读这个文件的内容,这个配置文件可以利用使用者的标识符(UID)来决定很多重要的变量数据,这也是每个使用者登入取得 bash 时一定会读取的文件,所以如果你想要帮所有者设定整体环境,可以改这里。不过,没事还是不要随便改这个文件,这个文件设定的有其他重要变量,也会引用其他文件。例如,/etc/profile.d/*.sh、/etc/local.conf等等。
这个文件设置的变量主要有:
- PATH:会依据 UID 决定 PATH 变量要不要含有 sbin 的系统指令目录;
- MAIL:依据帐号设置好使用者的 mailbox 到 /var/spool/mail/帐号名;
- USER:根据使用者的帐号设置此一变量内容;
- HOSTNAME:依据主机的 hostname 指令决定此一变量内容;
- HISTSIZE:历史命令记录笔数。CentOS 7.x 设置为 1000 ;
- umask:包括 root 默认为 022 而一般用户为 002 等!
/etc/profile 可不止会做这些事而已,他还会去调用外部的设置数据!在 CentOS 5.x 默认的情况下,下面这些数据会依序的被调用进来:
- /etc/inputrc
其实这个文件并没有被执行。/etc/profle会主动判断用户有没有自定义输入的按键功能,如果没有的话,/etc/protile 就会决定设置“INPUTRC=/etc/inputrc”这个变量。此文件内容为bash 的热键、[Tab]有没有声音等的数据。
- /etc/profile.d/*.sh
其实这是个目录内的众多文件!只要在 /etc/profile.d/ 这个目录内且扩展名为 .sh ,另外,使用者能够具有 r 的权限, 那么该文件就会被 /etc/profile 调用进来。在 CentOS 5.x 中,这个目录下面的文件规范了 bash 操作接口的颜色、 语系、ll 与 ls 指令的命令别名、vi 的命令别名、which 的命令别名等等。如果你需要帮所有使用者设置一些共享的命令别名时, 可以在这个目录下面自行创建扩展名为 .sh 的文件,并将所需要的数据写入即可!
- /et/sysconfigi18n
这个文件是由/etc/prolle.d/lang.sh调用的。这也是我们决定bash 默认使用何种语系的重要配置文件。文件里最重要的就是 LANG/LC_ALL 这些个变量的设置。bash 的 login shell 情况下所读取的整体环境配置文件其实只有/etc/profile, 但是/etc/profile还会调用其他的配置文件,所以让我们的bash操作接口变得非常的友善。
② ~/.bash_profile(login shell 才会读)
bash 在读完整体环境设定的 /etc/profile 并藉此呼叫其他配置文件后,接下来则是会读取使用者的个人配置文件。在login shell 的 bash 环境中,所读取的个人偏好配置文件其实主要有三个,依顺序分别为:
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
其实 bash 的 login shell 设定只会读取上面三个文件的其中一个,而读取的顺序则是依照上面的顺序。 如果 ~/.bash_profile 不存在才会去读取 ~/.bash_login,而前两者都不存在才会读取 ~/.profile 的意思。 会有这么多的文件,其实是因应其他 shell 转换过来的使用者的习惯而已。
最后,看看这个 login shell 的读取流程:
实线的方向是主线程流程,虚线的方向则是被调用的配置文件。
source :读入环境配置文件的指令
由于 /etc/profile 与 ~/.bash_profile 都是在取得 login shell 的时候才会读取的配置文件,所以, 如果你将自己的偏好设置写入上述的文件后,通常都是得登出再登陆后,该设置才会生效。那么,能不能直接读取配置文件而不登出登陆呢? 可以的!那就得要利用 source 这个指令了!
[[email protected] ~]# source 配置文件文件名
范例:将主文件夹的 ~/.bashrc 的设置读入目前的 bash 环境中
[[email protected] ~]$ source ~/.bashrc <==下面这两个指令是一样的!
[[email protected] ~]$ . ~/.bashrc
利用 source 或小数点 (.) 都可以将配置文件的内容读进来目前的 shell 环境中! 举例来说,我修改了 ~/.bashrc ,那么不需要登出,立即以 source ~/.bashrc 就可以将刚刚最新设置的内容读进来目前的环境中!还有,包括 ~/bash_profile 以及 /etc/profile 的设置中, 很多时候也都是利用到这个 source (或小数点)的功能!
有没有可能会使用到不同环境配置文件的时候?有啊! 最常发生在一个人的工作环境分为多种情况的时候了!举个例子来说,在鸟哥的大型主机中, 常常需要负责两到三个不同的案子,每个案子所需要处理的环境变量订定并不相同, 那么鸟哥就将这两三个案子分别编写属于该案子的环境变量设置文件,当需要该环境时,就直接“ source 变量文件 ”,如此一来,环境变量的设置就变的更简便而灵活了!
(2)non-login shell
- ~/.bashrc (non-login shell 会读)
谈完了 login shell 后,那么 non-login shell 这种非登陆情况取得 bash 操作接口的环境配置文件又是什么? 当你取得 non-login shell 时,该 bash 配置文件仅会读取 ~/.bashrc 而已!那么默认的 ~/.bashrc 内容是如何?
[[email protected] ~]# cat ~/.bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i' <==使用者的个人设置
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then <==整体的环境设置
. /etc/bashrc
fi
特别注意一下,由于 root 的身份与一般使用者不同。以 root 的身份登录和一般使用者的 ~/.bashrc 会有些许不同。看一下,你会发现在 root 的 ~/.bashrc 中其实已经规范了较为保险的命令别名了。 此外,咱们的 CentOS 5.x 还会主动的调用 /etc/bashrc 这个文件喔!为什么需要调用 /etc/bashrc 呢? 因为 /etc/bashrc 帮我们的 bash 定义出下面的数据:
- 依据不同的 UID 规范出 umask 的值;
- 依据不同的 UID 规范出提示字符 (就是 PS1 变量);
- 调用 /etc/profile.d/*.sh 的设置
你要注意的是,这个 /etc/bashrc 是 CentOS 特有的 (其实是 Red Hat 系统特有的),其他不同的 distributions 可能会放置在不同的文件名就是了。由于这个 ~/.bashrc 会调用 /etc/bashrc 及 /etc/profile.d/*.sh , 所以,万一你没有 ~/.bashrc (可能自己不小心将他删除了),那么你会发现你的 bash 提示字符可能会变成这个样子:
-bash-4.2$
不要太担心啦!这是正常的,因为你并没有调用 /etc/bashrc 来规范 PS1 变量啦!而且这样的情况也不会影响你的 bash 使用。 如果你想要将命令提示字符捉回来,那么可以复制 /etc/skel/.bashrc 到你的主文件夹,再修订一下你所想要的内容, 并使用 source 去调用 ~/.bashrc ,那你的命令提示字符就会回来啦!
二、其他相关的配置文件
事实上还有一些配置文件可能会影响到你的 bash 操作的,下面就来谈一谈:
- /etc/man_db.conf
这个文件乍看之下好像跟 bash 没相关性,但是对于系统管理员来说, 却也是很重要的一个文件!这文件的内容“规范了使用 man 的时候, man page 的路径到哪里去寻找!”所以说的简单一点,这个文件规定了下达 man 的时候,该去哪里查看数据的路径设置!
那么什么时候要来修改这个文件呢?如果你是以 tarball 的方式来安装你的数据,那么你的 man page 可能会放置在 /usr/local/softpackage/man 里头,那个 softpackage 是你的套件名称, 这个时候你就得以手动的方式将该路径加到 /etc/man_db.conf 里头,否则使用 man 的时候就会找不到相关的说明文档。
- ~/.bash_history
还记得我们在历史命令提到过这个文件吧?默认的情况下, 我们的历史命令就记录在这里!而这个文件能够记录几笔数据,则与 HISTFILESIZE 这个变量有关。每次登陆 bash 后,bash 会先读取这个文件,将所有的历史指令读入内存, 因此,当我们登陆 bash 后就可以查知上次使用过哪些指令。
- ~/.bash_logout
这个文件则记录了“当我登出 bash 后,系统再帮我做完什么动作后才离开”的意思。 你可以去读取一下这个文件的内容,默认的情况下,登出时, bash 只是帮我们清掉屏幕的讯息而已。 不过,你也可以将一些备份或者是其他你认为重要的工作写在这个文件中 (例如清空暂存盘), 那么当你离开 Linux 的时候,就可以解决一些烦人的事情!
9.9.4 终端机的环境设置: stty, set
(1)stty
如何查阅目前的一些按键内容呢?可以利用 stty (setting tty 终端机的意思) 呢! stty 也可以帮助设置终端机的输入按键代表意义!
[[email protected] ~]$ stty [-a]
选项与参数:
-a :将目前所有的 stty 参数列出来;
范例一:列出所有的按键与按键内容
[[email protected] ~]$ stty -a
speed 38400 baud; rows 20; columns 90; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
flush = ^O; min = 1; time = 0;
....(以下省略)....
我们可以利用 stty -a 来列出目前环境中所有的按键列表,在上头的列表当中,需要注意的是特殊字体那几个, 此外,如果出现 ^ 表示 [Ctrl] 那个按键的意思。举例来说, intr = ^C 表示利用 [ctrl] + c 来达成的。几个重要的代表意义是:
- intr : 送出一个 interrupt (中断) 的讯号给目前正在 run 的程序 (就是终止啰!);
- quit : 送出一个 quit 的讯号给目前正在 run 的程序;
- erase : 向后删除字符,
- kill : 删除在目前命令行上的所有文字;
- eof : End of file 的意思,代表“结束输入”。
- start : 在某个程序停止后,重新启动他的 output
- stop : 停止目前屏幕的输出;
- susp : 送出一个 terminal stop 的讯号给正在 run 的程序。
(2)set
除了 stty 之外,其实我们的 bash 还有自己的一些终端机设置值呢!那就是利用 set 来设置的! 我们之前提到一些变量时,可以利用 set 来显示,除此之外,其实 set 还可以帮我们设置整个指令输出/输入的环境。 例如记录历史命令、显示错误内容等等。
[[email protected] ~]$ set [-uvCHhmBx]
选项与参数:
- -u :默认不启用。若启用后,当使用未设置变量时,会显示错误讯息;
- -v :默认不启用。若启用后,在讯息被输出前,会先显示讯息的原始内容;
- -x :默认不启用。若启用后,在指令被执行前,会显示指令内容(前面有 ++ 符号)
- -h :默认启用。与历史命令有关;
- -H :默认启用。与历史命令有关;
- -m :默认启用。与工作管理有关;
- -B :默认启用。与刮号 [] 的作用有关;
- -C :默认不启用。若使用 > 等,则若文件存在时,该文件不会被覆盖。
范例一:显示目前所有的 set 设置值
[[email protected] ~]$ echo $-
himBH
# 那个 $- 变量内容就是 set 的所有设置啦! bash 默认是 himBH 喔!
范例二:设置 "若使用未定义变量时,则显示错误讯息"
[[email protected] ~]$ set -u
[[email protected] ~]$ echo $vbirding
-bash: vbirding: unbound variable
# 默认情况下,未设置/未宣告 的变量都会是“空的”,不过,若设置 -u 参数,
# 那么当使用未设置的变量时,就会有问题啦!很多的 shell 都默认启用 -u 参数。
# 若要取消这个参数,输入 set +u 即可!
范例三:执行前,显示该指令内容。
[[email protected] ~]$ set -x
++ printf '\033]0;%[email protected]%s:%s\007' dmtsai study '~' # 这个是在列出提示字符的控制码!
[[email protected] ~]$ echo ${HOME}
+ echo /home/dmtsai
/home/dmtsai
++ printf '\033]0;%[email protected]%s:%s\007' dmtsai study '~'
# 看见否?要输出的指令都会先被打印到屏幕上喔!前面会多出 + 的符号!
另外,其实我们还有其他的按键设置功能呢!就是在前一小节提到的 /etc/inputrc 这个文件里面设置。 还有例如 /etc/DIRCOLORS 与 /usr/share/terminfo/ 等,也都是与终端机有关的环境设置文件呢!
我们将 bash 默认的组合键给他汇整如下: