我们知道,操作系统其实就是一组软件,由于这组软件在控制整个硬件与管理系统的活动监测,如果这组软件被用户随意操作,若用户操作不当,将会使得整个系统崩溃。
但是系统总还是需要用户操作系统的,所有就有了在操作系统上面发展的应用程序。用户可以通过应用程序来指挥内核,达到我们的目的。其实 shell 的功能只是提供用户操作系统的一个接口,因此这个 shell 需要可以调用其它软件才好。
Tips:也就是说,只要能操作应用程序的借口能够被称之为 shell。狭义的 shell 指的是命令行方面的软件,包括本章要介绍的 bash 等。广义的 shell 则包括图形界面的软件,因为图形界面也能够操作各种应用程序。
通过 type 命令,我们可以知道这个命令是外部命令(非 bash 所提供的命令)或是内置在 bash 当中的。比如:
[root@mars ~]# type [-tpa] name 选项与参数: type:不加任何参数,会显示 name 是外部命令还是内部 -t:当加入 -t 参数时,type 会将 name 以下面这些字显示出它的意义: file:表示为外部命令 alias:表示该命令为命令别名所设置的名称 builtin:表示该命令为内置命令 -p:如果后面接的 name 为外部命令时,才会显示完整的文件名 -a:会由 PATH 设置的路径中,将所有含 name 的命令都列出来,包含alias 范例1:查询 ls 是否为内置 [root@mars ~]# type ls ls is aliased to `ls --color=auto' <==列出ls最主要的使用情况 [root@mars ~]# type -t ls alias <==仅列出ls执行时的依据 [root@mars ~]# type -a ls ls is aliased to `ls --color=auto' <==最先使用 aliase ls is /usr/bin/ls <==还有找到外部命令在/bin/ls 范例2:那么 cd 呢? [root@mars ~]# type cd cd is a shell builtin变量就是以一组文字或符号等,来替代一些设置或者是一串保留的数据。比如我们常说的 $PATH 变量,我们之所在任何目录下都可以执行 ls 命令,都是因为 ls 这个执行文件所在的目录/usr/bin 在PATH里面,可以被随时检测到。
我们使用echo就可以看到变量:
[root@mars ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin1.变量与变量内容以一个等号“=”来连接
2.等号两边不能直接接空格符,如下所示为错误的
myname = VBird 或 myname=VBird Tsai
3.变量名称只能是英文字母与数字,但是开头字符不能是数字,以下为错误:
2myname=VBird
4.变量内容若有空格符可使用双引号或者单引号将变量内容结合起来,但
双引号内的特殊字符如 $ 等,可以保留原本的特性,如下所示:var = “lang is $LANG” 则 echo \$var 可得 lang is en_US
单引号内的特殊字符则为一般字符(纯文本),如下:var=’lang si $LANG’ 则 echo \$var 可得 lang is \$LAGN
5.可用转义字符“\”将特殊符号(如Enter、$、\、空格等)变成一般字符
6.在一串命令中,还需要通过其他的命令提供的信息,可以使用反单引号“ `命令` ”或“ $(命令)”。注意,是键盘数字1 键旁边的那个键,不是单引号。例如湖区内核版本的设置:
version=$(uname -r)再echo $version可得“2.6.18-128.el5”
7.若该变量为了增加变量内容时,则可用“$ 变量名称”或 \${变量} 累加内容,如下所示:
PATH=”$PATH”:/home/bin
8.若该变量需要在其他子进程执行,则需要以 export 来使变量变成环境变量
export PATH
9.通常大写字符为系统默认变量,自行设置变量可以使用小写字符,方便判断
10.取消变量的方法为使用“unset 变量名称”
具体设置内容以及注意项,如下所示:
范例1:设置一个变量 name,且内容为 VBird's name [root@mars ~]# name=VBird's name # 单引号与双引号必须要成对,在上面的设置中仅有一个单引号,因此当你按下 # Enter后,你还可以继续输入变量内容。这与我们需要的功能不同,按Ctrl+C结束 [root@mars ~]# name="VBird's name" <==正确 [root@mars ~]# name='VBird's name' <==单引号不成对,失败 [root@mars ~]# name=VBird\'s\ name <==转义字符也行 范例2:在PATH变量中累加 /home/dmtsai/bin 这个目录 [root@mars ~]# PATH=$PATH:/home/dmtsai/bin [root@mars ~]# PATH="$PATH":/home/dmtsai/bin [root@mars ~]# PATH=${PATH}:/home/dmtsai/bin # 三种方式都可以 范例3:如果要将 name 的内容多出“yes”呢 [root@mars ~]# name=$nameyes # echo 发现为空白,如果没有双引号,那么变量就变成了$nameyes这个的内容 # 但是我们没有设置 nameyes 这个变量,自然为空 [root@mars ~]# name="$name"yes [root@mars ~]# name=${name}yes <==两种方式都可以 范例4:如何让刚刚的 name 可以用在下个 shell 的程序 [root@mars ~]# bash <==进入子进程 [root@mars ~]# echo $name <==什么都没有 [root@mars ~]# exit <==离开子进程 [root@mars ~]# export name [root@mars ~]# bash [root@mars ~]# echo $name VBird's name <==出现了 [root@mars ~]# exit <==退出什么是子进程呢?也就是说在目前的这个 shell 的情况下,再去打开另一个新的 shell,新的那个 shell 就是子进程。一般情况下,父进程的自定义变量是无法在子进程内使用的。但是通过 export 将变量变成环境变量后,就可以了。
Tips:那么在命令执行中,反单引号( `)这个符号有什么用呢?
比如,我想知道每个 crontab 相关文件名的权限,就可以这样做:ls -l `locate crontab`
之前我们提到父进程定义的变量不能被子进程所得知,除非使用 export ,现在我们就来说说父进程与子进程的概念:
子进程仅会集成父进程的环境变量,子进程不会继承父进程的自定义变量。所以我们才需要 export,有的地方也提到全局变量和局部变量,我们大致可以认为。环境变量就是全局变量,自定义变量就是局部变量。
declare / typeset 是一样的功能,就是声明变量的类型。如果使用 declare 后面并没有接任何参数,那么 bash 会主动列出所有的变量名称和内容,和 set 一样。
[root@mars ~]# declare [-aixr] variable 选项与参数: -a:将后面名为 variable 的变量定义为数组(array)类型 -i:将后面名为 variable 的变量定义为整数数字(integer)类型 -x:与 export 相同 -r:将变量设置成为 readonly 类型,该变量不可被改变内容,也不能重设 范例1:让 sum 进行 100+300+50 累加结果 [root@mars ~]# sum=100+300+50 [root@mars ~]# echo $sum 100+300+50 <==并没有帮我们进行计算,因为这是文字类型的变量 [root@mars ~]# declare -i sum=100+300+50 [root@mars ~]# echo $sum 450 <==得到了正确的结果变量默认为字符串,所以不指定变量类型,则 1 + 2 为一个字符串而不是计算式。bash中的数值运算,默认最多仅能到达整数类型,所以1 /3 结果是 0。
范例2:让 sum 变成非环境变量的自定义变量(以用declare -xr sum 变为了环境变量和只读) [root@mars ~]# declare +x sum <==将 - 变成 + 可以进行取消操作 [root@mars ~]# declare -p sum <== -p 可以单独列出变量类型 declare -ir sum="450" <==只剩下了i,r类型有趣的是,如果你不小心设置了某个变量的只读属性,你只有注销再登录才能复原该变量的类型。
在 bash 中,数组的设置方式是 var[index] = content,目前bash提供的是一维数组:
#设置一个数组 [root@mars ~]# var[1]="small min" [root@mars ~]# var[2]="big min" [root@mars ~]# var[3]="nice min" [root@mars ~]# echo "${var[1]},${var[2]},${var[3]}" small min,big min,nice min #数组的读取建议用${数组}的方式来读取上面的这个例子,我们可以详细地来剖析一下它:
${variable#/*kerberos/bin:}
这个 $ 是关键字,用在删除模式是必须的${variable#/*kerberos/bin:} 这个变量原本的名称,比如我们这里写的 path
${variable #/*kerberos/bin:} 这就是重点,代表从变量内容的最前面开始向右删除,且仅删除最短的那个
${variable#/*kerberos/bin:} 代表要被删除的部分,由于 # 代表由开头开始删除,所有这里由开始的 / 写起 * 通配符,一直匹配到/kerberos/bin为止 从上面的范例我们可以看出,path这个变量被删除的内容如下所示:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
接下来继续看 # 的功能:
范例2:删除前面的目录,保留后面的一个 [root@mars ~]# echo ${path#/*:} /usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #由于一个 # 仅删除最短的那个用删除线来看是这样:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@mars ~]# echo ${path##/*:} /root/bin #一个 # 变成 ## 之后,它变成了删除最长的那个用删除线来看是这样:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
所以我们可以发现删除的规则:
#:符合替换文字的“最短的”那一个##:符合替换文字的“最长的”那一个前面提到的是从前向后,那么从后向前删除呢?其实就使用百分比(%)符号,就可以从后往前删除了,规则和 # 是一致的。
变量的替换也很好理解:
范例3:将 path 的变量 sbin 替换成大写的 SBIN [root@mars ~]# echo ${path/sbin/SBIN} /usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #关键在与两个斜线,两个斜线中间的是旧字符 #后面的是新字符,所以结果就出现了上述的情况(只有一个SBIN被修改) [root@mars ~]# echo ${path//sbin/SBIN} /usr/kerberos/SBIN:/usr/kerberos/bin:/usr/local/SBIN:/usr/local/bin:/usr/SBIN:/usr/bin:/root/bin #如果是两条斜线,那么所有的符合条件的都会被替换总结一下前面的各项:
变量设置方式说明${变量#关键字}若变量从头开始的数据符合“关键字”,则将符合的最短数据删除${变量##关键字}若变量从头开始的数据符合“关键字”,则将符合的最长数据删除${变量%关键字}若变量从尾向前的数据符合“关键字”,则将符合的最短数据删除${变量%%关键字}若变量从尾向前的数据符合“关键字”,则将符合的最长数据删除${变量/旧字符/新字符串}若变量内容符合“旧字符串”,则第一个旧字符川会被替换${变量//旧字符/新字符串}若变量内容符合“旧字符串”,则全部的旧字符川会被替换上面的范例中,重点在于减号“ - ”后面接的关键字,我们可以这样理解:
new_var=${old_var_content}
新的变量,主要用来替代旧变量new_var=${old_var_content}
这是本例中的关键字,必须要存在new_var=${old_var-content}
旧的变量,被测试的选项new_var=${old_var-content}
变量的内容,在本例中,是给未给予变量的内容不过这还是有点问题,因为 username 可能被设置为空字符串。这样的话,你还可以通过这种方式:
范例2:若 username 未设置或为空字符串,则将 usernmae 内容设置为 root [root@mars ~]# username="" [root@mars ~]# username=${username-root} [root@mars ~]# echo $username <==因为 username 被设置为空字符串,所以还是保留 [root@mars ~]# username=${username:-root} [root@mars ~]# echo $username root <==加上“:”后若变量内容为空或者是未设置,都能够以后面的内容替换当我们的惯用命令很长的时候,我们可能希望通过某种方式让它便于输入。这就是命令别名,比如,在 Windows 中清屏是 “cls”,而 Linux 中是 “clear”,那我们其实可以通过别名的方式让 cls 同样可以使用:
[root@mars ~]# alias cls="clear" #或者其他的命令,比如删除 [root@mars ~]# alias rmf="rm -f" #我们如何看目前有哪些命令别名呢 [root@mars ~]# alias alias cls='clear' alias cp='cp -i' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias grep='grep --color=auto' alias l.='ls -d .* --color=auto' alias ll='ls -l --color=auto' alias ls='ls --color=auto' alias mv='mv -i' alias rm='rm -i' alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'至于要取消别名,就用 unalias,比如unalias cls
直接输入 histroy 就可以看到我们曾经执行的命令,默认是保存1000条。我们来看看它的参数:
[root@mars ~]# history [n] [root@mars ~]# history [-c] [root@mars ~]# history [-raw] histfiles 选项与参数: n :数字,列出最近的 n 条命令 -c:将目前 shell 中的所有 history 清除 -a:将目前新增的history命令新增入histfiles,若没有则默认写入~/.bash_history -r:将 histfiles 读入 history -w:将 history 写入 histfiles这里着重讲一下,history的另一个作用:
[root@mars ~]# !number [root@mars ~]# !command [root@mars ~]# !! 参数: numbner:执行第几条命令 command:由最近的命令向前搜索命令串开头为 command 的命令并执行 !! :执行上个命令,相当于 ↑+Enter [root@mars ~]# history 66 man rm 67 alias 68 man history 69 history [root@mars ~]# !66 <==执行第66条命令 [root@mars ~]# !! <==执行上一个,即本例中的 !66 [root@mars ~]# !al <==执行最近的以 al 开头的命令(即本例中的第 67 个)Tips:同一个账号同时多次登录的 history 写入问题
假设多个bansh同时写入,还是都是root。那么其实这些记录一开始都是写在内存中的,注销的时候才会更新。所以,最后一个注销的才会是最后写入的数据(其实都有记录,只是被覆盖了)