unix系统一些概念
基于mac写,有的部分可能和linux有出入

==了解== 熟悉 掌握 精通

shell 环境

  • 在mac里,普通用户shell环境是 zsh ,root用户环境是 sh ,查看用
    echo $SHELL
    zsh的配置是 .zshrc,bash的配置是 .bashrc
    sh没有配置文件,只加载$ENV的内容,不加载其他文件

  • /etc/ 下的配置文件是全局通用,即所有用户包括root 都用
    ~/ 下的配置文件是当前用户的配置,只能看到自己的,切换用户后就看不到了

  • 普通用户的 ~ 指的是 /User/[username],root 的 ~ 指的是 /var/root

启动条件的维度

  1. interactive mode / non-interactive mode
  • 交互模式(interactive mode)* 是指你直接输入bash以后 , bash 出现一个 “$> " 的 PROMPT,等待用户不断的输入指令,输入 “exit” 或者按了 CTRL+D 才会结束。

​ ssh 登陆到一台电脑,或者命令行下面打 bash ,后面没有参数的话,进入的都是交互模式.

  • **非交互模式(non-interactive mode)**是指你用 bash 运行一个命令或者脚本,运行完 bash 就退出。like $ bash test.sh
  • 境变量 $- 里如果有字符 i 的话,是一个 interactive shell,否则是 non-interactive mode
# (登陆sell直接交互)交互模式下直接检测 $- 得到 "Interactive"  
$> [[ $- == *i* ]] && echo "Interactive" || echo "Not interactive"
Interactive
# 直接运行命令属于非交互模式,所以输出 "Not interactive"
$>  bash -c '[[ $- == *i* ]] && echo "Interactive" || echo "Not interactive" '
Not interactive			
  • ⚡️登陆过后的 shell 都是交互模式的。

  • 在 Bash 中,source <文件名> 是在当前 bash shell 进程内执行脚本,效果和直接敲里面的命令一样,所以是交互模式。而 bash <文件名>是启动一个新的 bash 进程执行脚本,所以是非交互模式。

  • 因此,我们平时写的一大堆 bash 配置,都是针对“交互模式”的,如果让 bash 执行条命令都要去运行各种初始化脚本的话,效率太低了,所以 ~/.bashrc 开头就有一句判断:

    # If not running interactively, don't do anything
    [[ "$-" != *i* ]] && return
    

    就是为了避免非交互模式随便运行一条命令都要解析后面的各种配置用的。

    当然,新版本的 bash 如果以非交互模式启动,会直接跳过 ~/.bashrc 的解析,而这几行 bashrc 中的检测为了兼容被保留了下来。

    因此,如果我们不确定 bash/zsh 的版本和行为,又自己从头开始写配置的话,需要在配置开头增加相应的检测代码,避免不必要的工作。

  1. Login shell / Non-login shell

    • 交互模式非交互模式区别的是 bash/zsh 用于接受用户命令,还是执行运行一段脚本。

      而这里的 登陆 shell非登陆 shell决定的是 bash 加载哪个登陆脚本:

      • 登陆 shell:终端登陆时,ssh链接时,su --login <username> 切换用户时。
      • 非登陆 shell:直接运行 bash 时,su <username> 切换用户时(前面没有加 –login)。

      判断当前模式:

      shopt -q login_shell && echo "Login shell" || echo "Not login shell"
      
      #zsh下没有shopt 使用
      [[ -o login ]] && echo "Login shell" || echo "Not login shell"
      

    ​ su 切换是非登陆模式,除非显式声明-login

    • bash 在登陆和非登陆状态下,初始化脚本不同

      • 非登陆模式:只加载 ~/.bashrc,加载完就继续了。
      • 登陆模式:只加载 ~/.bash_profile,加载完就继续了,除非 ~/.bash_profile 不存在,那么尝试加载 ~/.bashrc,然后再继续。

      如果系统里只有一个 ~/.bashrc 文件,那么不论哪种模式它都会被加载。

      而如果系统中同时存在 ~/.bashrc 和 ~/.bash_profile,就会根据是否 login shell 而选择性加载。

      如果同时都有且登陆,~/.bashrc 中的文件就会被跳过。

      现在一般只留~/.bashrc,如果想both,可以在~/.bash_profile里调用~/.bashrc脚本以便统一配置用户环境。

    • 区分登陆/非登陆,可以检测只有登陆时才做一些事情,比如登陆欢迎提示。如果su切换或运行bash就不需要此提示。除非加上了 –login 参数。

    • login shell 下可以用 logout 或者 exit 退出,而 non-login shell 下面,则只能使用 exit 退出。

    • zsh 无论登陆与否都会加载~/.zshrc,需要在文件里判断情况

读取顺序

  • sh

    当以sh的方式调用时,Bash在启动文件被读取后进入POSIX模式,它就会尽量模仿sh的历史版本的启动行为,同时也符合POSIX标准。

    当以sh的名字作为交互式Shell调用时,Bash会寻找变量 ENV,如果有就调用。

  • bash

    • 非登陆:仅会读取 ~/.bashrc
    • 登陆:
      1⃣️ 先整体环境设置 /etc/profile
      2⃣️ 个人配置,读取下面三个之一:
      按优先级:~/.bash_profile~/.bash_login~/.profile
      前面的不存在,就往后读,读到了,再后面就不读了。
      .bash_profile会去调用.bashrc,如果没有就调用/etc/bashrc
      image-20191201181925883
  • POSIX mode

    有几种不同的调用方式:

    1. sh: Bash enters POSIX mode after reading startup files.
    2. bash --posix: Bash enters POSIX mode before reading startup files.
    3. set -o posix: Bash switches to POSIX mode.
    4. POSIXLY_CORRECT: If this variable is in the environment when bash starts, the shell enters posix mode before reading the startup files, like bash --posix. If it is set while bash is running, like set -o posix.
  • 一个存疑说osx相反,default for each new terminal window, calling .bash_profile instead of .bashrc. https://joshstaiger.org/archives/2005/07/bash_profile_vs.html

  • ~/.bashrc等中设定的变量(局部)只能继承/etc/profile中的变量,他们是"父子"关系

img

  • 【主】macOS下打开的shell是登录式交互shell,登录Shell(不管是不是交互式的)

    /etc/profile ➡️ ~/.bash_profile ➡️ ~/.bashrc➡️/etc/bashrc

    💡 如果安装了zsh,则.bash_profile 文件中的环境变量就无法起到作用,会加载~/.zprofile,这里面可以配置python环境变量等)

    ​ 且不执行/etc/profile,登陆执行~/.zshrc与/etc/zshenv与/etc/zshrc

  • 文本模式登录

    /etc/profile➡️~/.bash_profile➡️~/.bashrc➡️/etc/bashrc

  • 图形模式登录

    顺序读取:/etc/profile➡️~/.profile

  • 图形模式登录后,打开终端时

    顺序读取:~/.bashrc➡️/etc/bashrc

  • 从其它用户su到该用户

    ① 带-l参数(–login),如:su -l username,则bash是lonin的

    ​ 顺序读取:/etc/profile➡️~/.bash_profile➡️~/.bashrc➡️/etc/bashrc

    ② 没有带-l参数,则bash是non-login的

    ​ 顺序读取:~/.bashrc➡️/etc/bashrc

    ​ 并从父进程继承其环境变量

  • 上面的例子凡是读取到~/.bash_profile的,若该文件不存在,则读取~/.bash_login

    若前两者不存在,读取~/.profile

一些错误和混淆概念

执行脚本的方式:
bash test.sh :会在新的子进程执行脚本。此进程的相关变量函数进程结束就消失,不影响当前的shell环境。
./test.sh :会在新的子进程执行脚本。相关变量函数进程结束就消失。但是会使用脚本第一行#!指定的shell,而不一定是bash。
source test.sh. test.sh:会在当前的shell进程执行脚本。意味着变量和函数会保留在当前的shell环境中,即使脚本执行结束也不会消失。通常用来配置环境变量或定义函数。