Linux 基本工具(Bash 环境初始化)
了解 Bash Shell 环境初始化流程的最好的办法是查看 man bash
, 中间有部分专门描述 Bash Shell 相关的环境配置文件.如下:
FILES
/bin/bash
The bash executable
/etc/profile
The systemwide initialization file, executed for login shells
/etc/bash.bashrc
The systemwide per-interactive-shell startup file
/etc/bash.bash.logout
The systemwide login shell cleanup file, executed when a login shell exits
~/.bash_profile
The personal initialization file, executed for login shells
~/.bashrc
The individual per-interactive-shell startup file
~/.bash_logout
The individual login shell cleanup file, executed when a login shell exits
~/.inputrc
Individual readline initialization file
我们看到 bash man page 中提到 login shells
的概念, 我们先来了解下, 什么是 login shells
? 什么是 no login shells
.
login shell
登陆时走完整的会话构建流程, 比如tty1
~tty6
控制终端, 或者 ssh 远程登陆.no login shell
登陆时不需要走完整的会话构建流程, 比如 在 X11 图形环境下, 打开的终端窗口, 或者是在 Shell 下进入子 Shell 进程.
两者最大的区别是 login shell
会执行 系统范围 /etc/profile
一直到用户环境的 ~/.bash_profile
等等环境信息.而no login shell
并不会执行系统范围的环境初始化流程,仅执行用户环境 ~/.bashrc
初始化流程. no login shell
的系统环境信息是从父进程中集成过来的.
比如在/etc/profile.d
下添加了环境信息, Bash Shell 父进程如果没刷新, 直接进入 Bash Shell 子进程,那子进程也感知到最新环境信息, 确实要刷新的话, 需要手动初始化系统范围的环境信息, 比如执行 source /etc/profile
或者 . /etc/profile
.
source
和 .
符号是等价的.
Bash Man Page 也告诉我们除了可执行文件 /bin/bash
, 其他几个环境初始化文件都有各自的生命周期
/etc/profile
系统范围的环境信息初始化, 在新的login shell
构建过程中会激活该环境配置信息/etc/bash.bashrc
每个交互 Shell 初始化文件/etc/bash.bash.logout
系统范围login shell
退出时的环境清理文件~/.bash_profile
每个login shell
初始化过程,用户环境初始化配置文件.~/.bashrc
用户环境下交互 Shell 的环境初始化配置文件.~/.bash_logout
login shell
退出时执行用户环境清理配置文件~/.inputrc
用户环境交互原信息配置信息, 比如定义一些交互快捷键
接下来我们看看 /etc/profile
的初始化逻辑. 在 Ubuntu 系统下, /etc/profile
的内容比较简单
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ "$PS1" ]; then
if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then
# The file bash.bashrc already sets the default PS1.
# PS1='\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
可以看到初始化 /etc/bash.bashrc
, 以及循环初始化 /etc/profile.d
的环境配置文件.
而 /etc/bash.bashrc
的内容比较丰富, 我们逐步看一下:
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user@host:dir
#case "$TERM" in
#xterm*|rxvt*)
# PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
# ;;
#*)
# ;;
#esac
# enable bash completion in interactive shells
#if ! shopt -oq posix; then
# if [ -f /usr/share/bash-completion/bash_completion ]; then
# . /usr/share/bash-completion/bash_completion
# elif [ -f /etc/bash_completion ]; then
# . /etc/bash_completion
# fi
#fi
# sudo hint
if [ ! -e "$HOME/.sudo_as_admin_successful" ] && [ ! -e "$HOME/.hushlogin" ] ; then
case " $(groups) " in *\ admin\ *|*\ sudo\ *)
if [ -x /usr/bin/sudo ]; then
cat <<-EOF
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
EOF
fi
esac
fi
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/share/command-not-found/command-not-found -- "$1"
return $?
else
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
这里逻辑虽然多, 前面部分主要处理 chroot
环境下, PS1
的合理提示问题.后面部分主要是完成 Bash Shell
命令自动补全功能, 以及依赖 sudo
的操作的命令权限提示信息以及命令不存在的情况下, 启用command-not-found
包的逻辑.
而 ~/.bash_profile
一般来说等价 ~/.profile
, 较早的环境使用的是 ~/.profile
, 当然如果文件同时存在的话, 将兼容同时读取两个文件中内容. ~/.bash_profile
中的内容相对来说比较简单:
# ~/.bash_profile: executed by Bourne-compatible login shells.
if [ "$BASH" ]; then
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
fi
mesg n || true
就是读取 ~/.bashrc
中的内容, 而 ~/.bashrc
中的内容更多的是对用户交互环境的初始化.
实际上,对用户环境的初始化,有很多独立的配置文件, 比如~/.bash_aliases
, 专门用于配置环境别名. 这样便于持续性更新和维护, 而不是散落的到处都是.