理解Linux中Shell初始化文件和用户Profiles文件

Linux是一个多用户、实时共享的操作系统,意味着不止一个用户可以登录同一个系统。系统管理员通过任务管理来分配不同用户的权限,比如安装、升级、卸载应用程序,运行编译程序,文件查看、编辑等等。

Linux允许通过两种主要方式来创建用户环境:系统级(全局)和用户级(个人)。一般情况下,Linux系统都会运行基本shell程序,当用户登录成功后shell会在初始化时根据某些文件开创建环境。

推荐阅读: Linux中如何设置环境变量

Linux中的Shell初始化

Shell被调用时,会有一些初始化/启动文件被启用,它们的主要作用是为shell本身或用户设定运行环境,包含一些函数、 变量、别名等等。

Shell可以读取以下两种初始化文件:

  • 系统级启动文件 – 包含一些应用于户所有用户的全局配置,一般存在于/etc目录中,如 /etc/profiles/etc/bashrc 或者 /etc/bash.bashrc
  • 用户级启动文件 – 包含一些应用于单用户的配置文件,一般存放在各个用户目录,这些配置可以覆盖系统级全局配置,如.profiles.bash_profile.bashrc.bash_login.

Shell存在三种调用模式:

1. 交互式登录Shell

当用户成功登录系统后调用该Shell,使用/bin/login登录,随后读取/etc/passwd文件。

当启动交互式shell后,将读取/etc/profile文件以及特定的用户文件~/.bash_profile

login as: pi
pi@pi.raspi.in's password:

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Apr 25 10:43:53 2017 from 211.137.135.200
pi@raspi:~ $

2. 非登录交互式Shell

当使用如$/bin/bash or $/bin/zsh的命令行时,将会系统非登录交互式shell。他也可以通过/bin/su命令来运行。

另外,在打开如konsoleterminatorxterm等图形化终端程序时 ,非登录交互式shell也将被调用。

这种情况下调用时,它将拷贝父shell的环境,并读取相应用户级的 ~/.bashrc 配置文件。

pi@raspi:~ $ sudo -s
root@raspi:/home/pi# cd
root@raspi:~# ls -la
total 56
drwx------  8 root root 4096 Apr 13 10:26 .
drwxr-xr-x 22 root root 4096 Feb  1 20:28 ..
-rw-------  1 root root 2177 Apr 25 11:21 .bash_history
-rw-r--r--  1 root root  765 Feb  1 20:48 .bashrc
drwx------  3 root root 4096 Mar  2 14:28 .cache
drwxr-xr-x  3 root root 4096 Mar  3 11:15 .config
drwxr-xr-x  3 root root 4096 Mar  3 11:15 .local
drwxr-xr-x  2 root root 4096 Apr 13 10:26 .oracle_jre_usage
drwxr-xr-x  2 root root 4096 Feb 12 11:18 .pip
-rw-r--r--  1 root root  140 Nov 20  2007 .profile
-rw-------  1 root root   41 Mar  3 11:39 .python_history
drwx------  2 root root 4096 Mar  3 11:20 .ssh
-rw-------  1 root root 6342 Mar 30 17:30 .viminfo

3. 非交互式Shell

当运行脚本时则条用非交互式shell。在这种模式下,它将处理所运行的脚本中的命令、函数等操作,不需要进行交互式输入,除非脚本需求。使用的环境继承自父shell

理解系统级Shell启动文件

下来,我们来看看一些系统级启动文件都有什么内容:

/etc/profile file文件存储登录时系统级环境配置和启动程序。如果你想配置应用于所有用户的环境设置,可以加入此文件。

比如,你可在这里设置全局路径变量。

pi@raspi:~ $ cat /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 [ "`id -u`" -eq 0 ]; then
  PATH="/usr/local/sbin:/usr/local/usr/sbin:/usr/sbin:/bin"
else
#old bak  PATH="/usr/local/sbin:/usr/local/usr/sbin:/usr/sbin:/bin:/usr/local/games:/usr/games"
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin:/sbin:/usr/local/games:/usr/games"
fi
export PATH

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
export PATH=/usr/local/nginx/sbin:$PATH

注意:当你使用RHEL/CentOS7系统时,会看到如下提示”It’s not recommended to change this file unless you know what you are doing. It’s much better to create a custom .sh shell script in /etc/profile.d/ to make custom changes to your environment, as this will prevent the need for merging in future updates”。

提示中的 /etc/profile.d/ 目录保存着所有的自定义修改运行环境的脚本。

pi@raspi:~ $ cd /etc/profile.d/
pi@raspi:/etc/profile.d $ ls -l
total 8
-rw-r--r-- 1 root root 663 Mar 23  2014 bash_completion.sh
-rw-r--r-- 1 root root 387 Mar 31 11:47 oneinstack.sh
pi@raspi:/etc/profile.d $

/etc/bashrc/etc/bash.bashrc文件,包含应用于所有用户的系统级函数、变量、别名等配置信息。

如果你的系统包含多种Shell程序,建议你可以将一些特定的配置放到这里。

root@raspi:~# cat /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

# 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

理解用户级Shell启动文件

接下来,我们看一下用户级启动文件,它们中的配置只应用户对应用户,一般放置在相对应的用户目录中:

root@raspi:~# ls -la
total 56
drwx------  8 root root 4096 Apr 13 10:26 .
drwxr-xr-x 22 root root 4096 Feb  1 20:28 ..
-rw-------  1 root root 2177 Apr 25 11:21 .bash_history
-rw-r--r--  1 root root  765 Feb  1 20:48 .bashrc
drwx------  3 root root 4096 Mar  2 14:28 .cache
drwxr-xr-x  3 root root 4096 Mar  3 11:15 .config
drwxr-xr-x  3 root root 4096 Mar  3 11:15 .local
drwxr-xr-x  2 root root 4096 Apr 13 10:26 .oracle_jre_usage
drwxr-xr-x  2 root root 4096 Feb 12 11:18 .pip
-rw-r--r--  1 root root  140 Nov 20  2007 .profile
-rw-------  1 root root   41 Mar  3 11:39 .python_history
drwx------  2 root root 4096 Mar  3 11:20 .ssh
-rw-------  1 root root 6342 Mar 30 17:30 .viminfo

~/.bash_profile包含一些用户级的环境变量、启动程序、配置信息等等。你可以在这里自定义路径信息、变量等内容:

# cat ~/.bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi
# User specific environment and startup programs
export PATH

~/.bashrc文件中包含用户自定义别名和函数等。

pi@raspi:~ $ cat ~/.bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# 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, unless we know we "want" color)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
        # We have color support; assume it's compliant with Ecma-48
        # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
        # a case would tend to support setf rather than setaf.)
        color_prompt=yes
    else
        color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
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

~/.bash_login 文件包含一些登陆时运行的配置信息。当~/.bash_profile缺失时,bash将读取~/.bash_login

~/.bash_profile~/.bash_login缺失时,bash将读取 ~/.profile文件。它同样包含配置信息、函数、变量、别名的信息。

下面,我们再看看两个bash初始化时非必须,但是比较重要的配置文件。

Linux系统有的一个历史命令功能,该功能按用户记录运行的所有命令历史,存储在 ~/.bash_history 文件中。

查看历史命令:

root@bwgzl:~# history
    1  2017-04-25 11:41:34 root cd sf/
    2  2017-03-31 10:46:17 root ls
    3  2017-03-31 10:46:29 root ./setup-seafile-mysql.sh
    4  2017-03-31 10:47:47 root mysql -u sf -h localhost -p
    5  2017-03-31 10:47:57 root reboot
    6  2017-03-31 10:48:28 root ls
    7  2017-03-31 10:48:33 root ls
    8  2017-03-31 10:48:36 root cd sf
    9  2017-03-31 10:48:36 root ls
   10  2017-03-31 10:48:42 root ./setup-seafile-mysql.sh
   11  2017-03-31 10:48:48 root exit
   12  2017-03-31 11:16:54 root cd /data/wwwroot/unixetc.com/

最后一个是 ~/.bash_logout 文件,它不用于shell启动,但存储一些特殊的指令当用户注销时执行。

一个很有用的例子就是当用户注销后,清空所有屏幕内容。

root@bwgzl:~# cat bash_logout 
# ~/.bash_logout

clear