认识bash这个shell

硬件、内核与shell

我们知道管理计算节硬件的其实就是操作下统的内核(kernel),一般用户只能通过shell来和内核沟通,以便让内核完成我们想完成的任务,那么系统有多少shell可用呢?为什么要使用bash?
举个例子,当用户需要计算机播放音乐时,这里的相关组件和操作有
硬件:具有发声能力的硬件设备,如有声卡的设备
内核管理:操作系统的内核可以支持这个芯片组,当然还需要提供芯片的驱动程序
应用程序:支持用户输入发生的命令

实际场景是,我们总需要用户使用操作系统,所以就有了在操作系统上面发展的应用程序,用户可以通过应用程序来指挥内核,从操作系统的角色来看,应用程序在最外层,因此也被称为壳程序(shell),我们可以理解shell是连接用户和内核的桥梁,将用户的输入传递给内核。

其实壳程序的功能只是提供用户操作系统的一个界面,因此壳程序还要能调用其他软件才可以,像之前提到的man、chmod、chown等命令都是独立的应用程序。但我们可以通过壳程序(命令行模式)来操作这些应用程序。

广义的壳程序:只要能够提供用户和内核沟通的软件都叫壳程序
狭义的壳程序:指命令行相关的软件,包括bash(后面会介绍)

为什么要学命令行模式的shell?

  • 命令行模式的shell几乎所有的操作系统都一样
  • 远程管理:命令行模式传输速度更快,不容易掉线会信息外流

系统的合法shell与/etc/shells功能

早年的UNIX年代发展着众多,shell依据发展者的不同就有许多的版本,简单说一下shell的历史吧。第一个流行的shell是Steven Bourne发展出来的,为了纪念他称为Bourne shell简称sh,后来另一个广为流传的shell由伯克利大学的Bill joy设计依附于BSD版的UNIX系统的shell,这个shell的语法类似C语言,因此得名C shell简称csh。Linux使用的这个版本称为【Bourne Again Shell】简称bash,这个是Bourne shell的加强版,也是具有GNU的架构发展出来的。

检查linux下可用的shell

  1. [root@cnsz02vl29079 ~]# cat /etc/shells
  2. /bin/sh
  3. /bin/bash # Linux默认的shell
  4. /sbin/nologin
  5. /usr/bin/sh
  6. /usr/bin/bash
  7. /usr/sbin/nologin
  8. /bin/tcsh # 整合C Shell,提供更多功能
  9. /bin/csh # 已经被 /bin/tcsh所替换

为什么系统合法/可用的shell要写在/etc/shells文件中?
因为系统某些服务在运行过程中,会检查用户能使用的shells,就是通过查询/etc/shells文件
那么用户什么时候可以取得shell来工作,默认取到的是哪个shell呢?
1.在终端登录linux时,就会取得一个shell
2.登录取得的shell就记录在/etc/passwd这个文件内

  1. [root@cnsz02vl29079 ~]# cat /etc/passwd
  2. root:x:0:0:root:/root:/bin/bash
  3. bin:x:1:1:bin:/bin:/sbin/nologin
  4. daemon:x:2:2:daemon:/sbin:/sbin/nologin
  5. adm:x:3:4:adm:/var/adm:/sbin/nologin
  6. lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
  7. sync:x:5:0:sync:/sbin:/bin/sync
  8. shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
  9. halt:x:7:0:halt:/sbin:/sbin/halt
  10. 。。。
  11. centos:x:1000:1000:Cloud User:/home/centos:/bin/bash

这里记录很多,我们需要知道的是,每一行最后一个数据,就是登录后能获取的默认shell,可以看到root是/bin/bash,其他账号可能不同。

Bash shell的功能

目前Linux发行版的标准shell是bash,同时bash兼容于sh,并依据用户需求而加强的shell版本。bash主要的优点有:

  • 历史命令(history)
  • 命令与文件补全功能(Tab键)
  • 命令别名设置功能(alias)
  • 任务管理
  • 程序化脚本:(shell scripts)
  • 通配符

    查询命令是否为Bash shell的内置命令:type

    如何知道命令是来自于外部命令,或是内置在bash中?答案是,利用type命令
    type [-tpa] name
    选项与参数:
    1. :不加任何参数时,type会显示name是外部命令还是bash内置命令
    2. -t:加入该参数,typename以下面字眼显示出它的意义:
    3. file : 表示为外部命令
    4. alias : 表示为命令别名
    5. builtin: 表示为bash内置命令
    6. -p:后面接外部的name为外部命令时,才会显示完整文件名
    7. -a:会由PATH变量定义的路径中,将所有含name的命令都列出来,包含alias
    基本使用
    1. [root@cnsz02vl29079 ~]# type cat
    2. cat is hashed (/bin/cat)
    3. [root@cnsz02vl29079 ~]# type ls
    4. ls is aliased to `ls --color=auto'
    5. [root@cnsz02vl29079 ~]# type cd
    6. cd is a shell builtin
    7. [root@cnsz02vl29079 ~]# type -t cat
    8. file

    命令执行与快速编辑按钮

    换行输入长命令
    1. # 通过反斜杠\加Enter,注意:斜杠\是紧接Enter,中间无任何字符串
    2. [root@cnsz02vl29079 ~]# cp /var/spool/mail/ \
    3. >
    命令行组合快捷键
命令 说明
[ctrl] + u/[ctrl] + k [ctrl] + u:从光标处向前删除命令串;[ctrl] + k:从光标处向后删除命令串
[ctrl] + a/[ctrl] + e [ctrl] + a:光标移动到命令串最前面;[ctrl] + e:光标移动到命令串最后面

shell的变量功能

什么是变量?
通过特定字符代表不固定的内容
使用变量的好处

  • 变量的可变性与方便性
  • 影响bash环境操作的变量
  • 方便脚本程序设计

    变量的使用

  • 读取变量 echo $变量名

    1. [root@cnsz02vl29079 app]# echo $PATH
    2. /app/jdk1.8.0_121/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
    3. [root@cnsz02vl29079 app]# echo ${PATH}
    4. /app/jdk1.8.0_121/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
    5. [root@cnsz02vl29079 app]# echo $HOME
    6. /root
  • 设置变量 变量名=变量值
    变量的设置规则 ```shell

    当变量名未被设置时,输出为空

    [root@cnsz02vl29079 app]# echo $myname

[root@cnsz02vl29079 app]# myname=Shigl [root@cnsz02vl29079 app]# echo $myname Shigl

变量命名

[root@cnsz02vl29079 app]# 1myname=shigl bash: 1myname=shigl: command not found [root@cnsz02vl29079 app]# myname=shigl [root@cnsz02vl29079 app]# echo $myname shigl

  1. 1. 设置变量名时左右不能有空格
  2. 1. 变量名只能是英文字母与数字,但开头不能是数字
  3. 1. 变量内容可结合引号使用<br />单引号内的特殊字符如$,仅为一般字符(纯文本)<br />双引号内的特殊字符如$,可以保留原本含义(转义)
  4. ```shell
  5. [root@cnsz02vl29079 app]# echo $LANG
  6. en_US.UTF-8
  7. [root@cnsz02vl29079 app]# myvar="echo lang $LANG"
  8. [root@cnsz02vl29079 app]# echo $myvar
  9. echo lang en_US.UTF-8
  10. [root@cnsz02vl29079 app]# myvar='echo lang $LANG'
  11. [root@cnsz02vl29079 app]# echo $myvar
  12. echo lang $LANG
  1. 变量内容配合其他额外命令,通过单引号【`命令】或【$(命令)】,注意单引号指的是键盘1左边的那个逗号

    1. [root@cnsz02vl29079 app]# version=$(uname -r)
    2. [root@cnsz02vl29079 app]# echo $version
    3. 3.10.0-693.17.1.el7.x86_64
  2. 变量扩增(在原变量值的基础上增加内容),可使用【”认识与学习BASH - 图1{变量}】在后面累加内容
    如:PATH=”$PATH”:/home/bin或PATH={PATH}:/home/bin

    1. [root@cnsz02vl29079 app]# echo $version
    2. 3.10.0-693.17.1.el7.x86_64
    3. [root@cnsz02vl29079 app]# version=${version}:sss
    4. [root@cnsz02vl29079 app]# echo $version
    5. 3.10.0-693.17.1.el7.x86_64:sss
  3. 变量需要其他其他子程序执行,则需要以export来使变量成为环境变量
    export PATH
    export命令
    用法 export [-fpm] [变量名]=[变量值]
    参数说明:

    1. [root@cnsz02vl29079 app]# echo $myname
    2. Shigl
    3. [root@cnsz02vl29079 app]# export myname
    4. [root@cnsz02vl29079 app]# export -p
    5. declare -x CLASSPATH=".:/app/jdk1.8.0_121/lib/dt.jar:/app/jdk1.8.0_121/lib/tools.jar"
    6. declare -x HISTCONTROL="ignoredups"
    7. ...
    8. declare -x myname="Shigl"
    9. [root@cnsz02vl29079 app]# export -p | grep myname
    10. declare -x myname="Shigl"
    11. [root@cnsz02vl29079 app]# export -n myname
    12. [root@cnsz02vl29079 app]# export -p | grep myname
    13. [root@cnsz02vl29079 app]# echo $myname
    14. Shigl
    • -f  代表[变量名称]中为函数名称。
    • -n  删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
    • -p  列出所有的shell赋予程序的环境变量。

说明:这里的子程序可以理解为子进程,通常主进程里的变量是无法在子进程中使用的,export将变量转成环境变量后是可以在子进程中使用的

  1. [root@cnsz02vl29079 app]# echo $myname
  2. Shigl
  3. [root@cnsz02vl29079 app]# bash <== 进入所谓的子进程
  4. [root@cnsz02vl29079 app]# echo $myname
  5. [root@cnsz02vl29079 app]# exit <== 退出子进程
  6. exit
  7. [root@cnsz02vl29079 app]# echo $myname
  8. Shigl
  9. # export转为环境变量后
  10. [root@cnsz02vl29079 app]# echo $myname
  11. Shigl
  12. [root@cnsz02vl29079 app]# export myname
  13. [root@cnsz02vl29079 app]# bash
  14. [root@cnsz02vl29079 app]# echo $myname
  15. Shigl
  1. 通常大写字符为系统默认变量,自行设置的变量使用小写字符(仅为规范,并非强制)
  2. 取消变量的命令为unset: 【unset 变量】
    1. [root@cnsz02vl29079 app]# echo $myname
    2. shigl
    3. [root@cnsz02vl29079 app]# unset myname
    4. [root@cnsz02vl29079 app]# echo $myname
    通过变量名进入目录 ```shell [root@cnsz02vl29079 kernel]# cd /app/ [root@cnsz02vl29079 app]# cd /lib/modules/$(uname -r)/kernel/ [root@cnsz02vl29079 kernel]# pwd /lib/modules/3.10.0-693.17.1.el7.x86_64/kernel

实际相当于进行了两步操作

1.获取uname -r的路径 2.将1的结果带入到原命令

  1. <a name="92586e80"></a>
  2. ### 环境变量的功能
  3. 查看环境变量
  4. - env<br />列出所有环境变量
  5. - export<br />同env作用相同,只是export还有其他额外的功能
  6. ```shell
  7. [root@cnsz02vl29079 ~]# env
  8. XDG_SESSION_ID=77716
  9. HOSTNAME=cnsz02vl29079
  10. SHELL=/bin/bash
  11. HISTSIZE=300
  12. ...
  13. SUDO_GID=1001
  14. _=/bin/env
  15. [root@cnsz02vl29079 ~]# export
  16. declare -x HISTSIZE="300"
  17. ...
  18. declare -x XDG_SESSION_ID="77716"

常见的环境变量

  • HOME
    代表用户的根目录,cd ~ 或cd进入的就是。
  • SHELL
    当前使用的shell是哪种程序。Linux默认使用/bin/bash
  • MAIL
    使用mail命令收信时,系统会读取的邮箱文件
  • LANG
    语系数据,启动某些perl的程序语言文件时,会主动的分析语系数据文件
  • RANDOM
    随机数变量,一般Linux发行版都会有的随机数生成器,在/dev/random,在bash的环境下,RANDOM变量的内容介于0~32767

    # 直接输出结果会在0~32767之间
    [root@cnsz02vl29079 ~]# echo ${RANDOM}
    5472
    # 输出0~10之前的随机数 declare -i 后续会[declare]部分会介绍
    [root@cnsz02vl29079 ~]# declare -i number=$RANDOM*10/32767;echo $number
    4
    

    查看所有变量

  • set(含环境变量和自定义变量)

提示符设置(PS1)
当我们每次按[Enter]按键去执行某个命令后,最后再次出现提示字符时,就会主动去读取这个变量值。

[root@cnsz02vl29079 ~]# echo $PS1
[\u@\h \W]\$
# 可以看到不同的用户提示符也不同(root用户为#,其他用户为$)
[root@cnsz02vl29079 ~]# exit
exit
[shigl@cnsz02vl29079 app]$

特殊含义的变量

  • $:(本shell的PID)
    $代表当前sehll的进程号

    [root@cnsz02vl29079 app]# echo $$
    13809
    
  • ?:(上一个执行命命令的返回值)
    ?代表上一个(仅代表$?的上一个)命令的返回值,一般来说如果成功执行该命令,返回值为0,非0表示执行异常

    [root@cnsz02vl29079 app]# $?
    bash: 0: command not found
    [root@cnsz02vl29079 app]# echo $?
    127        # 返回值非0,表示上一个命令执行异常
    [root@cnsz02vl29079 app]# echo $?
    0        # 返回0,上一个命令为echo $?
    

export:自定义变量转成环境变量
通过前面的学习,我们知道环境变量和自定义的本质区别在于【该变量能否被子进程继续引用】

父进程:当登录linux并取得一个bash,这个bash就是一个独立的进程(可用PID识别)
子进程:输入bash进入的新bash环境就叫做子进程

[root@cnsz02vl29079 app]# echo $$
13809
# 进入新的bash,pid是不同的
[root@cnsz02vl29079 app]# bash
[root@cnsz02vl29079 app]# echo $$
17049
# 退回到首次进入的bash环境
[root@cnsz02vl29079 app]# exit
exit
[root@cnsz02vl29079 app]# echo $$
13809

进程与变量的关系?
子进程会继承父进程的环境变量,而不会继承父进程的自定义变量,因此在新的bash中自定义变量会失效,那么export命令正好就可以将自定义变量变成环境变量供子进程使用

# 用法
export 变量名称
[root@cnsz02vl29079 app]# name=SHIGL
[root@cnsz02vl29079 app]# export name
[root@cnsz02vl29079 app]# export
declare -x HISTSIZE="300"
...
declare -x XDG_SESSION_ID="77716"
declare -x name="SHIGL"

影响显示结果的语系变量(local)

语系问题常见的场景比如 man command的方式查询命令说明文件时,文件内容和语系不同而产生乱码。如何查看自己的linux支持哪些语系呢,就要用到locale命令

[root@cnsz02vl29079 app]# locale -a
# 以下是我们可以通过更改变量值自定义设置的语系变量
[root@cnsz02vl29079 app]# locale
LANG=en_US.UTF-8            # 主语言的环境
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=                        # 整体语系的环境

说明:
通常情况其他语系变量都未设置,只要有设置LANG或LC_ALL时,则其他语系变量就会被这两个变量所替换,所以一般情况我们主要关注LANG/Lc_ALL变量

系统默认的语系定义在/etc/locale.conf文件中

[root@cnsz02vl29079 app]# cat /etc/locale.conf
LANG=en_US.UTF-8

变量的有效范围

变量的有效范围,根据export命令的功能,我们可以这么理解

环境变量=全局变量
自定义变量=局部变量

变量读取、类型与声明

之前提到的变量都是直接设置的,那么有些需要用户自己输入的场景该怎么处理呢

  • read(读取来自键盘输入的变量)
    read [-pt] variable
    选项与参数

    -p:后面可以接提示符
    -t:后面接等待时间[单位:秒]
    

    基本使用

    [root@cnsz02vl29079 app]# read test
    123
    [root@cnsz02vl29079 app]# echo $test
    123
    # 带提示符和等待时间
    [root@cnsz02vl29079 app]# read -p "test command" -t 10 test
    # 10s未输入,则关闭等待用户输入
    test command[root@cnsz02vl29079 app]#
    
  • declare(申明变量的类型)
    declare后面没有接任何参数,那么bash就会主动的将所有的变量名称与内容统统显示出来,就如同set命令
    declare [-aixr] variable
    选项与参数

    -a:将变量variable定义成数组(array)类型
    -i:将变量variable定义成数组(int)类型
    -x:同export,将variable变成环境变量
    -r:将变量设置为readonly类型,变量内容不能被更改,也不能unset
    

    基本使用
    ```shell [root@cnsz02vl29079 ~]# declare -i num=12*12 [root@cnsz02vl29079 ~]# echo ${num} 144 [root@cnsz02vl29079 ~]# export | grep num [root@cnsz02vl29079 ~]# declare -x num [root@cnsz02vl29079 ~]# export | grep num declare -ix num=”144” [root@cnsz02vl29079 ~]# declare -r num [root@cnsz02vl29079 ~]# num=100 bash: num: readonly variable [root@cnsz02vl29079 ~]# export | grep num declare -irx num=”144”

    将-变成+可以取消环境变量为自定义变量

    [root@cnsz02vl29079 ~]# declare +x num [root@cnsz02vl29079 ~]# export | grep num [root@cnsz02vl29079 ~]#

说明: 1.变量类型默认为字符串,不指定类型是无法起到运算的效果 2.bash环境中数值运算,默认最多仅能达到整数形态,1/3得到的结果为0

数组变量类型
```shell
[root@cnsz02vl29079 ~]# arr[1]="lili"
[root@cnsz02vl29079 ~]# arr[2]="xixi"
[root@cnsz02vl29079 ~]# echo "${arr[1]}, ${arr[2]}"
lili, xixi

与文件系统及程序的限制关系:ulimit

Linux主机是允许多人同时登录,意味着多人都在共用这主机的内存等配置,如果不限制用户对资源的操作限制,那么极容易使操作系统挂掉。bash是可以限制用户的某些系统资源的,包括可以开启的文件数据,可使用的cpu时间,内存总量等,都是通过ulimt来设置
ulimit [-Shacdfltu] [配额]
选项与参数

-H: hard limit 严格限制,不允许超过设置值
-S: soft limit 警告限制,可以超过,超过则有警告信息
-a:后面不接任何参数,列出所有限制配额参数
-c:程序发生错误,将内存中的信息写成文件,称为内核文件(core file)
-f:此shell可以建立的最大文件容量
-d:程序可使用的最大段内存(segment)容量
-l:可用于锁定(lock)的内存量
-t:可使用最大cpu时间
-u:单一使用者可使用的最大进程(process)数量

基本使用

[root@cnsz02vl29079 ~]# ulimit -a
core file size          (blocks, -c) unlimited    # 
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 3886
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 655360
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 655360
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

文件内容删除、取代与替换

TODO

命令别名与历史命令

命令别名设置:alias&unalias

使用场景:当命令比较长时,为了简化操作命令
列出命令别名

[root@cnsz02vl29079 app]# alias
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 sd='sudo su'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

设置命令别名

  • 临时设置(alias 别名=’原命令’)

    [root@cnsz02vl29079 ~]# alias ca='cd /app/'
    [root@cnsz02vl29079 ~]# pwd
    /root
    [root@cnsz02vl29079 ~]# ca
    [root@cnsz02vl29079 app]# pwd
    /app
    
  • 永久设置

    [root@cnsz02vl29079 app]# vim ~/.bashrc
    # 写入要增加的别名命令
    [root@cnsz02vl29079 app]# source ~/.bashrc
    

取消命令别名

  • 取消临时设置(unalias)

    [root@cnsz02vl29079 app]# unalias ca
    [root@cnsz02vl29079 app]# cd /root/
    [root@cnsz02vl29079 ~]# ca
    bash: ca: command not found
    
  • 取消永久设置
    从~/.bashrc文件中删除命令配置并source下就可以了

    历史命令history

    选项与参数

    n:数值,意思是列出最近的n条命令
    -c:将目前shell中所有的history内容全部清除
    -a:将新增的history命令新增到histfiles,不加默认写入~/.bash_history
    -r:将histfiles的内容读到目前shell的history记录中
    -w:将history记录内容写入到histfiles中
    

    基本使用
    history 常用环境变量:
    HISTSIZE: 执行history命令输出的行数,默认1000行
    HISTFILESIZE: 保存history历史命令文件~/.bash_history最多保存的行数
    HISTTIMEFORMAT: 指定执行history命令时显示的时间格式
    HISTFILE: 指定存放历史命令的文件,默认是~/.bash_history

    [root@cnsz02vl29079 app]# env | grep HISTSIZE
    HISTSIZE=300
    [root@cnsz02vl29079 app]# history
    38  version
    39  name
    ...
    336  env | grep HISTSIZE
    337  history
    

    history命令返回结果操作

  • !number:执行第几条命令

  • !command:由最近命令向前查找command命令
  • !!:上一条命令,相当于【上键 + 回车】

    [root@cnsz02vl29079 app]# history 10
    332  env | grep HISTSIZE
    333  history
    334  vim ~/.bashrc
    335  vim ~/.bash_history
    336  env | grep HISTSIZE
    337  history
    338  history 10
    339  history
    340  history
    341  history 10
    [root@cnsz02vl29079 app]# !332
    env | grep HISTSIZE
    HISTSIZE=300
    [root@cnsz02vl29079 app]# !!
    env | grep HISTSIZE
    HISTSIZE=300
    [root@cnsz02vl29079 app]# !env
    env | grep HISTSIZE
    HISTSIZE=300
    

    history命令常见问题

  • 同账号同时多次登录history写入问题
    linux开好几个bash界面时,bash身份都是root,因此所有的bash都会有自己的1000条记录在内存中,到注销时才会更新记录文件,因此最后注销的那个bash才会最后写入,其他bash的命令操作其实有记录,只是会被最后一个bash覆盖。
    怎样保证history命令记录不被覆盖?
    使用单一bash登录,再用任务管理来切换不同任务,保证所有曾今执行过的命令都记录,方便未来进行命令debug

  • 无法记录时间
    history命令或命令文件中都没有记录使用命令的时间,考虑方向可以通过~/.bash_logout来进行history的记录,并增加date来增加时间参数