美文网首页我用 LinuxLinuxPHP开发
Linux深入探索04-Bash shell

Linux深入探索04-Bash shell

作者: 四月不见 | 来源:发表于2021-12-27 14:05 被阅读0次

    ----- 最近更新【2021-12-30】-----

    本文目录结构预览:

    • 一、简介
    • 二、shell 变量
      1、查看变量
      2、变量类型
      3、变量操作
      4、系统常见的全局变量
    • 三、shell 选项
      1、查看 shell 选项
      2、设置 shell 选项
    • 四、元字符
      1、元字符列表
      2、引用与转义
    • 五、shell 内置命令
      1、查看说明
      2、常用内置命令
    • 六、搜索路径
      1、查看搜索路径
      2、修改搜索路径
    • 七、历史列表
      1、查看历史列表
      2、调取历史命令
      3、调取并修改历史命令
      4、搜索历史命令
      5、设置历史列表大小
    • 八、别名 alias
      1、语法
      2、创建别名
      3、查看别名
      4、移除别名
      5、不使用别名
    • 九、初始化文件
      1、文件名称
      2、登录 shell 与 非登录 shell
      3、初始化文件内容
    • 十、参考

    一、简介

    简单地来说,shell 就是一个 Unix 程序,充当用户界面和脚本解释器,允许用户输入命令以及间接地访问内核的服务。

    从功能方面来说:
    第一,shell 是一个读取并解释所输入命令的程序。用户每输入一条 Unix 命令,shell 就读取该命令,并指出应该怎么做,所以 shell 是一个命令处理器
    第二,shell 还支持一些类型的编程语言。使用该语言可以编写由 shell 解释的程序,这些程序称为 shell 脚本

    目前比较流行的shell有以下几种 :Bash、Korn shell、C-Shell、Tcsh。

    Bash是目前最流行的 shell,本文也是以 Bash 为环境。

    如果不知道你目前使用的是哪种 shell,可以使用命令echo $SHELL来查看。

    本文内容可能会比较多,为了方面提前知道有哪些内容,这里先作一个简要的列表:

    • shell 变量
      --查看变量、变量类型、变量操作、系统常见的全局变量
    • shell 选项
      --查看 shell 选项、设置 shell 选项
    • 元字符
      --元字符列表、引用与转义
    • shell 内置命令
      --查看说明、常用内置命令
    • 搜索路径
      --查看搜索路径、修改搜索路径
    • 历史列表
      --查看历史列表、调取历史命令、调取并修改历史命令、搜索历史命令、设置历史列表大小
    • 别名 alias
      --语法、创建别名、查看别名、移除别名
    • 初始化文件
      --文件名称、登录 shell 与 非登录 shell、初始化文件内容

    二、shell 变量

    1、查看变量

    上面说的$SHELL是 shell 的一个全局变量,shell 中还有很多其它的变量。可以使用命令env或者printenv去查看全局变量。

    使用不带选项或者参数的 set 命令也可以显示所有的 shell 变量以及它们的值。

    2、变量类型

    根据储存类型,shell变量几乎总是存储一种类型的数据,即字符串。

    根据变量的作用域(Scope),shell 变量可以划分为以下三种类型:

    • 有的变量可以在当前shell进程及其子进程中使用,这叫做全局变量
    • 有的变量仅可以在当前shell进程中使用,这叫做环境变量
    • 有的变量只能在函数内部使用,这叫做局部变量(作用域仅为某代码片断(函数上下文))

    如果使用export命令将环境变量导出,那么它就在所有的子进程中也可以使用了,这时称为“全局变量”。

    很多书本或网站对环境变量与全局变量的定义都比较模糊,我这里采用一种比较好理解的方式来定义。

    3、变量操作

    变量通常有4种不同类型的操作,即创建变量、查看变量、修改变量、删除变量。

    变量的创建非常简单,使用如下语法就行:

    变量名称=变量值  # 注意!等号前后不能有空格。
    

    删除变量:

    unset 变量名 ...
    

    例:

    [14:59 linux1@noseeu ~]$ MYNAME=Nosee    #创建变量(当前shell可用)
    [14:59 linux1@noseeu ~]$ echo $MYNAME    #查看变量
    Nosee
    [14:59 linux1@noseeu ~]$ export $MYNAME     #导出变量(当前shell及子进程可用)
    [15:00 linux1@noseeu ~]$ unset MYNAME    #删除变量
    [15:00 linux1@noseeu ~]$ echo $MYNAME
    
    [15:00 linux1@noseeu ~]$ 
    

    注意:
    通过 export 导出的变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么这个全局变量也就随之消失了,其它的进程也就无法使用了,这个变量只是临时的。
    只有将变量写入 Shell 配置文件中才能达到永久的目的,Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。

    4、系统常见的全局变量

    HISTFILE # 历史列表:用来存储历史命令的文件名称
    HISTSIZE # 历史列表:用来存储历史命令的最大数目
    HOME # home目录
    HOSTNAME # 计算机名称
    LOGNAME # 当前用户标志(用户名)
    PATH # 程序搜索目录
    PS1 # shell提示
    PWD # 当前工作目录
    SHELL # shell类型
    TERM # 终端类型
    USER # 当前用户标志(用户名)
    

    三、shell 选项

    在Bash shell中,当我们需要控制 shell 行为的各个方面时,则可以使用shell 选项。

    shell 选项就像 on/off 开关一样。当打开一个选项时,就说设置了这个选项;当关闭这个选项时,就说复位了这个选项。

    shell 选项或者是 off 或者是 on ,它们不需要创建。

    1、查看 shell 选项

    可以使用命令set -o或者set +o来查看所有 shell 选项,两种查看方式只是在显示格式上不一样。

    set -o命令以一种易于阅读的方式显示(人类可读)
    set +o命令显示的输出适合用作 shell 脚本的数据(机器可读)

    选项介绍:

    选项 短名称 含义
    allexport -a 导出随后定义的所有变量和函数
    braceexpand -B 启用括号扩展(生成字符模式)
    emacs 命令行编辑器:Emacs模式,关闭vi模式
    hashall -h 查找到命令时(记住)的命令哈希位置
    hisexpand -H 历史列表:启用!风格替换
    history 历史列表:启用
    ignoreeof 忽略eof信号^D;使用exit或logout退出shell
    minitor -m 作业控制:启用
    noclobber -C 不允许重定向的输出替换某个文件
    notify -b 作业控制:当后台作业结束时立即通知
    vi vi模式:立即处理每个键入的字符

    使用命令shopt可以查看更多的 shell 选项。

    2、设置 shell 选项

    语法:

    set -o option  # 打开shell选项(开启)
    set +o option  #复位shell选项(关闭)
    

    注意,这里的-o表示开启一个选项,+o表示关闭一个选项。

    1)如,我要开启某个选项(选项noclobber表示不允许重定向的输出替换某个文件)
    则可以:

    set -o noclobber # 开启该选项
    

    或者:

    set -C
    

    关闭该选项则这样:set +o noclobberset +C

    2)忽略eof信号^D

    [21:34 linux1@noseeu ~]$ set -o ignoreeof
    # 此时我再按 <Crlt>-D 组合键
    [21:34 linux1@noseeu ~]$ Use "logout" to leave the shell.
    

    设计 shell 的程序员们知道人们会如何使用 shell,因此在大多数情况下,默认的 shell 选项就可以满足要求。这意味着我们极少需要云修改 shell 选项。

    四、元字符

    在shell中,有许多字符拥有它特殊的含义,我们称这样的字符为元字符。如;(分号)、\(反斜杠)、.(点号),抽象一点的如按键<Space><Tab><Enter>也是使用了元字符。

    1、元字符列表

    字符 名称 作用
    | 管道 命令行:创建一个管道线
    < 小于 命令行:重定向输入
    > 大于 命令行:重定向输出
    () 圆括号 命令行:在子shell中运行命令
    # hash、pound 命令行:注释
    ; 分号 命令行:用于分隔多条命令
    ` 反引号 命令行:命令替换
    ~ 波浪号 文件名扩展:插入home目录的名称
    ? 问号 文件名扩展:匹配任意一个字符
    [] 方括号 文件名扩展:与一组字符中的字符匹配
    * 星号 文件名扩展:匹配0个或多个字符
    ! 叹号、bang 历史列表:事件标记
    & 和号 作业控制:在后台运行命令
    \ 反斜杠 引用:下一个字符转义
    ' 单引号 引用:取消所有的替换
    " 双引号 引用:取消大部分替换
    {} 花括号 变量:确定变量名称的界限
    $ 美元符号 变量:用变量的值替换
    <Space> 空格符 空白符:在命令行中分隔单词
    <Tab> 制表符 空白符:在命令行中分隔单词
    <Enter>/<Return> 新行字符 空白符:标记一行结束

    以上元字符列表展示的就是元字符在shell中的常用作用。

    2、引用与转义

    有时候,我们希望按字面上的含义使用元字符(而不是使用其特殊含义),这时我们必须告诉shell按字面意思解释字符。这样做时,可以称其为引用字符。
    字符的引用有3种方法:使用反斜杠、一对单引号或者一对双引号。

    当使用反斜杠引用单个字符时,我们称反斜杠为“转义字符”。

    如:

    echo It is warm\; come on.
    

    上面例子中,;(分号)在shell中有特殊的含义,如果我们想原样输出则必须转义。这时可以说“使用反斜杠转义了分号”,或者可以说“使用反斜杠引用了分号”。

    当元字符少时,上面的方法没有什么问题。但是当元字符多的时候,我们就需要用到单引号或者双引号了。

    1)强引用(单引号)
    单引号里面的所有元字符都会被转义,也就是所有元字符都会原样输出。

    如,下面的$会被转义,$NAME会原样输出:

    linux1@noseeu:~$ echo 'My name is $NAME'
    My name is $NAME
    

    2)弱引用(双引号)
    双引号引用时,会保留$(美元符号)、`(反引号)、\(反斜杠)的特殊含义。

    如:

    linux1@noseeu:~$ echo "My name is $NAME"
    My name is Chan
    [22:04 linux1@noseeu ~]$ echo "now is `date`."
    now is Sun 26 Dec 2021 10:04:24 PM UTC.
    

    上面的$不会被转义,$NAME会被替换为NAME的实际值;`也不会被转义,date会被当作嵌入命令优先执行。

    注:反斜杠是所有引用中最强的,所以它甚至可以引用新行字符(<Enter>)。
    如,

    linux1@noseeu:~$ echo hi \
    > nosee.
    hi nosee.
    linux1@noseeu:~$ date;\
    > cal
    Sun 26 Dec 2021 05:28:01 PM UTC
       December 2021      
    Su Mo Tu We Th Fr Sa  
              1  2  3  4  
     5  6  7  8  9 10 11  
    12 13 14 15 16 17 18  
    19 20 21 22 23 24 25  
    26 27 28 29 30 31 
    

    在这里新行字符(<Enter>)失去了它的特殊含义,所以这时它并不是一行结束的信号了,意味着后面输入的内容都是接着上一行的。

    五、shell 内置命令

    当在shell中输入命令时,shell会将命令进行解析,然后决定如何处理命令,其中有两种可能。一些命令在shell的内部,这意味着shell可以直接解释它们,这些命令是内部命令,称为内置命令。其他所有命令都是外部命令,即必须独自运行的独立程序。

    当输入内置命令时,shell在自己的进程内运行该命令(不创建新的进程)。当输入外部命令时,shell将搜索合适的程序然后以一个单独的进程运行该命令(创建一个子进程)。

    查看某一条命令是不是内置命令的快捷方法是使用type。如:

    linux1@noseeu:~$ type time set date type
    time is a shell keyword
    set is a shell builtin
    date is hashed (/usr/bin/date)
    type is a shell builtin
    

    可以看出,time、set、type都是shell的内置命令,而date是外部命令。

    1、查看说明

    几乎所有的Unix程序在发行的时候都提供有说明明书页,即可以用man或者info去查看它们的说明。但内置命令不是一个单独的程序,它们是shell的一部分,每种shell都会提供大量的命令,所以每个内置命令都开发一个单独的说明书页是不现实的。

    其实,所有的内置命令都记录在shell的说明书页中,即你可以使用man bash去查看。但是shell的说明书页都非常长,可能需要使用搜索才能找到所需的内容(可以使用apropos命令或者man -k命令进行搜索)。

    还有一个方法,就是使用help命令也可以查看内置命令的说明,如:help unset

    linux1@noseeu:~$ help unset
    unset: unset [-f] [-v] [-n] [name ...]
        Unset values and attributes of shell variables and functions.
        ...
        ...
        Exit Status:
        Returns success unless an invalid option is given or a NAME is read-only.
    

    使用不带参数的help命令,可以显示一个所有内置命令的摘要列表。如下图:

    使用help -s命令可以查看某个内置命令的语法:

    linux1@noseeu:~$ help -s set
    set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
    

    2、常用内置命令

    alias # 为指定命令定义一个别名
    echo # 将指定字符串输出到STDOUT
    exit # 强制 shell 以指定的退出状态码退出
    export  # 将变量导出到全局变量
    fc  # 从历史记录中选择命令列表
    help # 显示帮助说明
    history # 显示命令历史记录
    kill # 向指定的进程 ID(PID) 发送一个系统信号
    pwd  # 显示当前工作目录的路径名
    set # 设置并显示环境变量的值和 shell 属性
    shopt # 打开/关闭控制 shell 可选行为的变量值
    type # 显示指定的单词如果作为命令将会如何被解释
    unset # 刪除指定的环境变量或 shell 属性
    

    六、搜索路径

    大部分命令都不是 shell 内置的,那么 shell 必须查找出合适的程序来执行。那么 shell 在哪里查找外部命令?

    shell 通过查找变量 PATH 来获得一系列目录名称,然后在这些目录下寻找对应的程序,这些目录名称就是我们所说的搜索路径

    1、查看搜索路径

    搜索路径是包含所有外部命令的程序的目录列表,使用命令echo $PATH可以查看 搜索路径。

    [21:31 linux1@noseeu ~]$ echo $PATH
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
    

    搜索顺序:
    当 shell 在查找外部程序时,它在搜索路径中会按指定顺序逐个检查目录,进到找到期望的外部命令时,它就停止搜索并执行程序。

    2、修改搜索路径

    修改搜索路径的基本思想就是将修改PATH变量的命令放到登录时自动执行的初始化文件中。

    PATH的值就是一个包含若干目录名称的字符串,各个目录名称用:(冒号)隔开。

    如我有一些自定义的程序放在 ~/bin 目录,那么我可以这样设置:

    export PATH="$PATH:$HOME/bin"
    

    一般修改 PATH 的值我们都是在原有的路径上再加上我们想要添加的路径。

    七、历史列表

    在输入命令时,shell 会将命令保存到所谓的历史列表中。然后我们可以采用不同方式访问历史列表,调取前面的命令,然后再对命令进行修改,并重新输入命令。

    1、查看历史列表

    最简单的查看历史列表的办法就是使用<Up><>Down键,但是这个方法每次只能查看一条命令。

    还有一个更强大的命令,可以查看全部或者部分历史命令,就是使用history或者fc命令。

    在历史列表中,每条命令称为一个事件,而每个事件都有一个内部编号,称为事件编号。历史列表的主要功能就是它可以基于事件编号调取命令。

    查看历史列表:

    [23:12 linux1@noseeu ~]$ fc -l
    ...
    931      echo "'now is `date`."
    932      help fc
    933      fc-l
    [23:12 linux1@noseeu ~]$ history
        1  sudo -i
        2  exit
    ...
    934  fc -l
    935  history
    

    每条命令前面的数字就是事件编号。

    2、调取历史命令

    如我们想再次运行932号命令,可以使用fc -s 932或者!932

    [23:14 linux1@noseeu ~]$ fc -s 932
    help fc
    fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
        Display or execute commands from the history list.
        ...
        Exit Status:
        Returns success or status of executed command; non-zero if an error occurs.
    [23:22 linux1@noseeu ~]$ !932
    help fc
    fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
        Display or execute commands from the history list.
        ...
        Exit Status:
        Returns success or status of executed command; non-zero if an error occurs.
    

    如果只是希望输入最近上一条命令,则可以不带事件编号:fc -s或者!!

    3、调取并修改历史命令

    shell 允许在重新执行历史命令之前对命令进行小的修改,语法如下:

    fc -s pattern=replacement number
    !number:s/pattern/replacement
    

    例:

    937      vim sh_test1 
    [23:34 linux1@noseeu ~]$ fc -s sh_=Sh 937
    vim Shtest1 
    [23:35 linux1@noseeu ~]$ !937:s/sh_/Sh
    vim Shtest1 
    

    如果只是想修改上一条输错的命令,还可以这样:

    [23:39 linux1@noseeu ~]$ datw
    Command 'datw' not found.
    [23:39 linux1@noseeu ~]$ ^w^e
    date
    Sun 26 Dec 2021 11:40:12 PM UTC
    [23:40 linux1@noseeu ~]$ 
    

    记住,这个方法仅适用于对上一条历史命令修改。

    4、搜索历史命令

    bash 还提供了一个非常方便的历史命令搜索方式,即使用<Ctrl>-R(^R)。
    如我需要调取一条设置PS1变量的命令,查历史列表又觉得麻烦。这时我就可以按下<Ctrl>-R键,然后输入关键词PS:

    linux1@noseeu:~$ 
    (reverse-i-search)`PS': PS1="\[\033[0;32m\][\A \u\[\033[0;33m\]@\H \w]$ \[\033[0m\]"
    

    如果显示出的命令是你想要的,直接按回车就可以执行了:

    linux1@noseeu:~$ PS1="\[\033[0;32m\][\A \u\[\033[0;33m\]@\H \w]$ \[\033[0m\]"
    [23:54 linux1@noseeu ~]$ 
    

    如果看到的不是你想要的命令,可以继续按<Ctrl>-R搜索下一个。

    5、设置历史列表大小

    Bash shell 将历史列表存储在一个文件中,所以下次登录时还是可以继续使用。为了避免历史列表太多,shell 允许通过设置一个变量来设置历史列表的大小。

    查看 shell 默认设置:

    linux1@noseeu:~$ set | grep HIST
    HISTCONTROL=ignoreboth
    HISTFILE=/home/linux1/.bash_history
    HISTFILESIZE=2000
    HISTSIZE=1000
    

    如我们要修改history命令所展示的历史列表条数:

    linux1@noseeu:~$ export HISTSIZE=10
    

    八、别名 alias

    别名就是赋予一条命令或者一列命令的名称。可以将别名作为缩写,或者使用别名创建已有命令的自定义变体。

    1、语法

    Define or display aliases.

    alias: alias [-p] [name[=value] ... ]
    

    2、创建别名

    我们经常会使用ls -al来查看文件列表,为了方便可以如下定义别名:

    alias ll='ls -al'
    

    当一条命令中包含有空格或元字符时,记得要使用引号包围。

    linux1@noseeu:~$ alias mytime='date; cal'
    linux1@noseeu:~$ mytime
    Mon 27 Dec 2021 01:01:56 AM UTC
       December 2021      
    Su Mo Tu We Th Fr Sa  
              1  2  3  4  
     5  6  7  8  9 10 11  
    12 13 14 15 16 17 18  
    19 20 21 22 23 24 25  
    26 27 28 29 30 31  
    

    3、查看别名

    当需要查看某个别名的含义时,可以这样:

    linux1@noseeu:~$ alias mytime
    alias mytime='date; cal'
    

    或者使用type命令:

    linux1@noseeu:~$ type mytime
    mytime is aliased to `date; cal'
    

    查看所有别名:

    linux1@noseeu:~$ alias
    alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
    alias egrep='egrep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias grep='grep --color=auto'
    alias l='ls -CF'
    alias la='ls -A'
    alias ll='ls -al'
    alias ls='ls --color=auto'
    alias mytime='date; cal'
    

    4、移除别名

    语法:Remove each NAME from the list of defined aliases.

    unalias [-a] name [name ...]
    

    例:

    linux1@noseeu:~$ unalias mytime 
    linux1@noseeu:~$ mytime
    mytime: command not found
    

    5、不使用别名

    ls命令,很多 linux 会默认就在配置文件中设置了ls --color=auto,这时你就会在不知情的情况下使用了别名。如果你不想使用使用别名,可以在命令前面键入一个\,告诉 shell 不使用任何别名,如\ls

    九、初始化文件

    以上所说的所有设置都是只有当前shell生效,注销然后再登录之后那些设置都不存在了。如果你想在你下次登录时还可以继续使用你的设置,则需要把你的设置添加到初始化文件中。

    Bash shell 的初始化文件包括两个,即登录文件环境文件

    初始化文件存放着所有希望在每次登录时自动执行的命令,而环境文件存放着所有希望在新 shell 启动时自动执行的程序。

    为了提供更多的功能,Bash shell 还提供了注销文件,用于存放注销登录时自动运行的命令。

    1、文件名称

    初始化文件的名称在不同的系统中可能会稍有不同,但基本都是以下这种:

    执行环境 文件名称
    登录文件 .bash_profile、.profile、.bash_login
    环境文件 .bashrc
    注销文件 .bash_logout

    文件存放的目录都为用户目录,即:/home/用户名

    注:
    系统启动的时候会先执行.profile然后执行.bashrc,所以如果这两个文件存在相同的配置的话,前面的会被后面的覆盖。

    2、登录 shell 与 非登录 shell

    1)登录 shell
    在任何时候,如通过ssh连接登录到远程主机,或者使用<Ctrl-Alt-F1>登录一个 shell,等。这类型需要用户进行登录认证才能进入的shell,我们称之为登录 shell

    登录 shell 的初始化会执行登录文件(.profile)与环境文件(.bashrc)。

    2)非登录 shell
    当打开一个 shell 窗口不需要登录认证时,如在一个已登录的 shell 窗口输入 bash 打开一个新 shell,或者在桌面环境简单地打开一个终端窗口,等,我们称这类 shell 为 非登录 shell

    非登录 shell 只执行环境文件。

    3、初始化文件内容

    shell 的初始化文件一般包含下述内容:
    1)创建或者修改环境变量的命令
    2)执行所有一次性操作的命令
    3)合理的注释
    4)等

    如:

    # 设置环境变量
    HISTSIZE=50
    HISTFILESIZE=1000
    
    # 设置文件创建掩码,控制新创建文件的默认权限
    umask 077
    
    # 设置别名
    alias ll='ls -alF'
    alias la='ls -A'
    alias l='ls -CF'
    
    # 设置默认的分页程序(需要调用分页程序显示数据时就会调用该程序,如 man 命令)
    export PAGER=less
    # 设置分页程序的默认选项,相当于:alias less='less -CMs'
    export LESS='-CMs'
    
    # 查看命名为`.bashrc`的文件是否存在,如果存在,则运行这个文件
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
    

    十、参考

    书箱:《Unix & Linux 大学教程》第11-14章 (美)Harley Hahn 著 张杰良 译
    博客: 命令行界面 (CLI)、终端 (Terminal)、Shell、TTY的区别
    SegmentFault: Linux TTY/PTS概述

    相关文章

      网友评论

        本文标题:Linux深入探索04-Bash shell

        本文链接:https://www.haomeiwen.com/subject/kfdjqrtx.html