P296
当我登陆的时候,系统就会给我一个 shell 让我来工作了。而这个登陆取得的 shell 就记录在 /etc/passwd 这个文件内!
P298
怎么知道这个命令是来自于外部命令(指的是其他非 bash 所提供的命令) 或是内建在 bash 当中的呢?嘿嘿!利用 type 这个命令来观察即可!举例来说:
[root@www ~]# type [-tpa] name 选项与参数: :不加任何选项与参数时,type 会显示出 name 是外部命令还是 bash 内建命令 -t :当加入 -t 参数时,type 会将 name 以底下这些字眼显示出他的意义: file :表示为外部命令; alias :表示该命令为命令别名所配置的名称; builtin :表示该命令为 bash 内建的命令功能; -p :如果后面接的 name 为外部命令时,才会显示完整文件名; -a :会由 PATH 变量定义的路径中,将所有含 name 的命令都列出来,包含 alias 范例一:查询一下 ls 这个命令是否为 bash 内建? [root@www ~]# type ls ls is aliased to `ls --color=tty' <==未加任何参数,列出 ls 的最主要使用情况 [root@www ~]# type -t ls alias <==仅列出 ls 运行时的依据 [root@www ~]# type -a ls ls is aliased to `ls --color=tty' <==最先使用 aliase ls is /bin/ls <==还有找到外部命令在 /bin/ls 范例二:那么 cd 呢? [root@www ~]# type cd cd is a shell builtin <==看到了吗? cd 是 shell 内建命令这个 type 也可以用来作为类似 which命令的用途啦
P301
- 变量的取用: echo
[root@www ~]# echo $variable
root@www ~]# echo $myname <==这里并没有任何数据~因为这个变量尚未被配置!是空的!
瞧!如此一来,这个变量名称 myname 的内容就带有 VBird 这个数据啰~而由上面的例子当中,我们也可以知道:在 bash 当中,当一个变量名称尚未被配置时,默认的内容是『空』的。
- 若该变量需要在其他子程序运行,则需要以 export 来使变量变成环境变量:
『export PATH』 - 通常大写字符为系统默认变量,自行配置变量可以使用小写字符,方便判断 (纯粹依照使用者兴趣与嗜好) ;
- 取消变量的方法为使用 unset :『unset 变量名称』例如取消 myname 的配置:
『unset myname』
例题:
[root@www ~]# name=VBird发现了吗?没错!使用了单引号的时候,那么 $name 将失去原有的变量内容,仅为一般字符的显示型态而已!这里必需要特别小心在意!
[root@www ~]# echo $name
VBird
[root@www ~]# myname="$name its me"
[root@www ~]# echo $myname
VBird its me
[root@www ~]# myname='$name its me'
[root@www ~]# echo $myname
$name its me
例题:
另外再举个例子,我们也知道, locate命令可以列出所有的相关文件档名,但是,如果我想要知道各个文件的权限呢?举例来说,我想要知道每个 crontab 相关档名的权限:
[root@www ~]# ls -l `locate crontab`
P304
- 用 env 观察环境变量与常见环境变量说明
范例一:列出目前的 shell 环境下的所有环境变量与其内容。 [root@www ~]# env HOSTNAME=www.vbird.tsai <== 这部主机的主机名 TERM=xterm <== 这个终端机使用的环境是什么类型 SHELL=/bin/bash <== 目前这个环境下,使用的 Shell 是哪一个程序? HISTSIZE=1000 <== 『记录命令的笔数』在 CentOS 默认可记录 1000 笔 USER=root <== 使用者的名称啊! LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01: or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=0 0;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz= 00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;3 1:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00 ;35:*.xpm=00;35:*.png=00;35:*.tif=00;35: <== 一些颜色显示 MAIL=/var/spool/mail/root <== 这个用户所取用的 mailbox 位置 PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin: /root/bin <== 不再多讲啊!是运行文件命令搜寻路径 INPUTRC=/etc/inputrc <== 与键盘按键功能有关。可以配置特殊按键! PWD=/root <== 目前用户所在的工作目录 (利用 pwd 取出!) LANG=en_US <== 这个与语系有关,底下会再介绍! HOME=/root <== 这个用户的家目录啊! _=/bin/env <== 上一次使用的命令的最后一个参数(或命令本身) |
set 除了环境变量之外,还会将其他在 bash 内的变量通通显示出来哩!
[root@www ~]# set BASH=/bin/bash <== bash 的主程序放置路径 BASH_VERSINFO=([0]="3" [1]="2" [2]="25" [3]="1" [4]="release" [5]="i686-redhat-linux-gnu") <== bash 的版本啊! BASH_VERSION='3.2.25(1)-release' <== 也是 bash 的版本啊! COLORS=/etc/DIR_COLORS.xterm <== 使用的颜色纪录文件 COLUMNS=115 <== 在目前的终端机环境下,使用的字段有几个字符长度 HISTFILE=/root/.bash_history <== 历史命令记录的放置文件,隐藏档 HISTFILESIZE=1000 <== 存起来(与上个变量有关)的文件之命令的最大纪录笔数。 HISTSIZE=1000 <== 目前环境下,可记录的历史命令最大笔数。 HOSTTYPE=i686 <== 主机安装的软件主要类型。我们用的是 i686 兼容机器软件 IFS=$' ' <== 默认的分隔符 LINES=35 <== 目前的终端机下的最大行数 MACHTYPE=i686-redhat-linux-gnu <== 安装的机器类型 MAILCHECK=60 <== 与邮件有关。每 60 秒去扫瞄一次信箱有无新信! OLDPWD=/home <== 上个工作目录。我们可以用 cd - 来取用这个变量。 OSTYPE=linux-gnu <== 操作系统的类型! PPID=20025 <== 父程序的 PID (会在后续章节才介绍) PS1='[u@h W]$ ' <== PS1 就厉害了。这个是命令提示字符,也就是我们常见的 [root@www ~]# 或 [dmtsai ~]$ 的配置值啦!可以更动的! PS2='> ' <== 如果你使用跳脱符号 () 第二行以后的提示字符也可以被列出来喔! name=VBird <== 刚刚配置的自定义变量也可以被列出来喔! $ <== 目前这个 shell 所使用的 PID ? <== 刚刚运行完命令的回传值。
PS1:(提示字符的配置)
这是 PS1 (数字的 1 不是英文字母),这个东西就是我们的『命令提示字符』喔!当我们每次按下 [Enter] 按键去运行某个命令后,最后要再次出现提示字符时,就会主动去读取这个变量值了。上头 PS1 内显示的是一些特殊符号,这些特殊符号可以显示不同的信息,每个 distributions 的 bash 默认的 PS1 变量内容可能有些许的差异,不要紧,『习惯你自己的习惯』就好了。你可以用 man bash (注3)去查询一下
PS1 的相关说明,以理解底下的一些符号意义。
- d :可显示出『星期 月 日』的日期格式,如:"Mon Feb 2"
- H :完整的主机名。举例来说,鸟哥的练习机为『www.vbird.tsai』
- h :仅取主机名在第一个小数点之前的名字,如鸟哥主机则为『www』后面省略
- :显示时间,为 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 时,提示字符为 # ,否则就是 $ 啰~
好了,让我们来看看 CentOS 默认的 PS1 内容吧:『[u@h W]$ 』,现在你知道那些反斜杠后的数据意义了吧?要注意喔!那个反斜杠后的数据为 PS1 的特殊功能,与 bash 的变量配置没关系啦!不要搞混了喔!那你现在知道为何你的命令提示字符是:『 [root@www ~]# 』了吧?好了,那么假设我想要有类似底下的提示字符:
[root@www /home/dmtsai 16:50 #12]#那个 # 代表第 12 次下达的命令。那么应该如何配置 PS1 呢?可以这样啊:
[root@www ~ ]# cd /home [root@www home]# PS1='[u@h w A ##]$ ' [root@www /home 17:02 #85]# # 看到了吗?提示字符变了!变的很有趣吧!其中,那个 #85 比较有趣, # 如果您再随便输入几次 ls 后,该数字就会添加喔!为啥?上面有说明滴!
- $:(关于本 shell 的 PID)
钱字号本身也是个变量喔!这个咚咚代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。更多的程序观念,我们会在第四篇的时候提及。想要知道我们的 shell 的 PID ,就可以用:『echo $$ 』即可!出现的数字就是你的 PID 号码。 - ?:(关于上个运行命令的回传值)
什么?问号也是一个特殊的变量?没错!在 bash 里面这个变量可重要的很!这个变量是:『上一个运行的命令所回传的值』,上面这句话的重点是『上一个命令』与『回传值』两个地方。当我们运行某些命令时,这些命令都会回传一个运行后的代码。一般来说,如果成功的运行该命令,则会回传一个 0 值,如果运行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。我们以底下的例子来看看:[root@www ~]# echo $SHELL /bin/bash <==可顺利显示!没有错误! [root@www ~]# echo $? 0 <==因为没问题,所以回传值为 0 [root@www ~]# 12name=VBird -bash: 12name=VBird: command not found <==发生错误了!bash回报有问题 [root@www ~]# echo $? 127 <==因为有问题,回传错误代码(非为0) # 错误代码回传值依据软件而有不同,我们可以利用这个代码来搜寻错误的原因喔! [root@www ~]# echo $? 0 # 咦!怎么又变成正确了?这是因为 "?" 只与『上一个运行命令』有关, # 所以,我们上一个命令是运行『 echo $? 』,当然没有错误,所以是 0 没错!
export: 自定义变量转成环境变量
谈了 env 与 set 现在知道有所谓的环境变量与自定义变量,那么这两者之间有啥差异呢?其实这两者的差异在于『该变量是否会被子程序所继续引用』啦!
子程序仅会继承父程序的环境变量,子程序不会继承父程序的自定义变量啦!所以你在原本 bash 的自定义变量在进入了子程序后就会消失不见,一直到你离开子程序并回到原本的父程序后,这个变量才会又出现!
P308
[root@www ~]# locale -a ....(前面省略).... zh_TW zh_TW.big5 <==大五码的中文编码 zh_TW.euctw zh_TW.utf8 <==万国码的中文编码 zu_ZA zu_ZA.iso88591 zu_ZA.utf8
正体中文语系至少支持了两种以上的编码,一种是目前还是很常见的 big5 ,另一种则是越来越热门的 utf-8 编码。那么我们如何修订这些编码呢?其实可以透过底下这些变量的说:
[root@www ~]# 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= <==整体语系的环境 ....(后面省略).... |
变量键盘读取、数组与宣告: read, array, declare
要读取来自键盘输入的变量,就是用 read 这个命令了。
[root@www ~]# read [-pt] variable 选项与参数: -p :后面可以接提示字符! -t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦! 范例一:让用户由键盘输入一内容,将该内容变成名为 atest 的变量 [root@www ~]# read atest This is a test <==此时光标会等待你输入!请输入左侧文字看看 [root@www ~]# echo $atest This is a test <==你刚刚输入的数据已经变成一个变量内容! 范例二:提示使用者 30 秒内输入自己的大名,将该输入字符串作为名为 named 的变量内容 [root@www ~]# read -p "Please keyin your name: " -t 30 named Please keyin your name: VBird Tsai <==注意看,会有提示字符喔! [root@www ~]# echo $named VBird Tsai <==输入的数据又变成一个变量的内容了!
- declare / typeset
[root@www ~]# declare [-aixr] variable 选项与参数: -a :将后面名为 variable 的变量定义成为数组 (array) 类型 -i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型 -x :用法与 export 一样,就是将后面的 variable 变成环境变量; -r :将变量配置成为 readonly 类型,该变量不可被更改内容,也不能 unset 范例一:让变量 sum 进行 100+300+50 的加总结果 [root@www ~]# sum=100+300+50 [root@www ~]# echo $sum 100+300+50 <==咦!怎么没有帮我计算加总?因为这是文字型态的变量属性啊! [root@www ~]# declare -i sum=100+300+50 [root@www ~]# echo $sum 450 <==瞭乎??
范例二:将 sum 变成环境变量 [root@www ~]# declare -x sum [root@www ~]# export | grep sum declare -ix sum="450" <==果然出现了!包括有 i 与 x 的宣告! 范例三:让 sum 变成只读属性,不可更动! [root@www ~]# declare -r sum [root@www ~]# sum=tesgting -bash: sum: readonly variable <==老天爷~不能改这个变量了! 范例四:让 sum 变成非环境变量的自定义变量吧! [root@www ~]# declare +x sum <== 将 - 变成 + 可以进行『取消』动作 [root@www ~]# declare -p sum <== -p 可以单独列出变量的类型 declare -ir sum="450" <== 看吧!只剩下 i, r 的类型,不具有 x 啰!
数组 (array) 变量类型
在 bash 里头,数组的配置方式是:
var[index]=content
与文件系统及程序的限制关系:ulimit
想象一个状况:我的 Linux 主机里面同时登陆了十个人,这十个人不知怎么搞的,同时开启了 100 个文件,每个文件的大小约 10MBytes ,请问一下,我的 Linux 主机的内存要有多大才够? 10*100*10 = 10000 MBytes = 10GBytes ...老天爷,这样,系统不挂点才有鬼哩!为了要预防这个情况的发生,所以我们的 bash 是可以『限制用户的某些系统资源』的,包括可以开启的文件数量,可以使用的 CPU 时间,可以使用的内存总量等等。如何配置?用 ulimit 吧!
[root@www ~]# ulimit [-SHacdfltu] [配额] 选项与参数: -H :hard limit ,严格的配置,必定不能超过这个配置的数值; -S :soft limit ,警告的配置,可以超过这个配置值,但是若超过则有警告信息。 在配置上,通常 soft 会比 hard 小,举例来说,soft 可配置为 80 而 hard 配置为 100,那么你可以使用到 90 (因为没有超过 100),但介于 80~100 之间时, 系统会有警告信息通知你! -a :后面不接任何选项与参数,可列出所有的限制额度; -c :当某些程序发生错误时,系统可能会将该程序在内存中的信息写成文件(除错用), 这种文件就被称为核心文件(core file)。此为限制每个核心文件的最大容量。 -f :此 shell 可以创建的最大文件容量(一般可能配置为 2GB)单位为 Kbytes -d :程序可使用的最大断裂内存(segment)容量; -l :可用于锁定 (lock) 的内存量 -t :可使用的最大 CPU 时间 (单位为秒) -u :单一用户可以使用的最大程序(process)数量。 范例一:列出你目前身份(假设为root)的所有限制数据数值 [root@www ~]# ulimit -a core file size (blocks, -c) 0 <==只要是 0 就代表没限制 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited <==可创建的单一文件的大小 pending signals (-i) 11774 max locked memory (kbytes, -l) 32 max memory size (kbytes, -m) unlimited open files (-n) 1024 <==同时可开启的文件数量 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 10240 cpu time (seconds, -t) unlimited max user processes (-u) 11774 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 范例二:限制用户仅能创建 10MBytes 以下的容量的文件 [root@www ~]# ulimit -f 10240 [root@www ~]# ulimit -a file size (blocks, -f) 10240 <==最大量为10240Kbyes,相当10Mbytes [root@www ~]# dd if=/dev/zero of=123 bs=1M count=20 File size limit exceeded <==尝试创建 20MB 的文件,结果失败了!想要复原 ulimit 的配置最简单的方法就是注销再登陆,否则就是得要重新以 ulimit 配置才行!不过,要注意的是,一般身份使用者如果以 ulimit 配置了 -f 的文件大小,那么他『只能继续减小文件容量,不能添加文件容量喔!』另外,若想要管控使用者的 ulimit 限值,可以参考第十四章的 pam 的介绍。
P313
范例三:我想要删除前面所有的目录,仅保留最后一个目录 [root@www ~]# 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 <==这两行其实是同一行啦! [root@www ~]# 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 为止的字符串 [root@www ~]# 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 <==这两行其实是同一行啦! 范例五:那如果我只想要保留第一个目录呢? [root@www ~]# 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 <==这两行其实是同一行啦! |
由于我是想要由变量内容的后面向前面删除,而我这个变量内容最后面的结尾是『/root/bin』,所以你可以看到上面我删除的数据最终一定是『bin』,亦即是『:*bin』那个 * 代表通配符!至于 % 与 %% 的意义其实与 # 及 ## 类似!这样理解否?
例题:
[root@www ~]# echo ${MAIL##/*/}
|
[root@www ~]# echo ${MAIL%/*}
范例六:将 path 的变量内容内的 sbin 取代成大写 SBIN: [root@www ~]# echo ${path/sbin/SBIN} /usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin: /usr/sbin:/usr/bin:/root/bin # 这个部分就容易理解的多了!关键词在于那两个斜线,两斜线中间的是旧字符串 # 后面的是新字符串,所以结果就会出现如上述的特殊字体部分啰! [root@www ~]# echo ${path//sbin/SBIN} /usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/SBIN:/usr/local/bin:/SBIN:/bin: /usr/SBIN:/usr/bin:/root/bin # 如果是两条斜线,那么就变成所有符合的内容都会被取代喔!
我们将这部份作个总结说明一下:
变量配置方式 | 说明 |
${变量#关键词} ${变量##关键词} |
若变量内容从头开始的数据符合『关键词』,则将符合的最短数据删除 若变量内容从头开始的数据符合『关键词』,则将符合的最长数据删除 |
${变量%关键词} ${变量%%关键词} |
若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据删除 若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据删除 |
${变量/旧字符串/新字符串} ${变量//旧字符串/新字符串} |
若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串取代』 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串取代』 |
- 变量的测试与内容替换
在某些时刻我们常常需要『判断』某个变量是否存在,若变量存在则使用既有的配置,若变量不存在则给予一个常用的配置。我们举底下的例子来说明好了,看看能不能较容易被你所理解呢!
范例一:测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root [root@www ~]# echo $username <==由于出现空白,所以 username 可能不存在,也可能是空字符串 [root@www ~]# username=${username-root} [root@www ~]# echo $username root <==因为 username 没有配置,所以主动给予名为 root 的内容。 [root@www ~]# username="vbird tsai" <==主动配置 username 的内容 [root@www ~]# username=${username-root} [root@www ~]# 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 [root@www ~]# username="" [root@www ~]# username=${username-root} [root@www ~]# echo $username <==因为 username 被配置为空字符串了!所以当然还是保留为空字符串! [root@www ~]# username=${username:-root} [root@www ~]# echo $username root <==加上『 : 』后若变量内容为空或者是未配置,都能够以后面的内容替换!
测试:若 str 不存在时,则 var 的测试结果直接显示 "无此变量" [root@www ~]# unset str; var=${str?无此变量} -bash: str: 无此变量 <==因为 str 不存在,所以输出错误信息 测试:若 str 存在时,则 var 的内容会与 str 相同! [root@www ~]# str="oldvar"; var=${str?novar} [root@www ~]# echo var="$var", str="$str" var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容
P317
命令别名配置: alias, unalias
P318
范例三:立刻将目前的数据写入 histfile 当中 [root@www ~]# history -w # 在默认的情况下,会将历史纪录写入 ~/.bash_history 当中! [root@www ~]# echo $HISTSIZE 1000
- 当我们以 bash 登陆 Linux 主机之后,系统会主动的由家目录的 ~/.bash_history读取以前曾经下过的命令,那么 ~/.bash_history 会记录几笔数据呢?这就与你 bash 的HISTFILESIZE 这个变量配置值有关了!
- 假设我这次登陆主机后,共下达过 100 次命令,『等我注销时,系统就会将 101~1100 这总共 1000 笔历史命令升级到 ~/.bash_history 当中。』也就是说,历史命令在我注销时,会将最近的 HISTFILESIZE 笔记录到我的纪录文件当中啦!
- 当然,也可以用 history -w 强制立刻写入的!那为何用『升级』两个字呢?因为 ~/.bash_history 记录的笔数永远都是 HISTFILESIZE 那么多,旧的信息会被主动的拿掉!仅保留最新的!
我们在第六章与第七章都曾谈过『相对路径与绝对路径』的关系,在本章的前几小节也谈到了 alias 与 bash 的内建命令。现在我们知道系统里面其实有不少的 ls 命令,或者是包括内建的 echo 命令,那么来想一想,如果一个命令 (例如 ls) 被下达时,到底是哪一个 ls 被拿来运行?很有趣吧!基本上,命令运行的顺序可以这样看:
- 以相对/绝对路径运行命令,例如『 /bin/ls 』或『 ./ls 』;
- 由 alias 找到该命令来运行;
- 由 bash 内建的 (builtin) 命令来运行;
- 透过 $PATH 这个变量的顺序搜寻到的第一个命令来运行。
举例来说,你可以下达 /bin/ls 及单纯的 ls 看看,会发现使用 ls 有颜色但是 /bin/ls 则没有颜色。因为 /bin/ls 是直接取用该命令来下达,而 ls 会因为『 alias ls='ls --color=tty' 』这个命令别名而先使用!如果想要了解命令搜寻的顺序,其实透过 type -a ls 也可以查询的到啦!上述的顺序最好先了解喔!
例题:
[root@www ~]# alias echo='echo -n' [root@www ~]# type -a echo echo is aliased to `echo -n' echo is a shell builtin echo is /bin/echo |
P320
bash 的进站与欢迎信息:/etc/issue prelogin message and identification file, /etc/motd message of the day
什么! bash 也有进站画面与欢迎信息喔?真假?真的啊!还记得在终端机接口 (tty1 ~ tty6) 登陆的时候,会有几行提示的字符串吗?那就是进站画面啊!那个字符串写在哪里啊?呵呵!在 /etc/issue 里面啊!先来看看:
[root@www ~]# cat /etc/issue
CentOS release 5.3 (Final)
Kernel
on an m
|
鸟哥是以完全未升级过的 CentOS 5.3 作为范例,里面默认有三行,较有趣的地方在于 与 m。就如同 $PS1 这变量一样,issue 这个文件的内容也是可以使用反斜杠作为变量取用喔!你可以 man issue 配合 man mingetty 得到底下的结果:
issue 内的各代码意义 |
d 本地端时间的日期; l 显示第几个终端机接口; m 显示硬件的等级 (i386/i486/i586/i686...); 显示主机的网络名称; o 显示 domain name; 操作系统的版本 (相当于 uname -r) 显示本地端时间的时间; s 操作系统的名称; v 操作系统的版本。 |
做一下底下这个练习,看看能不能取得你要的进站画面?
例题:CentOS release 5.3 (Final) (terminal: tty3)注意,tty3 在不同的 tty 有不同显示,日期则是再按下 [enter] 后就会所有不同。
Date: 2009-02-05 17:29:19
Kernel 2.6.18-128.el5 on an i686
Welcome!
CentOS release 5.3 (Final) (terminal: l)
Date: d
Kernel
on an m
Welcome!
你要注意的是,除了 /etc/issue 之外还有个 /etc/issue.NET 呢!这是啥?这个是提供给 telnet 这个远程登录程序用的。当我们使用 telnet 连接到主机时,主机的登陆画面就会显示 /etc/issue.Net 而不是 /etc/issue 呢!
至于如果您想要让使用者登陆后取得一些信息,例如您想要让大家都知道的信息,那么可以将信息加入 /etc/motd 里面去!例如:当登陆后,告诉登陆者,系统将会在某个固定时间进行维护工作,可以这样做:
[root@www ~]# vi /etc/motd Hello everyone, Our server will be maintained at 2009/02/28 0:00 ~ 24:00. Please don't login server at that time. ^_^ |
那么当你的使用者(包括所有的一般账号与 root)登陆主机后,就会显示这样的信息出来:
Last login: Thu Feb 5 22:35:47 2009 from 127.0.0.1 Hello everyone, Our server will be maintained at 2009/02/28 0:00 ~ 24:00. Please don't login server at that time. ^_^
P322
我们前几个小节谈到的命令别名啦、自定义的变量啦,在你注销 bash 后就会失效,所以你想要保留你的配置,就得要将这些配置写入配置文件才行
- /etc/profile (login shell 才会读)
你可以使用 vim 去阅读一下这个文件的内容。这个配置文件可以利用使用者的标识符 (UID) 来决定很多重要的变量数据,这也是每个使用者登陆取得 bash 时一定会读取的配置文件!所以如果你想要帮所有使用者配置整体环境,那就是改这里啰!不过,没事还是不要随便改这个文件喔这个文件配置的变量主要有:
- PATH:会依据 UID 决定 PATH 变量要不要含有 sbin 的系统命令目录;
- MAIL:依据账号配置好使用者的 mailbox 到 /var/spool/mail/账号名;
- USER:根据用户的账号配置此一变量内容;
- HOSTNAME:依据主机的 hostname 命令决定此一变量内容;
- HISTSIZE:历史命令记录笔数。CentOS 5.x 配置为 1000 ;
- ~/.bash_profile (login shell 才会读)
bash 在读完了整体环境配置的 /etc/profile 并藉此呼叫其他配置文件后,接下来则是会读取使用者的个人配置文件。在 login shell 的 bash 环境中,所读取的个人偏好配置文件其实主要有三个,依序分别是:
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
P324
- ~/.bashrc (non-login shell 会读)
谈完了 login shell 后,那么 non-login shell 这种非登陆情况取得 bash 操作接口的环境配置文件又是什么?当你取得 non-login shell 时,该 bash 配置文件仅会读取 ~/.bashrc 而已啦!那么默认的 ~/.bashrc 内容是如何?
[root@www ~]# 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
此外,咱们的 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-3.2$
|
P325
~/.bash_logout
这个文件则记录了『当我注销 bash 后,系统再帮我做完什么动作后才离开』的意思。你可以去读取一下这个文件的内容,默认的情况下,注销时, bash 只是帮我们清掉屏幕的信息而已。不过,你也可以将一些备份或者是其他你认为重要的工作写在这个文件中 (例如清空缓存盘),那么当你离开 Linux 的时候,就可以解决一些烦人的事情啰!
终端机的环境配置: stty, set
那么如何查阅目前的一些按键内容呢?可以利用 stty (setting tty 终端机的意思) 呢!stty 也可以帮助配置终端机的输入按键代表意义喔!
[root@www ~]# stty [-a] 选项与参数: -a :将目前所有的 stty 参数列出来; 范例一:列出所有的按键与按键内容 [root@www ~]# stty -a speed 38400 baud; rows 24; columns 80; 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; ....(以下省略)....如果你想要用 [ctrl]+h 来进行字符的删除,那么可以下达:
[root@www ~]# stty erase ^h
除了 stty 之外,其实我们的 bash 还有自己的一些终端机配置值呢!那就是利用 set 来配置的!我们之前提到一些变量时,可以利用 set 来显示,除此之外,其实 set 还可以帮我们配置整个命令输出/输入的环境。例如记录历史命令、显示错误内容等等。
[root@www ~]# set [-uvCHhmBx] 选项与参数: -u :默认不激活。若激活后,当使用未配置变量时,会显示错误信息; -v :默认不激活。若激活后,在信息被输出前,会先显示信息的原始内容; -x :默认不激活。若激活后,在命令被运行前,会显示命令内容(前面有 ++ 符号) -h :默认激活。与历史命令有关; -H :默认激活。与历史命令有关; -m :默认激活。与工作管理有关; -B :默认激活。与刮号 [] 的作用有关; -C :默认不激活。若使用 > 等,则若文件存在时,该文件不会被覆盖。 范例一:显示目前所有的 set 配置值 [root@www ~]# echo $- himBH # 那个 $- 变量内容就是 set 的所有配置啦! bash 默认是 himBH 喔! 范例二:配置 "若使用未定义变量时,则显示错误信息" [root@www ~]# set -u [root@www ~]# echo $vbirding -bash: vbirding: unbound variable # 默认情况下,未配置/未宣告 的变量都会是『空的』,不过,若配置 -u 参数, # 那么当使用未配置的变量时,就会有问题啦!很多的 shell 都默认激活 -u 参数。 # 若要取消这个参数,输入 set +u 即可! 范例三:运行前,显示该命令内容。 [root@www ~]# set -x [root@www ~]# echo $HOME + echo /root /root ++ echo -ne ' 33]0;root@www:~' # 看见否?要输出的命令都会先被打印到屏幕上喔!前面会多出 + 的符号!
P329
- 标准输入 (stdin) :代码为 0 ,使用 < 或 << ;
- 标准输出 (stdout):代码为 1 ,使用 > 或 >> ;
- 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;
范例一:观察你的系统根目录 (/) 下各目录的文件名、权限与属性,并记录下来 [root@www ~]# ll / <==此时屏幕会显示出文件名信息 [root@www ~]# ll / > ~/rootfile <==屏幕并无任何信息 [root@www ~]# ll ~/rootfile <==有个新档被创建了! -rw-r--r-- 1 root root 1089 Feb 6 17:00 /root/rootfile
怪了!屏幕怎么会完全没有数据呢?这是因为原本『 ll / 』所显示的数据已经被重新导向到 ~/rootfile 文件中了!那个 ~/rootfile 的档名可以随便你取。如果你下达『 cat ~/rootfile 』那就可以看到原本应该在屏幕上面的数据啰。如果我再次下达:『 ll /home > ~/rootfile 』后,那个 ~/rootfile 文件的内容变成什么?他将变成『仅有 ll /home 的数据』而已!咦!原本的『 ll / 』数据就不见了吗?是的!因为该文件的创建方式是:
- 该文件 (本例中是 ~/rootfile) 若不存在,系统会自动的将他创建起来,但是
- 当这个文件存在的时候,那么系统就会先将这个文件内容清空,然后再将数据写入!
- 也就是若以 > 输出到一个已存在的文件中,那个文件就会被覆盖掉啰!
- 1> :以覆盖的方法将『正确的数据』输出到指定的文件或装置上;
- 1>>:以累加的方法将『正确的数据』输出到指定的文件或装置上;
- 2> :以覆盖的方法将『错误的数据』输出到指定的文件或装置上;
- 2>>:以累加的方法将『错误的数据』输出到指定的文件或装置上;
- /dev/null 垃圾桶黑洞装置与特殊写法
想象一下,如果我知道错误信息会发生,所以要将错误信息忽略掉而不显示或储存呢?这个时候黑洞装置 /dev/null 就很重要了!这个 /dev/null 可以吃掉任何导向这个装置的信息喔!将上述的范例修订一下:
范例四:承范例三,将错误的数据丢弃,屏幕上显示正确的数据 [dmtsai@www ~]$ find /home -name .bashrc 2> /dev/null /home/dmtsai/.bashrc <==只有 stdout 会显示到屏幕上, stderr 被丢弃了 |
再想象一下,如果我要将正确与错误数据通通写入同一个文件去呢?这个时候就得要使用特殊的写法了!我们同样用底下的案例来说明:
范例五:将命令的数据全部写入名为 list 的文件中 [dmtsai@www ~]$ find /home -name .bashrc > list 2> list <==错误 [dmtsai@www ~]$ find /home -name .bashrc > list 2>&1 <==正确 [dmtsai@www ~]$ find /home -name .bashrc &> list <==正确 |
范例三:我不清楚 /tmp/abc 是否存在,但就是要创建 /tmp/abc/hehe 文件 [root@www ~]# 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 被创建起来。
整个流程图示如下:
图 5.2.1、 命令依序运行的关系示意图
例题:
ls /tmp/vbirding && echo "exist" || echo "not exist"意思是说,当 ls /tmp/vbirding 运行后,若正确,就运行 echo "exist" ,若有问题,就运行 echo "not exist" !那如果写成如下的状况会出现什么?
ls /tmp/vbirding || echo "not exist" && echo "exist"这其实是有问题的,为什么呢?由图 5.2.1 的流程介绍我们知道命令是一个一个往后运行,因此在上面的例子当中,如果 /tmp/vbirding 不存在时,他会进行如下动作:
- 若 ls /tmp/vbirding 不存在,因此回传一个非为 0 的数值;
- 接下来经过 || 的判断,发现前一个命令回传非为 0 的数值,因此,程序开始运行 echo "not exist" ,而 echo "not exist" 程序肯定可以运行成功,因此会回传一个 0 值给后面的命令;
- 经过 && 的判断,咦!是 0 啊!所以就开始运行 echo "exist" 。
经过这个例题的练习,你应该会了解,由于命令是一个接着一个去运行的,因此,如果真要使用判断,那么这个 && 与 || 的顺序就不能搞错。一般来说,假设判断式有三个,也就是:
command1 && command2 || command3
而且顺序通常不会变,因为一般来说, command2 与 command3 会放置肯定可以运行成功的命令,因此,依据上面例题的逻辑分析,您就会晓得为何要如此放置啰~这很有用的啦!而且.....考试也很常考~