了解 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, 专门用于配置环境别名. 这样便于持续性更新和维护, 而不是散落的到处都是.

【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!
https://cloud.tencent.com/act/cps/redirect?redirect=1068&cps_key=e4b50f6c64a4480367f8a8d16fd07c5a&from=console

标签: profile, bash, bash环境配置, bash_profile

添加新评论