Linux学习(13)--初识bash shell

来源:互联网 发布:必应for mac 编辑:程序博客网 时间:2024/06/05 02:40

bash shell

我们知道,操作系统其实就是一组软件,由于这组软件在控制整个硬件与管理系统的活动监测,如果这组软件被用户随意操作,若用户操作不当,将会使得整个系统崩溃。

但是系统总还是需要用户操作系统的,所有就有了在操作系统上面发展的应用程序。用户可以通过应用程序来指挥内核,达到我们的目的。其实 shell 的功能只是提供用户操作系统的一个接口,因此这个 shell 需要可以调用其它软件才好。

Tips:也就是说,只要能操作应用程序的借口能够被称之为 shell。狭义的 shell 指的是命令行方面的软件,包括本章要介绍的 bash 等。广义的 shell 则包括图形界面的软件,因为图形界面也能够操作各种应用程序。

bash 的内置命令:type

通过 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 lsls is aliased to `ls --color=auto'           <==列出ls最主要的使用情况[root@mars ~]# type -t lsalias                                  <==仅列出ls执行时的依据[root@mars ~]# type -a lsls 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

shell 的变量

变量就是以一组文字或符号等,来替代一些设置或者是一串保留的数据。比如我们常说的 $PATH 变量,我们之所在任何目录下都可以执行 ls 命令,都是因为 ls 这个执行文件所在的目录/usr/bin 在PATH里面,可以被随时检测到。

我们使用echo就可以看到变量:

[root@mars ~]# echo $PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

变量的设置规则

  • 1.变量与变量内容以一个等号“=”来连接

  • 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 $nameVBird's name               <==出现了[root@mars ~]# exit        <==退出

什么是子进程呢?也就是说在目前的这个 shell 的情况下,再去打开另一个新的 shell,新的那个 shell 就是子进程。一般情况下,父进程的自定义变量是无法在子进程内使用的。但是通过 export 将变量变成环境变量后,就可以了。

Tips:那么在命令执行中,反单引号( `)这个符号有什么用呢?

比如,我想知道每个 crontab 相关文件名的权限,就可以这样做:ls -l `locate crontab`

变量的有效范围

之前我们提到父进程定义的变量不能被子进程所得知,除非使用 export ,现在我们就来说说父进程与子进程的概念:

father_sub process

子进程仅会集成父进程的环境变量,子进程不会继承父进程的自定义变量。所以我们才需要 export,有的地方也提到全局变量和局部变量,我们大致可以认为。环境变量就是全局变量,自定义变量就是局部变量。

变量键盘读取、数组与声明

读取:read

范例1:让用户由键盘输入内容,将该内容变成名为 atest 的变量[root@mars ~]# read atestThis is a test.             <==此时光标就在等待你输入[root@mars ~]# echo $atest This is a test.             <==刚刚输入的内容已经变成了一个变量

declare / typeset

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 $sum100+300+50             <==并没有帮我们进行计算,因为这是文字类型的变量[root@mars ~]# declare -i sum=100+300+50[root@mars ~]# echo $sum450                   <==得到了正确的结果

变量默认为字符串,所以不指定变量类型,则 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类型

有趣的是,如果你不小心设置了某个变量的只读属性,你只有注销再登录才能复原该变量的类型。

数组:array

在 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#数组的读取建议用${数组}的方式来读取

变量内容的删除、替代与替换

变量内容的删除

#先让小写的 path 设置得与 PATH 内容相同,便于实验[root@mars ~]# path=${PATH}[root@mars ~]# echo $path /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin范例1:假设我不喜欢 kerberos,如何操作[root@mars ~]# echo ${path#/*kerberos/bin:}/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

上面的这个例子,我们可以详细地来剖析一下它:

${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#如果是两条斜线,那么所有的符合条件的都会被替换

总结一下前面的各项:

变量设置方式 说明 ${变量#关键字} 若变量从头开始的数据符合“关键字”,则将符合的最短数据删除 ${变量##关键字} 若变量从头开始的数据符合“关键字”,则将符合的最长数据删除 ${变量%关键字} 若变量从尾向前的数据符合“关键字”,则将符合的最短数据删除 ${变量%%关键字} 若变量从尾向前的数据符合“关键字”,则将符合的最长数据删除 ${变量/旧字符/新字符串} 若变量内容符合“旧字符串”,则第一个旧字符川会被替换 ${变量//旧字符/新字符串} 若变量内容符合“旧字符串”,则全部的旧字符川会被替换

变量的测试与内容替换

范例1:测试一下是否存在 username 这个变量,若不存在则赋值内容为 root[root@mars ~]# echo $username               <==由于出现空白,所以 username 可能不存在,也可能是空字符串[root@mars ~]# username=${username-root}[root@mars ~]# echo $usernameroot           <==因为 username 没有设置,所以主动赋给root的内容[root@mars ~]# username="mars name"        <==主动设置内容[root@mars ~]# username=${username-root}[root@mars ~]# echo $usernamemars name      <==因为已经设置过,所有不会用root替代

上面的范例中,重点在于减号“ - ”后面接的关键字,我们可以这样理解:

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                <==加上“:”后若变量内容为空或者是未设置,都能够以后面的内容替换

命令别名与历史命令

alias

当我们的惯用命令很长的时候,我们可能希望通过某种方式让它便于输入。这就是命令别名,比如,在 Windows 中清屏是 “cls”,而 Linux 中是 “clear”,那我们其实可以通过别名的方式让 cls 同样可以使用:

[root@mars ~]# alias cls="clear"#或者其他的命令,比如删除[root@mars ~]# alias rmf="rm -f"#我们如何看目前有哪些命令别名呢[root@mars ~]# aliasalias 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

历史命令:history

直接输入 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。那么其实这些记录一开始都是写在内存中的,注销的时候才会更新。所以,最后一个注销的才会是最后写入的数据(其实都有记录,只是被覆盖了)

0 0