走进 Linux shell (二)

走进 Linux shell (一)

5. 理解 shell
 

5.1 shell 的类型

系统启动什么样的shell程序取决于个人的用户 ID 配置。在 /etc/passwd 文件中,在用户 ID 记录的第 7 个字段中列出了默认的 shell 程序。只要用户
登录到某个虚拟控制台终端或是在 GUI 中启动终端仿真器,默认的 shell 程序就会开始运行。

    [devalone@devalone shell-script]$ cat /etc/passwd | grep devalone
    devalone:x:1000:1000:MichaelY.:/home/devalone:/bin/bash
    
bash shell 程序位于 /bin 目录内。从长列表中可以看出 /bin/bash(bash shell)是一个可执行程序:    

    [devalone@devalone shell-script]$ ls -lF /bin/bash
    -rwxr-xr-x. 1 root root 1071984 9月  30 2016 /bin/bash*

默认的交互shell会在用户登录某个虚拟控制台终端或在GUI中运行终端仿真器时启动。不过还有另外一个默认 shell 是 /bin/sh,它作为默认的系统 shell,
用于那些需要在启动时使用的系统 shell脚本。

系统中可用的 shell 列于 /etc/shells 文件中,可以查看该文件了解可用 shell:

    [devalone@devalone shell-script]$ cat /etc/shells
    /bin/sh
    /bin/bash
    /sbin/nologin
    /usr/bin/sh
    /usr/bin/bash
    /usr/sbin/nologin


5.2 shell 的父子关系
-----------------------------------------------------------------------------------------------------------------------------------------
用于登录某个虚拟控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一个父 shell。

在CLI提示符后输入 /bin/bash 命令或其他等效的 bash 命令时,会创建一个新的 shell 进程。这个 shell 进程被称为子 shell(child shell)。子shell
也拥有 CLI 提示符,同样会等待命令输入。

当输入bash、生成子shell的时候,看不到任何相关的信息。使用 ps 命令配合 -f 选项查看:

    [devalone@devalone shell-script]$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    devalone  2373  2360  0 10:17 pts/1    00:00:00 -bash
    devalone  3839  2373  0 15:53 pts/1    00:00:00 ps -f

    [devalone@devalone shell-script]$ bash

    [devalone@devalone shell-script]$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    devalone  2373  2360  0 10:17 pts/1    00:00:00 -bash
    devalone  3840  2373  1 15:54 pts/1    00:00:00 bash
    devalone  3872  3840  0 15:54 pts/1    00:00:00 ps -f

第一次使用ps -f的时候,显示出了两个进程。
输入命令bash之后,一个子shell 就出现了。第二个 ps -f 是在子shell 中执行的。可以从显示结果中看到有两个 bash shell 程序在运行。第一个
bash shell 程序,也就是父shell进程,其原始进程ID是 2373。第二个 bash shell程序,即子shell进程,其PID是 3840。注意,子shell 的父进程 ID
(PPID)是 2373,指明了这个父shell 进程就是该子shell 的父进程。

可以利用 exit 命令退出子shell。


5.2.1 进程列表
-----------------------------------------------------------------------------------------------------------------------------------------
可以在一行中指定要依次运行的一系列命令。这可以通过命令列表来实现,只需要在命令之间加入分号(;) 即可。

    [devalone@devalone ~]$ pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls

在上面的例子中,所有的命令依次执行,不存在任何问题。不过这并不是进程列表。命令列表要想成为进程列表,这些命令必须包含在括号里。

    [devalone@devalone ~]$ (pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls)

尽管多出来的括号看起来没有什么太大的不同,但起到的效果确是非同寻常。括号的加入使命令列表变成了进程列表,生成了一个子shell来执行对应的命令。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    进程列表是一种命令分组 (command grouping)。另一种命令分组是将命令放入花括号中,并在命令列表尾部加上分号 (;)。语法为 { command; }。使用
    花括号进行命令分组并不会像进程列表那样创建出子 shell。

要想知道是否生成了子 shell,得借助一个使用了环境变量的命令。这个命令就是 echo $BASH_SUBSHELL。如果该命令返回 0,就表明没有子 shell。如果返
回 1 或者其他更大的数字,就表明存在子 shell。

    [devalone@devalone ~]$ pwd ; cd /etc ; pwd ; cd ; pwd ; echo $BASH_SUBSHELL
    /home/devalone
    /etc
    /home/devalone
    0

    [devalone@devalone ~]$ (pwd ; cd /etc ; pwd ; cd ; pwd ; echo $BASH_SUBSHELL)
    /home/devalone
    /etc
    /home/devalone
    1


甚至可以在命令列表中嵌套括号来创建子 shell 的子 shell。

    [devalone@devalone ~]$ (pwd;echo $BASH_SUBSHELL)
    /home/devalone
    1

    [devalone@devalone ~]$ (pwd;(echo $BASH_SUBSHELL))
    /home/devalone
    2

在 shell 脚本中,经常使用子 shell 进行多进程处理。但是采用子 shell 的成本不菲,会明显拖慢处理速度。在交互式的 CLI shell 会话中,子 shell
同样存在问题。它并非真正的多进程处理,因为终端控制着子 shell 的 I/O。


5.2.2 shell 的其它用法
-----------------------------------------------------------------------------------------------------------------------------------------
在交互式的 shell CLI中,还有很多更富有成效的子 shell用法。进程列表、协程和管道都利用了子 shell。它们都可以有效地在交互式 shell 中使用。


    ● 后台模式
    -------------------------------------------------------------------------------------------------------------------------------------
    在后台模式中运行命令可以在处理命令的同时让出CLI,以供他用。
    
    要想将命令置入后台模式,可以在命令末尾加上字符&

        [devalone@devalone ~]$ sleep 3000&
        [1] 1829

        [devalone@devalone ~]$ ps -f
        UID        PID  PPID  C STIME TTY          TIME CMD
        devalone  1742  1741  0 17:35 pts/0    00:00:00 -bash
        devalone  1829  1742  0 17:57 pts/0    00:00:00 sleep 3000
        devalone  1830  1742  0 17:58 pts/0    00:00:00 ps -f

    sleep 命令会在后台(&) 睡眠3000秒 (50分钟)。当它被置入后台,在 shell CLI 提示符返回之前,会出现两条信息。第一条信息是显示在方括号中的
    后台作业(background job)号 (1)。第二条是后台作业的进程ID(1829)。

    也可以使用 jobs 命令来显示后台作业信息。jobs 命令可以显示出当前运行在后台模式中的所有用户的进程(作业)。
        
        [devalone@devalone ~]$ jobs
        [1]+  运行中               sleep 3000 &

    jobs命令在方括号中显示出作业号(1)。它还显示了作业的当前状态(running) 以及对应的命令 (sleep 3000 &)。

    利用 jobs 命令的 -l(字母 L 的小写形式)选项,还能够看到更多的相关信息。除了默认信息之外,-l 选项还能够显示出命令的 PID。
    
    [devalone@devalone ~]$ jobs -l
    [1]+  1829 运行中               sleep 3000 &

    一旦后台作业完成,就会显示出结束状态:
    [1]+ Done sleep 3000 &
    
    
    ● 将进程列表置入后台
    -------------------------------------------------------------------------------------------------------------------------------------
    进程列表是运行在子 shell 中的一条或多条命令。使用包含了sleep 命令的进程列表,并显示出变量 BASH_SUBSHELL,结果和期望的一样。

        [devalone@devalone ~]$ (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)
        1
    
    将相同的进程列表置入后台模式会在命令输出上表现出些许不同:
        [devalone@devalone ~]$ (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2) &
        [2] 1861
        [devalone@devalone ~]$ 1

        [2]+  已完成               ( sleep 2; echo $BASH_SUBSHELL; sleep 2 )

    把进程列表置入后台会产生一个作业号和进程 ID,然后返回到提示符。不过奇怪的是表明单一级子 shell 的数字 1 显示在了提示符的旁边!只需要按
    一下回车键,就会得到另一个提示符。

    在 CLI 中运用子 shell 的创造性方法之一就是将进程列表置入后台模式。既可以在子 shell 中进行繁重的处理工作,同时也不会让子 shell 的I/O
    受制于终端。
    
    
    ● 协程(coproc)
    -------------------------------------------------------------------------------------------------------------------------------------
    协程可以同时做两件事。它在后台生成一个子shell,并在这个子 shell中执行命令。

    要进行协程处理,得使用 coproc 命令,还有要在子 shell 中执行的命令。
    
    [devalone@devalone ~]$ coproc sleep 10
    [2] 1870

    除了会创建子 shell 之外,协程基本上就是将命令置入后台模式。当输入 coproc 命令及其参数之后,会发现启用了一个后台作业。屏幕上会显示出
    后台作业号(1)以及进程ID(1870)
    
    jobs命令能够显示出协程的处理状态。
    
    [devalone@devalone ~]$ jobs
    [1]-  运行中               sleep 3000 &
    [2]+  运行中               coproc COPROC sleep 10 &

    
    上面的例子中可以看到在子 shell 中执行的后台命令是 coproc COPROC sleep 10。COPROC 是 coproc 命令给进程起的名字。可以使用命令的扩展语法
    自己设置这个名字:
    
    [devalone@devalone ~]$ coproc My_Job { sleep 10; }
    
    通过使用扩展语法,协程的名字被设置成 My_Job。这里要注意的是,扩展语法写起来有点麻烦。必须确保在第一个花括号 ({) 和命令名之间有一个空格。
    还必须保证命令以分号(;)结尾。另外,分号和闭花括号(})之间也得有一个空格。
    
    将协程与进程列表结合起来产生嵌套的子 shell。只需要输入进程列表,然后把命令 coproc 放在前面就行了。
    
    [devalone@devalone ~]$ coproc ( sleep 10; sleep 2 )
    [2] 1883
    
    
5.3 理解shell 的内建命令
-----------------------------------------------------------------------------------------------------------------------------------------
内建命令和非内建命令的操作方式大不相同。


5.3.1 外部命令
-----------------------------------------------------------------------------------------------------------------------------------------
外部命令,有时候也被称为文件系统命令,是存在于 bash shell之外的程序。它们并不是 shell 程序的一部分。外部命令程序通常位于/bin、/usr/bin、
/sbin 或 /usr/sbin中。


ps 就是一个外部命令。可以使用 which 和 type 命令找到它。

    [devalone@devalone ~]$ which ps
    /usr/bin/ps

    [devalone@devalone ~]$ type -a ps
    ps 是 /usr/bin/ps


当外部命令执行时,会创建出一个子进程。这种操作被称为衍生(forking)。外部命令 ps 很方便显示出它的父进程以及自己所对应的衍生子进程。

    [devalone@devalone ~]$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    devalone  1742  1741  0 17:35 pts/0    00:00:00 -bash
    devalone  1829  1742  0 17:57 pts/0    00:00:00 sleep 3000
    devalone  1908  1742  0 18:32 pts/0    00:00:00 ps -f

作为外部命令,ps 命令执行时会创建出一个子进程。在这里,ps命令的 PID是 1908,父 PID 是 1742。作为父进程的 bash shell 的 PID 是 1742。

当进程必须执行衍生操作时,它需要花费时间和精力来设置新子进程的环境。所以说,外部命令多少还是有代价的。


5.3.2 内建命令
-----------------------------------------------------------------------------------------------------------------------------------------
内建命令和外部命令的区别在于前者不需要使用子进程来执行。它们已经和 shell 编译成了一体,作为 shell 工具的组成部分存在。不需要借助外部程序
文件来运行。

cd 和 exit 命令都内建于 bash shell。可以利用 type 命令来了解某个命令是否是内建的。

    [devalone@devalone ~]$ type cd
    cd 是 shell 内建

    [devalone@devalone ~]$ type exit
    exit 是 shell 内建

因为既不需要通过衍生出子进程来执行,也不需要打开程序文件,内建命令的执行速度要更快,效率也更高。

要注意,有些命令有多种实现。例如 echo 和 pwd 既有内建命令也有外部命令。两种实现略有不同。要查看命令的不同实现,使用 type 命令的-a选项。

    [devalone@devalone ~]$ type -a echo
    echo 是 shell 内建
    echo 是 /usr/bin/echo

    [devalone@devalone ~]$ which echo
    /usr/bin/echo

    [devalone@devalone ~]$ type -a pwd
    pwd 是 shell 内建
    pwd 是 /usr/bin/pwd

    [devalone@devalone ~]$ which pwd
    /usr/bin/pwd

命令 type -a 显示出了每个命令的两种实现。注意,which 命令只显示出了外部命令文件。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    对于有多种实现的命令,如果想要使用其外部命令实现,直接指明对应的文件就可以了。例如,要使用外部命令 pwd,可以输入 /usr/bin/pwd。
    
    
    ● history 命令
    -------------------------------------------------------------------------------------------------------------------------------------
    一个有用的内建命令是 history 命令。bash shell 会跟踪用过的命令。用户可以唤回这些命令并重新使用。
    
    [devalone@devalone ~]$ history
     1020  (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2) &
     1021  coproc sleep 10
     1022  jobs
     1023  coproc sleep 10
     1024  jobs
     1025  coproc My_Job { sleep 10; }
     1026  jobs
     1027  coproc ( sleep 10; sleep 2 )
     1028  jobs
     1029  ps --forest
    ...
    
通常历史记录中会保存最近的 1000 条命令。

可以唤回并重用历史列表中最近的命令。这样能够节省时间和击键量。输入!!,然后按回车键就能够唤出刚刚用过的那条命令来使用。

    [devalone@devalone ~]$ ps --forest
      PID TTY          TIME CMD
     1742 pts/0    00:00:00 bash
     1829 pts/0    00:00:00  \_ sleep
     1934 pts/0    00:00:00  \_ ps
    [devalone@devalone ~]$ !!
    ps --forest
      PID TTY          TIME CMD
     1742 pts/0    00:00:00 bash
     1829 pts/0    00:00:00  \_ sleep
     1935 pts/0    00:00:00  \_ ps

命令历史记录被保存在隐藏文件 .bash_history 中,它位于用户的主目录中。这里要注意的是,bash命令的历史记录是先存放在内存中,当 shell 退出时
才被写入到历史文件中。

    [devalone@devalone ~]$ cat .bash_history
    grep [du] testfile
    ll
    gzip testfile
    ll
    rm ln_test
    ll
    ll testfile*
    gunzip testfile.gz
    ll
    ...

可以在退出 shell 会话之前强制将命令历史记录写入 .bash_history 文件。要实现强制写入,需要使用 history 命令的 -a 选项。

    [devalone@devalone ~]$ history -a


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    要强制重新读取 .bash_history 文件,更新终端会话的历史记录,可以使用 history -n 命令。
    
    
可以唤回历史列表中任意一条命令。只需输入惊叹号和命令在历史列表中的编号即可。

    [devalone@devalone ~]$ history
    ...    
     1040  history
     1041  history  | less
     1042  ps --forest
     1043  cat .bash_history
     1044  man history
     1045  history -a
     1046  history

    [devalone@devalone ~]$ !1042
    ps --forest
      PID TTY          TIME CMD
     1742 pts/0    00:00:00 bash
     1973 pts/0    00:00:00  \_ ps

 
    ● 命令别名
    -------------------------------------------------------------------------------------------------------------------------------------
    alias 命令是另一个 shell 的内建命令。命令别名允许为常用的命令(及其参数)创建另一个名称,从而将输入量减少到最低。
    
    要查看当前可用的别名,使用 alias 命令以及选项 -p。
    
    [devalone@devalone ~]$ alias -p
    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 vi='vim'
    
注意 ll 是 'ls -l --color=auto' 命令的别名。

可以使用 alias 命令创建属于自己的别名:

    [devalone@devalone ~]$ alias lii='ls -li'

    [devalone@devalone shell_test]$ lii
    总用量 16
    3021080 drwxrwxr-x. 2 devalone devalone 4096 7月   3 10:46 new_dir
    2892886 -rw-rw-r--. 1 devalone devalone   23 7月   3 14:29 sortfile
    2892883 -rwxrwxr-x. 1 devalone devalone  152 7月   3 10:51 test1.sh
    2892882 -rw-rw-r--. 1 devalone devalone  277 7月   3 11:26 testfile

在定义好别名之后,随时都可以在 shell 中使用它,就算在 shell 脚本中也没问题。要注意,因为命令别名属于内部命令,一个别名仅在它所被定义的
shell 进程中才有效。


5.3.3 bash shell 命令汇总
-----------------------------------------------------------------------------------------------------------------------------------------


    bash 内建命令
    +-----------+------------------------------------------------------------------------------------------
    | 命 令        | 描 述
    +-----------+------------------------------------------------------------------------------------------
    | :            | 扩展参数列表,执行重定向操作
    +-----------+------------------------------------------------------------------------------------------
    | .            | 读取并执行指定文件中的命令(在当前shell环境中)
    +-----------+------------------------------------------------------------------------------------------
    | alias        | 为指定命令定义一个别名
    +-----------+------------------------------------------------------------------------------------------
    | bg        | 将作业以后台模式运行
    +-----------+------------------------------------------------------------------------------------------
    | bind        | 将键盘序列绑定到一个readline函数或宏
    +-----------+------------------------------------------------------------------------------------------
    | break        | 退出for、while、select或until循环
    +-----------+------------------------------------------------------------------------------------------
    | builtin    | 执行指定的shell内建命令
    +-----------+------------------------------------------------------------------------------------------
    | caller    | 返回活动子函数调用的上下文
    +-----------+------------------------------------------------------------------------------------------
    | cd        | 将当前目录切换为指定的目录
    +-----------+------------------------------------------------------------------------------------------
    | command    | 执行指定的命令,无需进行通常的shell查找
    +-----------+------------------------------------------------------------------------------------------
    | compgen    | 为指定单词生成可能的补全匹配
    +-----------+------------------------------------------------------------------------------------------
    | complete    | 显示指定的单词是如何补全的
    +-----------+------------------------------------------------------------------------------------------
    | compopt    | 修改指定单词的补全选项
    +-----------+------------------------------------------------------------------------------------------
    | continue    | 继续执行for、while、select或until循环的下一次迭代
    +-----------+------------------------------------------------------------------------------------------
    | declare    | 声明一个变量或变量类型。
    +-----------+------------------------------------------------------------------------------------------
    | dirs        | 显示当前存储目录的列表
    +-----------+------------------------------------------------------------------------------------------
    | disown    | 从进程作业表中删除指定的作业
    +-----------+------------------------------------------------------------------------------------------
    | echo        | 将指定字符串输出到STDOUT
    +-----------+------------------------------------------------------------------------------------------
    | enable    | 启用或禁用指定的内建shell命令
    +-----------+------------------------------------------------------------------------------------------
    | eval        | 将指定的参数拼接成一个命令,然后执行该命令
    +-----------+------------------------------------------------------------------------------------------
    | exec        | 用指定命令替换shell进程
    +-----------+------------------------------------------------------------------------------------------
    | exit        | 强制shell以指定的退出状态码退出
    +-----------+------------------------------------------------------------------------------------------
    | export    | 设置子shell进程可用的变量
    +-----------+------------------------------------------------------------------------------------------
    | fc        | 从历史记录中选择命令列表
    +-----------+------------------------------------------------------------------------------------------
    | fg        | 将作业以前台模式运行
    +-----------+------------------------------------------------------------------------------------------
    | getopts    | 分析指定的位置参数
    +-----------+------------------------------------------------------------------------------------------
    | hash        | 查找并记住指定命令的全路径名
    +-----------+------------------------------------------------------------------------------------------
    | help        | 显示帮助文件
    +-----------+------------------------------------------------------------------------------------------
    | history    | 显示命令历史记录
    +-----------+------------------------------------------------------------------------------------------
    | jobs        | 列出活动作业
    +-----------+------------------------------------------------------------------------------------------
    | kill        | 向指定的进程ID(PID)发送一个系统信号
    +-----------+------------------------------------------------------------------------------------------
    | let        | 计算一个数学表达式中的每个参数
    +-----------+------------------------------------------------------------------------------------------
    | local        | 在函数中创建一个作用域受限的变量
    +-----------+------------------------------------------------------------------------------------------
    | logout    | 退出登录shell
    +-----------+------------------------------------------------------------------------------------------
    | mapfile    | 从STDIN读取数据行,并将其加入索引数组
    +-----------+------------------------------------------------------------------------------------------
    | popd        | 从目录栈中删除记录
    +-----------+------------------------------------------------------------------------------------------
    | printf    | 使用格式化字符串显示文本
    +-----------+------------------------------------------------------------------------------------------
    | pushd        | 向目录栈添加一个目录
    +-----------+------------------------------------------------------------------------------------------
    | pwd        | 显示当前工作目录的路径名
    +-----------+------------------------------------------------------------------------------------------
    | read        | 从STDIN读取一行数据并将其赋给一个变量
    +-----------+------------------------------------------------------------------------------------------
    | readarray    | 从STDIN读取数据行并将其放入索引数组
    +-----------+------------------------------------------------------------------------------------------
    | readonly    | 从STDIN读取一行数据并将其赋给一个不可修改的变量
    +-----------+------------------------------------------------------------------------------------------
    | return    | 强制函数以某个值退出,这个值可以被调用脚本提取
    +-----------+------------------------------------------------------------------------------------------
    | set        | 设置并显示环境变量的值和shell属性
    +-----------+------------------------------------------------------------------------------------------
    | shift        | 将位置参数依次向下降一个位置
    +-----------+------------------------------------------------------------------------------------------
    | shopt        | 打开/关闭控制shell可选行为的变量值
    +-----------+------------------------------------------------------------------------------------------
    | source    | 读取并执行指定文件中的命令(在当前shell环境中)
    +-----------+------------------------------------------------------------------------------------------
    | suspend    | 暂停shell的执行,直到收到一个SIGCONT信号
    +-----------+------------------------------------------------------------------------------------------
    | test        | 基于指定条件返回退出状态码0或1
    +-----------+------------------------------------------------------------------------------------------
    | times        | 显示累计的用户和系统时间
    +-----------+------------------------------------------------------------------------------------------
    | trap        | 如果收到了指定的系统信号,执行指定的命令
    +-----------+------------------------------------------------------------------------------------------
    | type        | 显示指定的单词如果作为命令将会如何被解释
    +-----------+------------------------------------------------------------------------------------------
    | typeset    | 声明一个变量或变量类型。
    +-----------+------------------------------------------------------------------------------------------
    | ulimit    | 为系统用户设置指定的资源的上限
    +-----------+------------------------------------------------------------------------------------------
    | umask        | 为新建的文件和目录设置默认权限
    +-----------+------------------------------------------------------------------------------------------
    | unalias    | 删除指定的别名
    +-----------+------------------------------------------------------------------------------------------
    | unset        | 删除指定的环境变量或shell属性
    +-----------+------------------------------------------------------------------------------------------
    | wait        | 等待指定的进程完成,并返回退出状态码
    +-----------+------------------------------------------------------------------------------------------


    bash shell 常见外部命令
    +-----------------------------------------------------------------------------------------------------
    | 命 令        |    描 述
    +-----------+------------------------------------------------------------------------------------------
    | bzip2        | 采用Burrows-Wheeler块排序文本压缩算法和霍夫曼编码进行压缩
    +-----------+------------------------------------------------------------------------------------------
    | cat        | 列出指定文件的内容
    +-----------+------------------------------------------------------------------------------------------
    | chage        | 修改指定系统用户账户的密码过期日期
    +-----------+------------------------------------------------------------------------------------------
    | chfn        | 修改指定用户账户的备注信息
    +-----------+------------------------------------------------------------------------------------------
    | chgrp        | 修改指定文件或目录的默认属组
    +-----------+------------------------------------------------------------------------------------------
    | chmod        | 为指定文件或目录修改系统安全权限
    +-----------+------------------------------------------------------------------------------------------
    | chown        | 修改指定文件或目录的默认属主
    +-----------+------------------------------------------------------------------------------------------
    | chpasswd    | 读取一个包含登录名/密码的文件并更新密码
    +-----------+------------------------------------------------------------------------------------------
    | chsh        | 修改指定用户账户的默认shell
    +-----------+------------------------------------------------------------------------------------------
    | clear        | 从终端仿真器或虚拟控制台终端删除文本
    +-----------+------------------------------------------------------------------------------------------
    | compress    | 最初的Unix文件压缩工具
    +-----------+------------------------------------------------------------------------------------------
    | coproc    | 在后台模式中生成子shell,并执行指定的命令
    +-----------+------------------------------------------------------------------------------------------
    | cp        | 将指定文件复制到另一个位置
    +-----------+------------------------------------------------------------------------------------------
    | crontab    | 初始化用户的crontable文件对应的编辑器(如果允许的话)
    +-----------+------------------------------------------------------------------------------------------
    | cut        | 删除文件行中指定的位置
    +-----------+------------------------------------------------------------------------------------------
    | date        | 以各种格式显示日期
    +-----------+------------------------------------------------------------------------------------------
    | df        | 显示所有挂载设备的当前磁盘空间使用情况
    +-----------+------------------------------------------------------------------------------------------
    | du        | 显示指定文件路径的磁盘使用情况
    +-----------+------------------------------------------------------------------------------------------
    | emacs        | 调用emacs文本编辑器
    +-----------+------------------------------------------------------------------------------------------
    | file        | 查看指定文件的文件类型
    +-----------+------------------------------------------------------------------------------------------
    | find        | 对文件进行递归查找
    +-----------+------------------------------------------------------------------------------------------
    | free        | 查看系统上可用的和已用的内存
    +-----------+------------------------------------------------------------------------------------------
    | gawk        | 使用编程语言命令的流编辑器
    +-----------+------------------------------------------------------------------------------------------
    | grep        | 在文件中查找指定的文本字符串
    +-----------+------------------------------------------------------------------------------------------
    | gedit        | 调用GNOME桌面编辑器
    +-----------+------------------------------------------------------------------------------------------
    | getopt    | 解析命令选项(包括长格式选项)
    +-----------+------------------------------------------------------------------------------------------
    | groups    | 显示指定用户的组成员关系
    +-----------+------------------------------------------------------------------------------------------
    | groupadd    | 创建新的系统组
    +-----------+------------------------------------------------------------------------------------------
    | groupmod    | 修改已有的系统组
    +-----------+------------------------------------------------------------------------------------------
    | gzip        | 采用Lempel-Ziv编码的 GNU项目压缩工具
    +-----------+------------------------------------------------------------------------------------------
    | head        | 显示指定文件内容的开头部分
    +-----------+------------------------------------------------------------------------------------------
    | help        | 显示bash内建命令的帮助页面
    +-----------+------------------------------------------------------------------------------------------
    | killall    | 根据进程名向运行中的进程发送一个系统信号
    +-----------+------------------------------------------------------------------------------------------
    | kwrite    | 调用KWrite文本编辑器
    +-----------+------------------------------------------------------------------------------------------
    | less        | 查看文件内容的高级方法
    +-----------+------------------------------------------------------------------------------------------
    | link        | 用别名创建一个指向文件的链接
    +-----------+------------------------------------------------------------------------------------------
    | ln        | 创建针对指定文件的符号链接或硬链接
    +-----------+------------------------------------------------------------------------------------------
    | ls        | 列出目录内容
    +-----------+------------------------------------------------------------------------------------------
    | makewhatis| 创建能够使用手册页关键字进行搜索的whatis数据库
    +-----------+------------------------------------------------------------------------------------------
    | man        | 显示指定命令或话题的手册页
    +-----------+------------------------------------------------------------------------------------------
    | mkdir        | 在当前目录下创建指定目录
    +-----------+------------------------------------------------------------------------------------------
    | more        | 列出指定文件的内容,在每屏数据后暂停下来
    +-----------+------------------------------------------------------------------------------------------
    | mount        | 显示虚拟文件系统上挂载的磁盘设备或将磁盘设备挂载到虚拟文件系统上
    +-----------+------------------------------------------------------------------------------------------
    | mv        | 重命名文件
    +-----------+------------------------------------------------------------------------------------------
    | nano        | 调用nano文本编辑器
    +-----------+------------------------------------------------------------------------------------------
    | nice        | 在系统上使用不同优先级来运行命令
    +-----------+------------------------------------------------------------------------------------------
    | passwd    | 修改某个系统用户账户的密码
    +-----------+------------------------------------------------------------------------------------------
    | ps        | 显示系统上运行中进程的信息
    +-----------+------------------------------------------------------------------------------------------
    | pwd        | 显示当前目录
    +-----------+------------------------------------------------------------------------------------------
    | renice    | 修改系统上运行中应用的优先级
    +-----------+------------------------------------------------------------------------------------------
    | rm        | 删除指定文件
    +-----------+------------------------------------------------------------------------------------------
    | rmdir        | 删除指定目录
    +-----------+------------------------------------------------------------------------------------------
    | sed        | 使用编辑器命令的文本流行编辑器
    +-----------+------------------------------------------------------------------------------------------
    | sleep        | 在指定的一段时间内暂停bash shell操作
    +-----------+------------------------------------------------------------------------------------------
    | sort        | 基于指定的顺序组织数据文件中的数据
    +-----------+------------------------------------------------------------------------------------------
    | stat        | 显示指定文件的文件统计数据
    +-----------+------------------------------------------------------------------------------------------
    | sudo        | 以root用户账户身份运行应用
    +-----------+------------------------------------------------------------------------------------------
    | tail        | 显示指定文件内容的末尾部分
    +-----------+------------------------------------------------------------------------------------------
    | tar        | 将数据和目录归档到单个文件中
    +-----------+------------------------------------------------------------------------------------------
    | top        | 显示活动进程以及其他重要的系统统计数据
    +-----------+------------------------------------------------------------------------------------------
    | touch        | 新建一个空文件,或更新一个已有文件的时间戳
    +-----------+------------------------------------------------------------------------------------------
    | umount    | 从虚拟文件系统上删除一个已挂载的磁盘设备
    +-----------+------------------------------------------------------------------------------------------
    | uptime    | 显示系统已经运行了多久
    +-----------+------------------------------------------------------------------------------------------
    | useradd    | 新建一个系统用户账户
    +-----------+------------------------------------------------------------------------------------------
    | userdel    | 删除已有系统用户账户
    +-----------+------------------------------------------------------------------------------------------
    | usermod    | 修改已有系统用户账户
    +-----------+------------------------------------------------------------------------------------------
    | vi        | 调用vim文本编辑器
    +-----------+------------------------------------------------------------------------------------------
    | vmstat    | 生成一个详尽的系统内存和CPU使用情况报告
    +-----------+------------------------------------------------------------------------------------------
    | whereis    | 显示指定命令的相关文件,包括二进制文件、源代码文件以及手册页
    +-----------+------------------------------------------------------------------------------------------
    | which        | 查找可执行文件的位置
    +-----------+------------------------------------------------------------------------------------------
    | who        | 显示当前系统中的登录用户
    +-----------+------------------------------------------------------------------------------------------
    | whoami    | 显示当前用户的用户名
    +-----------+------------------------------------------------------------------------------------------
    | xargs        | 从STDIN中获取数据项,构建并执行命令
    +-----------+------------------------------------------------------------------------------------------
    | zip        | Windows下PKZIP程序的Unix版本
    +-----------+------------------------------------------------------------------------------------------

6. 使用 Linux 环境变量
-----------------------------------------------------------------------------------------------------------------------------------------

6.1 环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
bash shell 用一个叫作环境变量(environment variable)的特性来存储有关shell会话和工作环境的信息(这也是它们被称作环境变量的原因)。这项特性
允许用户在内存中存储数据,以便程序或 shell 中运行的脚本能够轻松访问到它们。这也是存储持久数据的一种简便方法。

在 bash shell中,环境变量分为两类:

    □ 全局变量
    □ 局部变量


6.1.1 全局环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
全局环境变量对于 shell 会话和所有生成的子 shell 都是可见的。局部变量则只对创建它们的 shell可见。这让全局环境变量对那些所创建的子 shell 要
获取父 shell 信息的程序来说非常有用。

Linux 系统在用户开始 bash 会话时就设置了一些全局环境变量。系统环境变量基本上都是使用全大写字母,以区别于普通用户的环境变量。

看全局变量,可以使用 env 或 printenv 命令:

    [devalone@devalone ~]$ printenv
    XDG_SESSION_ID=2
    HOSTNAME=devalone.sansovo.org
    SELINUX_ROLE_REQUESTED=
    TERM=xterm
    SHELL=/bin/bash
    HISTSIZE=1000
    SSH_CLIENT=192.168.1.101 49958 22
    SELINUX_USE_CURRENT_RANGE=
    SSH_TTY=/dev/pts/0
    USER=devalone
    ...
    
    [devalone@devalone ~]$ env
    XDG_SESSION_ID=2
    HOSTNAME=devalone.sansovo.org
    SELINUX_ROLE_REQUESTED=
    TERM=xterm
    SHELL=/bin/bash
    HISTSIZE=1000
    SSH_CLIENT=192.168.1.101 49958 22
    SELINUX_USE_CURRENT_RANGE=
    SSH_TTY=/dev/pts/0
    USER=devalone
    ...

    
系统为 bash shell 设置的全局环境变量数目众多,其中有很多是在登录过程中设置的,另外,用户的登录方式也会影响到所设置的环境变量。

要显示个别环境变量的值,可以使用 printenv 命令,但是不要用 env 命令。

    [devalone@devalone ~]$ printenv USER
    devalone

    [devalone@devalone ~]$ printenv HOME
    /home/devalone

也可以使用 echo 显示变量的值。在这种情况下引用某个环境变量的时候,必须在变量前面加上一个美元符 ($)。

    [devalone@devalone ~]$ echo $HOME
    /home/devalone

在 echo 命令中,在变量名前加上 $ 可不仅仅是要显示变量当前的值。它能够让变量作为命令行参数。

    [devalone@devalone ~]$ ls $HOME
    software  study  workspaces  模板  图片  下载  桌面
    devel  driver    programs    shell_test  test  公共   视频  文档  音乐

    [devalone@devalone ~]$ ls -l $HOME/shell_test
    总用量 16
    drwxrwxr-x. 2 devalone devalone 4096 7月   3 10:46 new_dir
    -rw-rw-r--. 1 devalone devalone   23 7月   3 14:29 sortfile
    -rwxrwxr-x. 1 devalone devalone  152 7月   3 10:51 test1.sh
    -rw-rw-r--. 1 devalone devalone  277 7月   3 11:26 testfile

全局环境变量可用于进程的所有子 shell:

    [devalone@devalone ~]$ bash

    [devalone@devalone ~]$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    devalone  1758  1757  0 09:17 pts/0    00:00:00 -bash
    devalone  1835  1758  2 09:26 pts/0    00:00:00 bash
    devalone  1865  1835  0 09:26 pts/0    00:00:00 ps -f

    [devalone@devalone ~]$ echo $HOME
    /home/devalone

    [devalone@devalone ~]$ exit
    exit


6.1.2 局部环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
局部环境变量只能在定义它们的进程中可见。尽管它们是局部的,但是和全局环境变量一样重要。事实上,Linux 系统也默认定义了标准的局部环境变量。
不过用户也可以定义自己的局部变量,这些变量被称为用户定义局部变量。

在Linux 系统并没有一个只显示局部环境变量的命令。set 命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量。

    [devalone@devalone ~]$ set
    BASH=/bin/bash
    BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath
    BASH_ALIASES=()
    BASH_ARGC=()
    BASH_ARGV=()
    BASH_CMDS=()
    BASH_COMPLETION_COMPAT_DIR=/etc/bash_completion.d
    BASH_LINENO=()
    BASH_REMATCH=()
    BASH_SOURCE=()
    BASH_VERSINFO=([0]="4" [1]="3" [2]="42" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")
    BASH_VERSION='4.3.42(1)-release'
    CATALINA_HOME=/home/devalone/programs/apache-tomcat-8.5.11
    COLUMNS=130
    COMP_WORDBREAKS=$' \t\n"\'><=;|&(:'
    DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    DIRSTACK=()
    EUID=1000
    GROUPS=()
    HISTCONTROL=ignoredups
    HISTFILE=/home/devalone/.bash_history
    HISTFILESIZE=1000
    HISTSIZE=1000
    HOME=/home/devalone
    HOSTNAME=devalone.sansovo.org
    ...
    
所有通过 printenv 命令能看到的全局环境变量都出现在了 set 命令的输出中。但在 set 命令的输出中还有其他一些环境变量,即局部环境变量和用户
定义变量。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    命令env、printenv 和 set 之间的差异很细微。set 命令会显示出全局变量、局部变量以及用户定义变量。它还会按照字母顺序对结果进行排序。env
    和 printenv 命令同 set 命令的区别在于前两个命令不会对变量排序,也不会输出局部变量和用户定义变量。在这种情况下,env 和 printenv的输出是
    重复的。


6.2 设置用户定义变量
-----------------------------------------------------------------------------------------------------------------------------------------
可以在 bash shell 中直接设置自己的变量。即在交互式 shell 或 shell 脚本程序中创建自己的变量并引用它们。


6.2.1 设置局部用户定义变量
-----------------------------------------------------------------------------------------------------------------------------------------
一旦启动了 bash shell(或者执行一个shell脚本),就能创建在这个shell进程内可见的局部变量了。可以通过等号给环境变量赋值,值可以是数值或字符串。

    [devalone@devalone ~]$ echo $my_variable

    [devalone@devalone ~]$ my_variable=hello
    [devalone@devalone ~]$ echo $my_variable
    hello

每次引用 my_variable 环境变量的值,只要通过 $my_variable 引用即可。如果要给变量赋一个含有空格的字符串值,必须用单引号来界定字符串的首和尾。

    [devalone@devalone ~]$ my_variable=hello world
    bash: world: 未找到命令...

    [devalone@devalone ~]$ my_variable='hello world'
    [devalone@devalone ~]$ echo $my_variable
    hello world

没有单引号的话,bash shell 会以为下一个词是另一个要执行的命令。注意,用户定义的局部环境变量用的是小写字母,而系统环境变量都是大写字母。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    所有的环境变量名均使用大写字母,这是 bash shell 的标准惯例。如果是自己创建的局部变量或是 shell 脚本,请使用小写字母。变量名区分大小写。
    在涉及用户定义的局部变量时坚持使用小写字母,这能够避免重新定义系统环境变量可能带来的灾难。


记住,变量名、等号和值之间没有空格,这一点非常重要。如果在赋值表达式中加上了空格,bash shell 就会把值当成一个单独的命令:

    [devalone@devalone ~]$ my_variable= 'hello world'
    bash: hello world: 未找到命令...

设置了局部环境变量后,就能在shell进程的任何地方使用它了。但是,如果生成了另外一个 shell,它在子 shell 中就不可用。

    [devalone@devalone ~]$ echo $my_variable
    hello world
    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ echo $my_varible

    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$ echo $my_variable
    hello world

类似地,如果在子进程中设置了一个局部变量,那么一旦退出了子进程,那个局部环境变量就不可用。

    [devalone@devalone ~]$ echo $my_child_variable

    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ my_child_variable='hello little world'
    [devalone@devalone ~]$ echo $my_child_variable
    hello little world
    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$ echo $my_child_variable

    [devalone@devalone ~]$


    
6.2.2 设置全局环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
在设定全局环境变量的进程所创建的子进程中,该变量都是可见的。创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。

这个过程通过 export 命令来完成,变量名前面不需要加$。

    [devalone@devalone ~]$ my_variable='I am global now'
    [devalone@devalone ~]$ export my_variable
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$

修改子 shell 中全局环境变量并不会影响到父 shell中该变量的值。

    [devalone@devalone ~]$ my_variable='I am global now'
    [devalone@devalone ~]$ export my_variable
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ my_variable="null"
    [devalone@devalone ~]$ echo $my_variable
    null
    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$ echo $my_variable
    I am global now

子 shell 甚至无法使用 export 命令改变父 shell 中全局环境变量的值。

    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ my_variable="null"
    [devalone@devalone ~]$ echo $my_variable
    null
    [devalone@devalone ~]$ export my_variable
    [devalone@devalone ~]$ echo $my_variable
    null
    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$ echo $my_variable
    I am global now


6.3 删除环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
用 unset 命令删除已经存在的环境变量。在 unset 命令中引用环境变量时,记住不要使用$。

    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ unset my_variable
    [devalone@devalone ~]$ echo $my_variable

    [devalone@devalone ~]$


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    在涉及环境变量名时,什么时候该使用$,什么时候不该使用$,记住一点就行了:如果要用到变量,使用$;如果要操作变量,不使用$。这条规则的一
    个例外就是使用 printenv 显示某个变量的值。


在处理全局环境变量时,处理结果有些不同。如果是在子进程中删除了一个全局环境变量,这只对子进程有效。该全局环境变量在父进程中依然可用。

    [devalone@devalone ~]$ my_variable="I am global now"
    [devalone@devalone ~]$ export my_variable
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ bash
    [devalone@devalone ~]$ echo $my_variable
    I am global now
    [devalone@devalone ~]$ unset my_variable
    [devalone@devalone ~]$ echo $my_variable

    [devalone@devalone ~]$ exit
    exit
    [devalone@devalone ~]$ echo $my_variable
    I am global now


6.4 默认的 shell 环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
默认情况下,bash shell 会用一些特定的环境变量来定义系统环境。这些变量在 Linux 系统上都已经设置好了,只管放心使用。bash shell 源自当初的
Unix Bourne shell,因此也保留了 Unix Bourne shell里定义的那些环境变量。


    bash shell提供的与 Unix Bourne shell 兼容的环境变量:
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | 变 量        | 描 述
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | CDPATH    | 冒号分隔的目录列表,作为cd命令的搜索路径
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | HOME        | 当前用户的主目录
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | IFS        | shell 用来将文本字符串分割成字段的一系列字符
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | MAIL        | 当前用户收件箱的文件名(bash shell会检查这个文件,看看有没有新邮件)
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | MAILPATH    | 冒号分隔的当前用户收件箱的文件名列表(bash shell会检查列表中的每个文件,看看有没有新邮件)
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | OPTARG    | getopts命令处理的最后一个选项参数值
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | OPTIND    | getopts命令处理的最后一个选项参数的索引号
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | PATH        | shell查找命令的目录列表,由冒号分隔
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | PS1        | shell命令行界面的主提示符
    +-----------+---------------------------------------------------------------------------------------------------------------------
    | PS2        | hell命令行界面的次提示符
    +-----------+---------------------------------------------------------------------------------------------------------------------

    除了默认的 Bourne shell 的环境变量,bash shell还提供一些自有的环境变量:
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | 变 量                    | 描    述
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | *                        | 含有所有命令行参数(以单个文本值的形式)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | @                        | 含有所有命令行参数(以多个文本值的形式)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | #                        | 命令行参数数目
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | ?                        | 最近使用的前台进程的退出状态码
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | -                        | 当前命令行选项标记
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | $                        | 当前shell的进程ID(PID)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | !                        | 最近执行的后台进程的PID
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | 0                        | 命令行中使用的命令名称
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | _                        | shell的绝对路径名
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH                    | 用来调用shell的完整文件名
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_ALIASES            | 含有当前已设置别名的关联数组
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_ARGC                | 含有传入子函数或shell脚本的参数总数的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_ARGV                | 含有传入子函数或shell脚本的参数的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_CMDS                | 关联数组,包含shell执行过的命令的所在位置
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_COMMAND            | shell正在执行的命令或马上就执行的命令
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_ENV                | 设置了的话,每个bash脚本会在运行前先尝试运行该变量定义的启动文件
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_EXECUTION_STRING    | 使用bash -c选项传递过来的命令
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_LINENO            | 含有当前执行的shell函数的源代码行号的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_REMATCH            | 只读数组,在使用正则表达式的比较运算符=~进行肯定匹配(positive match)时,包含了匹配到的模式和子模式
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_SOURCE            | 含有当前正在执行的shell函数所在源文件名的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_SUBSHELL            | 当前子shell环境的嵌套级别(初始值是0)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_VERSINFO            | 含有当前运行的bash shell的主版本号和次版本号的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_VERSION            | 当前运行的bash shell的版本号
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASH_XTRACEFD            | 若设置成了有效的文件描述符(0、1、2),则'set -x'调试选项生成的跟踪输出可被重定向。通常用来将跟踪输出到
    |                        | 一个文件中
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASHOPTS                | 当前启用的bash shell选项的列表
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | BASHPID                | 当前bash进程的PID
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COLUMNS                | 当前bash shell实例所用终端的宽度
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_CWORD            | COMP_WORDS变量的索引值,后者含有当前光标的位置
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_LINE                | 当前命令行
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_POINT            | 当前光标位置相对于当前命令起始的索引
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_KEY                | 用来调用shell函数补全功能的最后一个键
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_TYPE                | 一个整数值,表示所尝试的补全类型,用以完成shell函数补全
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_WORDBREAKS        | Readline库中用于单词补全的词分隔字符
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMP_WORDS            | 含有当前命令行所有单词的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COMPREPLY                | 含有由shell函数生成的可能填充代码的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | COPROC                | 占用未命名的协进程的I/O文件描述符的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | DIRSTACK                | 含有目录栈当前内容的数组变量
    +-----------------------+----------------------------------------------------------------------------------------------------------
    | EMACS                    | 设置为't'时,表明emacs shell缓冲区正在工作,而行编辑功能被禁止
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | ENV                    | 如果设置了该环境变量,在bash shell脚本运行之前会先执行已定义的启动文件(仅用于当bash shell以 POSIX 模式
    |                        | 被调用时)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | EUID                    | 当前用户的有效用户ID(数字形式)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | FCEDIT                | 供fc命令使用的默认编辑器
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | FIGNORE                | 在进行文件名补全时可以忽略后缀名列表,由冒号分隔
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | FUNCNAME                | 当前执行的shell函数的名称
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | FUNCNEST                | 当设置成非零值时,表示所允许的最大函数嵌套级数(一旦超出,当前命令即被终止)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | GLOBIGNORE            | 冒号分隔的模式列表,定义了在进行文件名扩展时可以忽略的一组文件名
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | GROUPS                | 含有当前用户属组列表的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | histchars                | 控制历史记录扩展,最多可有3个字符
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTCMD                | 当前命令在历史记录中的编号
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTCONTROL            | 控制哪些命令留在历史记录列表中
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTFILE                | 保存shell历史记录列表的文件名(默认是.bash_history)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTFILESIZE            | 最多在历史文件中存多少行
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTTIMEFORMAT        | 如果设置了且非空,就用作格式化字符串,以显示bash历史中每条命令的时间戳
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTIGNORE            | 由冒号分隔的模式列表,用来决定历史文件中哪些命令会被忽略
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HISTSIZE                | 最多在历史文件中存多少条命令
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HOSTFILE                | shell在补全主机名时读取的文件名称
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HOSTNAME                | 当前主机的名称
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | HOSTTYPE                | 当前运行bash shell的机器
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | IGNOREEOF                | shell在退出前必须收到连续的EOF字符的数量(如果这个值不存在,默认是1)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | INPUTRC                | Readline初始化文件名(默认是.inputrc)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LANG                    | shell的语言环境类别
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LC_ALL                | 定义了一个语言环境类别,能够覆盖LANG变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LC_COLLATE            | 设置对字符串排序时用的排序规则
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LC_CTYPE                | 决定如何解释出现在文件名扩展和模式匹配中的字符
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LC_MESSAGES            | 在解释前面带有$的双引号字符串时,该环境变量决定了所采用的语言环境设置
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LC_NUMERIC            | 决定着格式化数字时采用的语言环境设置
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LINENO                | 当前执行的脚本的行号
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | LINES                    | 定义了终端上可见的行数
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | MACHTYPE                | 用“CPU公司系统”(CPU-company-system)格式定义的系统类型
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | MAPFILE                | 一个数组变量,当mapfile命令未指定数组变量作为参数时,它存储了mapfile所读入的文本
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | MAILCHECK                | shell查看新邮件的频率(以秒为单位,默认值是60)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | OLDPWD                | shell之前的工作目录
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | OPTERR                | 设置为1时,bash shell会显示getopts命令产生的错误
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | OSTYPE                | 定义了shell所在的操作系统
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PIPESTATUS            | 含有前台进程的退出状态列表的数组变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | POSIXLY_CORRECT        | 设置了的话,bash会以POSIX模式启动
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PPID                    | bash shell父进程的PID
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PROMPT_COMMAND        | 设置了的话,在命令行主提示符显示之前会执行这条命令
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PROMPT_DIRTRIM        | 用来定义当启用了\w或\W提示符字符串转义时显示的尾部目录名的数量。被删除的目录名会用一组英文句点替换
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PS3                    | select命令的提示符
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PS4                    | 如果使用了bash的-x选项,在命令行之前显示的提示信息
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | PWD                    | 当前工作目录
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | RANDOM                | 返回一个0~32767的随机数(对其的赋值可作为随机数生成器的种子)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | READLINE_LINE            | 当使用bind –x命令时,存储Readline缓冲区的内容
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | READLINE_POINT        | 当使用bind –x命令时,表示Readline缓冲区内容插入点的当前位置
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | REPLY                    | read命令的默认变量
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | SECONDS                | 自从shell启动到现在的秒数(对其赋值将会重置计数器)
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | SHELL                    | bash shell的全路径名
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | SHELLOPTS                | 已启用bash shell选项列表,列表项之间以冒号分隔
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | SHLVL                    | shell的层级;每次启动一个新bash shell,该值增加1
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | TIMEFORMAT            | 指定了shell的时间显示格式
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | TMOUT                    | select和read命令在没输入的情况下等待多久(以秒为单位)。默认值为0,表示无限长
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | TMPDIR                | 目录名,保存bash shell创建的临时文件
    +-----------------------+---------------------------------------------------------------------------------------------------------
    | UID                    | 当前用户的真实用户ID(数字形式)
    +-----------------------+---------------------------------------------------------------------------------------------------------

不是所有的默认环境变量都会在运行 set 命令时列出。尽管这些都是默认环境变量,但并不是每一个都必须有一个值。


6.5 设置 PATH 环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
在 shell 命令行界面中输入一个外部命令时,shell必须搜索系统来找到对应的程序。PATH 环境变量定义了用于进行命令和程序查找的目录。

    [devalone@devalone ~]$ echo $PATH
    /usr/local/protoc/bin:/home/devalone/programs/apache-tomcat-8.5.11/bin:/usr/local/apache/maven/bin:/usr/local/apache/ant/bin
    :/usr/java/latest/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/devalone/.local/bin:/home/devalone/bin

PATH中的目录使用冒号分隔。

如果命令或者程序的位置没有包括在PATH变量中,那么如果不使用绝对路径的话,shell 是没法找到的。如果 shell 找不到指定的命令或程序,它会产生
一个错误信息:

    [devalone@devalone ~]$ test2.sh
    bash: test2.sh: 未找到命令...

问题是,应用程序放置可执行文件的目录常常不在 PATH 环境变量所包含的目录中。解决的办法是保证 PATH 环境变量包含了所有存放应用程序的目录。
可以把新的搜索目录添加到现有的PATH环境变量中,无需从头定义。PATH 中各个目录之间是用冒号分隔的。只需引用原来的 PATH 值,然后再给这个字符串
添加新目录就行了。参考下面的例子:

    [devalone@devalone shell-script]$ pwd
    /home/devalone/study/shell-script
    [devalone@devalone shell-script]$ PATH=$PATH:/home/devalone/study/shell-script
    [devalone@devalone shell-script]$ echo $PATH
    /usr/local/protoc/bin:/home/devalone/programs/apache-tomcat-8.5.11/bin:/usr/local/apache/maven/bin:/usr/local/apache/ant/bin
    :/usr/java/latest/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/devalone/.local/bin:/home/devalone/bin
    :/home/devalone/study/shell-script

    [devalone@devalone shell-script]$ test2.sh
    User info for userid: devalone
    UID: 1000
    HOME: /home/devalone

    
程序员通常的办法是将单点符也加入 PATH环 境变量。该单点符代表当前目录:
    
    [devalone@devalone shell-script]$ PATH=$PATH:.
    [devalone@devalone shell-script]$ cd shell_test
    [devalone@devalone shell_test]$ ll
    总用量 16
    drwxrwxr-x. 2 devalone devalone 4096 7月   3 10:46 new_dir
    -rw-rw-r--. 1 devalone devalone   23 7月   3 14:29 sortfile
    -rwxrwxr-x. 1 devalone devalone  152 7月   4 11:52 test1.sh
    -rw-rw-r--. 1 devalone devalone  277 7月   3 11:26 testfile

    [devalone@devalone shell_test]$ test1.sh
    The time and date are: 2018年 07月 04日 星期三 11:52:45 CST
    Let's see who's logged into the system:
    devalone pts/0        2018-07-04 09:17 (192.168.1.101)

对 PATH 变量的修改只能持续到退出或重启系统。这种效果并不能一直持续。


6.6 定位系统环境变量
-----------------------------------------------------------------------------------------------------------------------------------------
在登录 Linux 系统启动一个 bash shell时,默认情况下 bash 会在几个文件中查找命令。这些文件叫作启动文件或环境文件。bash 检查的启动文件取决于
启动 bash shell 的方式。启动 bash shell 有 3 种方式:

    □ 登录时作为默认登录shell
    □ 作为非登录shell的交互式shell
    □ 作为运行脚本的非交互shell
    

6.6.1 登录 shell
-----------------------------------------------------------------------------------------------------------------------------------------
当登录 Linux 系统时,bash shell 会作为登录 shell 启动。登录 shell 会从 5 个不同的启动文件里读取命令:

    □ /etc/profile
    □ $HOME/.bash_profile
    □ $HOME/.bashrc
    □ $HOME/.bash_login
    □ $HOME/.profile

/etc/profile 文件是系统上默认的 bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。


    NOTE:
    -------------------------------------------------------------------------------------------------------------------------------------
    要留意的是有些 Linux 发行版使用了可拆卸式认证模块( Pluggable Authentication Modules ,PAM)。在这种情况下,PAM 文件会在 bash shell
    启动之前处理,这些文件中可能会包含环境变量。PAM 文件包括 /etc/environment 文件和 $HOME/.pam_environment文件。

另外 4 个启动文件是针对用户的,可根据个人需求定制。


    ■ /etc/profile 文件
    -------------------------------------------------------------------------------------------------------------------------------------
    /etc/profile 文件是 bash shell 默认的的主启动文件。只要用户登录了 Linux 系统,bash 就会执行 /etc/profile 启动文件中的命令。不同的Linux
    发行版在这个文件里放了不同的命令。
    
    [devalone@devalone shell_test]$ cat /etc/profile
    # /etc/profile

    # System wide environment and startup programs, for login setup
    # Functions and aliases go in /etc/bashrc

    # It's NOT a good idea to change this file unless you know what you
    # are doing. It's much better to create a custom.sh shell script in
    # /etc/profile.d/ to make custom changes to your environment, as this
    # will prevent the need for merging in future updates.

    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }


    if [ -x /usr/bin/id ]; then
        if [ -z "$EUID" ]; then
            # ksh workaround
            EUID=`id -u`
            UID=`id -ru`
        fi
        USER="`id -un`"
        LOGNAME=$USER
        MAIL="/var/spool/mail/$USER"
    fi

    # Path manipulation
    if [ "$EUID" = "0" ]; then
        pathmunge /usr/sbin
        pathmunge /usr/local/sbin
    else
        pathmunge /usr/local/sbin after
        pathmunge /usr/sbin after
    fi

    HOSTNAME=`/usr/bin/hostname 2>/dev/null`
    HISTSIZE=1000
    if [ "$HISTCONTROL" = "ignorespace" ] ; then
        export HISTCONTROL=ignoreboth
    else
        export HISTCONTROL=ignoredups
    fi

    export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

    # By default, we want umask to get set. This sets it for login shell
    # Current threshold for system reserved uid/gids is 200
    # You could check uidgid reservation validity in
    # /usr/share/doc/setup-*/uidgid file
    if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
        umask 002
    else
        umask 022
    fi

    for i in /etc/profile.d/*.sh ; do
        if [ -r "$i" ]; then
            if [ "${-#*i}" != "$-" ]; then
                . "$i"
            else
                . "$i" >/dev/null
            fi
        fi
    done

    unset i
    unset -f pathmunge
    
    注意,用户的一些自定义信息尽量不要在这个文件上修改,可以在 /etc/profile.d/ 目录下创建自定义的配置文件,设置相关信息,系统在读取执行
    /etc/profile 脚本文件时,会迭代读取执行 /etc/profile.d/ 目录下的所有文件。

    
    ■ $HOME 目录下的启动文件
    -------------------------------------------------------------------------------------------------------------------------------------
    剩下的启动文件都起着同一个作用:提供一个用户专属的启动文件来定义该用户所用到的环境变量。大多数 Linux 发行版只用这四个启动文件中的一到
    两个:

        $HOME/.bash_profile
        $HOME/.bashrc
        $HOME/.bash_login
        $HOME/.profile
    
    注意,这四个文件都以点号开头,这说明它们是隐藏文件。它们位于用户的 HOME 目录下,所以每个用户都可以编辑这些文件并添加自己的环境变量,这
    些环境变量会在每次启动 bash shell 会话时生效。
    
    shell 会按照按照下列顺序,运行第一个被找到的文件,余下的则被忽略:
    
        $HOME/.bash_profile
        $HOME/.bash_login
        $HOME/.profile
    
    注意,这个列表中并没有 $HOME/.bashrc 文件。这是因为该文件通常通过其他文件运行的。
    
        [devalone@devalone shell_test]$ cat ~/.bash_profile
        # .bash_profile

        # Get the aliases and functions
        if [ -f ~/.bashrc ]; then
                . ~/.bashrc
        fi

        # User specific environment and startup programs

        PATH=$PATH:$HOME/.local/bin:$HOME/bin

        export PATH
    
    
    .bash_profile 启动文件会先去检查 HOME 目录中是不是还有一个叫 .bashrc 的启动文件。如果有的话,会先执行启动文件里面的命令。
    
    

6.6.2 交互式 shell 进程
-----------------------------------------------------------------------------------------------------------------------------------------
如果 bash shell 不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),这样启动的 shell 叫作交互式 shell。交互式 shell 不会像登录
shell 一样运行,但它依然提供了命令行提示符来输入命令。

如果 bash 是作为交互式 shell 启动的,它就不会访问 /etc/profile 文件,只会检查用户 HOME 目录中的 .bashrc文件。

    [devalone@devalone ~]$ cat .bashrc
    # .bashrc

    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi

    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=

    # User specific aliases and functions

.bashrc 文件有两个作用:一是查看并执行 /etc 目录下通用的 bashrc 文件,二是为用户提供一个定制自己的命令别名和私有脚本函数的地方。


6.6.3 非交互式 shell 进程
-----------------------------------------------------------------------------------------------------------------------------------------
系统执行 shell 脚本时用的就是这种 shell,不同的是它没有命令行提示符。但是当用户在系统上运行脚本时,也许希望能够运行一些特定启动的命令。

为了处理这种情况,bash shell 提供了 BASH_ENV 环境变量。当 shell 启动一个非交互式 shell 进程时,它会检查这个环境变量来查看要执行的启动文件。
如果有指定的文件,shell 会执行该文件里的命令,这通常包括 shell 脚本变量设置。

那如果 BASH_ENV 变量没有设置,shell 脚本到哪里去获得它们的环境变量呢?别忘了有些 shell 脚本是通过启动一个子 shell 来执行的,子 shell 可以
继承父 shell 导出过的变量。

例如,如果父 shell 是登录 shell,在 /etc/profile、/etc/profile.d/*.sh 和 $HOME/.bashrc 文件中设置并导出了变量,用于执行脚本的子 shell 就
能够继承这些变量。

由父 shell 设置但并未导出的变量都是局部变量。子 shell 无法继承局部变量。

对于那些不启动子 shell 的脚本,变量已经存在于当前 shell 中了。所以就算没有设置 BASH_ENV,也可以使用当前 shell 的局部变量和全局变量。


6.6.4 环境变量持久化
-----------------------------------------------------------------------------------------------------------------------------------------
对全局环境变量来说(Linux 系统中所有用户都需要使用的变量),可能更倾向于将新的或修改过的变量设置放在 /etc/profile 文件中,但这可不是什么
好主意。如果升级了所用的发行版,这个文件也会跟着更新,那所有定制过的变量设置可就都没有了。

最好是在 /etc/profile.d 目录中创建一个以 .sh 结尾的文件。把所有新的或修改过的全局环境变量设置放在这个文件中。

在大多数发行版中,存储个人用户永久性 bash shell 变量的地方是 $HOME/.bashrc文件。这一点适用于所有类型的 shell进程。但如果设置了 BASH_ENV变量,
那么记住,除非它指向的是 $HOME/.bashrc,否则应该将非交互式 shell 的用户变量放在别的地方。

6.7 数组变量
-----------------------------------------------------------------------------------------------------------------------------------------
环境变量有一个很好的特性就是,它们可作为数组使用。数组是能够存储多个值的变量。这些值可以单独引用,也可以作为整个数组来引用。

要给某个环境变量设置多个值,可以把值放在括号里,值与值之间用空格分隔。

    [devalone@devalone ~]$ mytest=(one two three four five)

如果想把数组像普通的环境变量那样显示,会失望的:
    [devalone@devalone ~]$ echo $mytest
    one

只有数组的第一个值显示出来了。要引用一个单独的数组元素,就必须用代表它在数组中位置的数值索引值。索引值要用方括号括起来。

    [devalone@devalone ~]$ echo ${mytest[2]}
    three

环境变量数组的索引值都是从零开始。

要显示整个数组变量,可用星号作为通配符放在索引值的位置。

    [devalone@devalone ~]$ echo ${mytest[*]}
    one two three four five


也可以改变某个索引值位置的值:

    [devalone@devalone ~]$ mytest[2]=seven
    [devalone@devalone ~]$ echo ${mytest[*]}
    one two seven four five

甚至能用 unset 命令删除数组中的某个值:,但是要小心,可能会有点复杂。

    [devalone@devalone ~]$ unset mytest[2]
    [devalone@devalone ~]$ echo ${mytest[*]}
    one two four five
    [devalone@devalone ~]$ echo ${mytest[2]}

    [devalone@devalone ~]$ echo ${mytest[3]}
    four

用 unset 命令删除在索引值为 2 的位置上的值。显示整个数组时,看起来像是索引里面已经没这个索引了。但当专门显示索引值为 2 的位置上的值时,
就能看到这个位置是空的。

可以在 unset 命令后跟上数组名来删除整个数组。

    [devalone@devalone ~]$ unset mytest
    [devalone@devalone ~]$ echo ${mytest[*]}

    [devalone@devalone ~]$

有时数组变量会让事情很麻烦,所以在 shell 脚本编程时并不常用。对其他 shell 而言,数组变量的可移植性并不好。

系列目录: 

    走进 Linux shell (一)

    走进 Linux shell (二)

    走进 Linux shell (三)

参考:

    《Linux 命令行与 shell 脚本编程大全》 第 3 版 —— 2016.8(美)Richard Blum  Cristine Bresnahan

猜你喜欢

转载自blog.csdn.net/devalone/article/details/81179723
今日推荐