Bash 初始化文件
交互式 login shell
在下列情况下,我们可以获得一个 login shell
:
- 登录系统时获得的顶层
shell
,无论是通过本地终端登录,还是通过网络ssh
登录。这种情况下获得的login shell
是一个交互式shell
。 - 在终端下使用
--login
选项调用bash
,可以获得一个交互式login shell
。 - 在脚本中使用
--login
选项调用bash
(例如:#!/bin/bash --login
)可以得到一个非交互式的login shell
。 - 使用
su -
切换到指定用户时,获得此用户的login shell
。如果不使用-
,则获得non-login shell
。
login shell
启动时首先读取 /etc/profile
系统全局配置,然后依次查找 ~/.bash_profile
、 ~/.bash_login
、 ~/.profile
三个配置文件,并且读取首个找到的并且可读的文件。 login shell
退出时读取并执行 ~/.bash_logout
中的命令。 如果配置文件存在但不可读,则会显示错误消息;如果文件不存在, bash
将自动搜索下一个文件。
[!1info] 默认的
/etc/profile
会定义PATH
、USER
、HOSTNAME
、HISTSIZE
等全局环境变量,还会自动导入/etc/bash.bashrc
文件(包含系统级shell
函数和别名),以及/etc/profile.d
路径下被用于针对特定程序进行初始化的所有*.sh
文件。
交互式 non-login shell
非登录 shell
意味着在启动时不必通过系统身份验证。 GUI
中用户打开的终端默认为非登录 shell
,可以通过 logout
命令判断:
# 在Ubuntu GUI桌面打开一个终端
> logout
bash: logout: not login shell: use `exit'
> bash --login
> logout # 正常登出 什么也不会输出
非登录 shell
在初始化时仅读取 ~/.bashrc
资源文件。
[!1INFO]
~/.bashrc
文件会自动被~/.bash_profile
或~/.profile
加载,因此为了保证login shell
和交互式non-login shell
得到相同的配置,一般将环境变量定义在~/.bashrc
文件中。
> echo "export sflag=\"login shell will see this message\"" >> ~/.profile
> bash
> echo $sflag
# 找不到这个变量 会打印一个空行
> exit
> bash --login
> echo $sflag
login shell will see this message
> logout
非交互式 shell
通过 bash
命令执行脚本时会以非交互( non-interactively
)的方式启动 shell
,这保证了在脚本执行过程中不会被用户干扰。在非交互式脚本启动时,仅会加载 BASH_ENV
变量指向的文件。
[!1WARN] 由于
PATH
变量默认不会被非交互式shell
加载,因此变量BASH_ENV
的值应该为绝对路径。
通过特殊变量 -
可以查看当前shell的模式:
> echo $-
himBHs # 带有'i'就是交互式shell
另一个简单的方式是检查当前 shell
中是否存在提示符环境变量 PS1
.
if [ -z "$PS1" ]; then echo "非交互式";else echo "交互式";fi
特殊情况
兼容模式
如果使用命令 sh
调用 bash
,则为了保证兼容性会按照 sh
的方式对 bash
进行初始化。作为 login shell
启动时, bash
依次读取 /etc/profile
和 ~/.profile
配置文件。作为 non-login shell
启动时, bash
仅会读取环境变量 ENV
指向的文件。
POSIX 模式
当通过以下方式启动 bash
时:
- 设置
set -o posix
或export POSIXLY_CORRECT=1
bash --posix
bash
会尽可能按照 POSIX
标准进行初始化,仅会读取环境变量 ENV
指向的文件。
远程启动脚本
使用 rshd
远程启动脚本时仅会加载 ~/.bashrc
文件,但要注意的是尽量不要使用 rlogin, telnet, rsh, rcp
等远程命令,因为这些命令会传输未加密的明文信息。如果有远程访问需求尽量使用SSH。
UID 与 EUID 不匹配
如果我们给 bash
设置 set-user-id
标志,那么由于其默认所有者为 root
,当其他非 root
用户运行 bash
时,该进程的 UID
将不等于 EUID
,这种情况下为了保证安全性, bash
在初始化阶段不会加载任何文件。
[!1INFO]
Linux
内核会为每个进程维护task_struct
用于记录进程运行时所需要的信息, 其中UID
(真实用户ID)用于记录创建进程的用户的UID
,EUID
(有效用户ID)用于判断当前进程对文件的访问级别,一般情况下UID = EUID
。如果可执行文件的set-user-id: SUID
位有效(例如:/usr/bin/passwd
的权限为-rwsr-xr-x
,用户级别的x
被替换为s
),表示当该文件被执行时,进程具有文件所有者的权限而不是创建者的权限, 这时EUID
的值为文件所有者的UID
, 而不是进程创建者的UID
.
受限制的 shell
通过 rbash
或 bash --restricted
或 bash -r
启动时会生成功能受限制的shell,具体表现为:
- 不能使用
cd
命令并且命令中不能包含/
- 不能更改
SHELL
、PATH
、ENV
和BASH_ENV
环境变量 source
命令的参数也不能包含带有/
的文件hash –p <path> <name>
用于给路径起别名的命令的参数中也不能包含/
- 初始化时不会导入文件中的函数并且会忽略
SHELLOPTS
- 不能使用重定向
- 不能使用
exec
命令 - 不能使用
enable -f/-d
增加删除命令 - 不能使用
command -p
指定运行命令需要的路径 - 不能主动关闭限制模式
这个功能理论上可以让用户在指定的文件夹内执行指定的文件来完成有限的功能,但是如果环境变量设置不当会导致用户很轻松地就能解除限制:
> rbash
> cd /etc
rbash: cd: restricted
> bash
> cd /etc # 可以成功执行,因为这个时候我们在bash环境中,没有任何限制
一种有效的做法是给新建的用户的能执行的命令作出限制,例如我们可以新建一个只能执行 ftp
命令的 ruser
:
> useradd -s /bin/rbash ruser # 设置用户登录时提供的shell
> chown -R root:ruser /home/ruser/.bashrc /home/ruser/.bash_profile
# 设置root为拥有者,ruser组为组拥有者(新建的ruser默认输入ruser组)
> chmod 640 /home/ruser/.bashrc /home/ruser/.bash_profile
# root可以读写,ruser组里的用户只读,其他用户什么也不能干
> mkdir /home/ruser/bin # 存储用户的可执行文件或链接
> echo "export PATH=/home/ruser/bin" >> /home/ruser/.bash_profile
> ln -s /user/bin/ftp /home/ruser/bin/ftp