Bash/Zsh 有四种不同运行模式,你的 bash 配置写错地方的话,不但会拖慢 bash 的速度,还会发生明明写了登陆配置但是就是没生效的情况。
第一个维度:interactive mode / non-interactive mode
Bash 的 交互模式(interactive mode) 是指你直接输入:
bash
以后 bash 出现一个 “$>
” 的 PROMPT,等待用户不断的输入指令,输入 “exit” 或者按了 CTRL+D 才会结束。你 ssh 登陆到一台电脑,或者命令行下面打 bash ,后面没有参数的话,进入的都是交互模式。
而 非交互模式(non-interactive mode) 是指你用 bash 运行一个命令或者脚本,运行完 bash 就退出那种:
bash -c "echo 123"
bash script.sh
上面这两种情况下,bash 运行完脚本,就退出了,不会出现 PROMPT,也不会等待用户输入新指令。
境变量 $-
里如果有字符 i 的话,代表是一个 interactive shell,否则是 non-interactive mode,我们可以简单测试一下:
$> [[ $- == *i* ]] && echo "Interactive" || echo "Not interactive"
Interactive
$> bash -c '[[ $- == *i* ]] && echo "Interactive" || echo "Not interactive" '
Not interactive
登陆过后的 shell 都是交互模式的,再交互模式下直接检测 $-
得到 “Interactive” 的结果,而bash 直接运行命令属于非交互模式,所以输出 “Not interactive”
再写一个脚本:check_interactive.sh
继续验证:
#! /bin/bash
[[ $- == *i* ]] && echo "Interactive" || echo "Not interactive"
检验一下:
$> source check_interactive.sh
Interactive
$> bash check_interactive.sh
Not interactive
$> bash -c "source check_interactive.sh"
Not interactive
在 Bash 中,source <文件名>
是在当前 bash shell 进程内执行脚本,效果和直接敲里面的命令一样,所以是交互模式。而 bash <文件名>
是启动一个新的 bash 进程执行脚本,所以是非交互模式。
因此,我们平时写的一大堆 bash 配置,都是针对 “交互模式” 的,如果让 bash 执行条命令都要去运行各种初始化脚本的话,效率太低了,所以 ~/.bashrc
开头就有一句判断:
# If not running interactively, don't do anything
[[ "$-" != *i* ]] && return
或者写为:
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
就是为了避免非交互模式随便运行一条命令都要解析后面的各种配置用的。
当然,新版本的 bash 如果以非交互模式启动,会直接跳过 ~/.bashrc
的解析,而这几行 bashrc 中的检测为了兼容被保留了下来。
因此,如果我们不确定 bash/zsh 的版本和行为,又自己从头开始写配置的话,需要在配置开头增加相应的检测代码,避免不必要的工作。
(点击 more/continue 继续)