随便说说

上篇文章,我推荐了一个对用户良好的Shell——Fish,然后又想到了一个关键问题,Shell的本质到底是什么?所以我就重新回顾了一下Shell的定义,感触良多,特地写多一篇文章讲一讲,过程中我还会讲到 Bash、source和profile等内容。

Shell是什么?

  • Shell 是一个命令行界面(CLI)程序,充当用户与操作系统内核之间的中介。它允许用户通过输入命令与操作系统交互,执行各种任务,如文件管理、程序执行、系统监控等。简单来说,Shell 是用户和操作系统之间的沟通桥梁。

  • 常见的Shell包括:

    • Bash (Bourne Again Shell):Linux 系统上最常用的 Shell,几乎所有 Linux 系统默认都使用它。
    • Zsh (Z Shell):一种功能强大的 Shell,提供更多的自动补全和脚本功能。
    • Fish (Friendly Interactive Shell):注重易用性和互动体验,界面友好,支持现代化的特性。

Shell的四大模式

  • 登陆模式:用户登录时启动的 Shell,是最常见的模式。比如使用 ssh登陆,使用 su - <用户名>切换用户,在图形界面下启动终端,都属于登陆模式。
  • 非登陆模式:Shell本身也是个程序,所以我们可以用运行程序的方式去运行它,比如在命令行下直接输入 bash运行,或者使用 bash <脚本>来让bash执行某个脚本,这些都属于非登陆模式。
  • 交互模式:用户可以与Shell进行交互,通常用于执行命令和查看结果。
  • 非交互模式:Shell自动执行命令,通常用于脚本和批量任务,不需要用户输入。

总结一下,当我们正常登陆Shell时,我们都处于登陆模式和交互模式。当我们在Shell下以程序的方式运行Shell时,就处于非登陆模式和交互模式。特别的,如果我们使用Shell就执行某个脚本时,就会处于非登陆模式和非交互模式了。

环境变量

  1. 环境变量本质上是一些存放配置信息的值,这些值在系统和程序运行时都会用到。很多环境变量的名称都是约定好并且是固定的,它们告诉程序一些重要的事情,比如:
    1. 你在哪里(比如你家目录的位置 HOME)。
    2. 哪些文件夹可以找到程序(比如 PATH,告诉系统去哪些文件夹找命令)。
    3. 你使用的是什么编辑器(比如 EDITOR,指定你用的文本编辑器)。
  2. 环境变量是一组动态值,Shell每次运行的时候,总是会加载系统设置的一些默认环境变量,我们也可以在Shell中对环境变量进行增删改查。
  3. 值得注意的是,无论你在Shell中对环境变量做什么改动,这些修改都会随着当前Shell的关闭而消失。
  4. 环境变量具有继承属性,比如我们在当前Shell中运行了一个子Shell,这个子Shell会继承父Shell的所有环境变量,以下实验可以很好地说明这个特性。
    1. 以登陆模式登陆Shell,比如打开终端,ssh连接等。
    2. 执行以下命令查询环境变量a,发现a没有定义值。
      echo $a  
      
    3. 给a定义值,并且再次查询,发现a的值已经定义为1了
      export a=1
      echo $a   
      
    4. 创建子Shell,并且在子Shell中查询a,发现a值确实被定义
      bash
      echo $a   
      
    5. 在子Shell中查询b,发现b没有定义值,然后定义b值为2
      echo $b
      export b=2
      echo $b  
      
    6. 退出子Shell,此时我们在父Shell查询a和b,发现a值定义仍在,但b值却随着子Shell的消失而不见了
      exit
      echo $a
      echo $b  
      

执行脚本

我们执行Shell脚本的方式有两种,一种是直接通过Shell执行,比如 bash <脚本名称>,还有一种是通过Source执行,比如 source <脚本名称>,那么它们两者有什么不同吗?

  • Shell方式:创建子Shell,以非登陆非交互模式执行脚本,脚本中涉及对环境变量的修改都只会在子Shell中生效。一旦脚本执行结束,子Shell退出,父Shell不会保留任何子Shell中的环境变量。
  • Source方式:直接在当前Shell中执行脚本,脚本中涉及对环境变量的修改都会在当前Shell中生效,直到当前Shell退出。

配置文件

  1. 环境变量虽然会随着Shell的退出而消失,但我们可以通过配置文件来设置默认环境变量,使得每次进入Shell时都会自动加载。
  2. 大部分Linux系统中都会存在以下几个文件,在这些文件中都可以设置默认环境变量,但效果却不一样
    /etc/profile
    ~/.profile
    /etc/bashrc
    ~/.bashrc
    
  3. 需要注意,部分系统中可能没有 bashrc文件,但会存在 bash.bashrc文件,它们本质上是一样的。
  4. 首先是作用域,/etc开头的文件,都是与系统相关,直接作用于所有用户,而 ~/开头的文件,则只作用于该文件所属的用户。这也很好理解,本来 ~就是表示用户的家目录,其他用户就不应该去读取。
  5. 在登陆模式下,Shell会去自动加载并执行 /etc/profile ~/.profile文件,其中有关对环境变量的命令也会自动执行,以此达到自动加载环境变量的效果。比如我们可以在其中设置PATH,给PATH增加路径:
    # 以下命令请添加到/etc/profile或~/.profile文件末尾
     export PATH=$PATH:/aaa/bbb
    
  6. 如果是非登陆模式下,Shell不会读取任何的 profile文件,而是只读取 ~/bashrc文件。并且该文件属于bash独有,也就是说,如果以非登陆模式下执行其他Shell,如Fish,则不会读取 ~/bashrc文件。但相对应的,类似于Bash,Fish也有自己独有的配置文件,这方面内容请查阅官方文档。
  7. 如果你自己有去尝试的话,会发现个很奇怪的现象,明明我把环境变量只定义在 profile文件中,但是如果尝试以非登陆模式进入Shell时,还是能读取到该环境变量。这个问题其实很简单,因为环境变量具有继承属性,当前Shell的父Shell已经默认加载了 profile文件中的环境变量了,那么子Shell当然也会有。
  8. 需要注意的是,/etc/bashrc文件在任何时候,都不会被读取和执行,因此不要在该文件中进行设置。

参考资料

/etc/bashrc和/etc/profile傻傻分不清楚?