Bash 初始化行为

基本概念

登录Login) shell

A login shell is one whose first character of argument zero is a -, or one started with the –login option.

登录系统时的 shell,作为登录系统的一部分运行,通常用于环境变量的配置.

用户登陆后运行的其他任何 shell,被称为非登录Non-login)shell.

交互式Interactive)shell

An interactive shell is one started without non-option arguments (unless -s is specified) and without the -c option whose standard input and error are both connected to terminals (as determined by isatty(3)), or one started with the -i option. PS1 is set and $- includes i if bash is interactive, allowing a shell script or a startup file to test this state.

可以进行命令交互的 shell,交互式 shell 通常从用户终端读取和写入.

相反,脚本或者其他进程启动的、不可交互的被称为非交互式Non-interactive)shell.

远程Remote)shell

Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If Bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable. It will not do this if invoked as sh. The --norc option may be used to inhibit this behavior, and the --rcfile option may be used to force another file to be read, but neither rshd nor sshd generally invoke the shell with those options or allow them to be specified.

远程 shell 指通过网络远程启动 shell,它是非登录非交互式的.

.bash_profile | .bash_login | .profile

在登录 shell 中,Bash 首先查找 /etc/profile 文件,加载全局环境配置.

然后 Bash 会在家目录中按顺序查找 .bash_profile.bash_login.profile,并仅执行第一个可读文件.

.bashrc

.bashrc 是 Bash shell 的配置文件,一般用于存放别名或者函数.

为了保证用户环境的一致性,大部分发行版的 .profile 都会默认引用 .bashrc.

Bash 行为参考

L I R inh p rc Example
- ssh host.com (sshd sets argv[0]=”-bash”)
- ssh host.com </dev/null (sshd sets argv[0]=”-bash”)
bash -ibash on tty
bash hello.shbash -c echo foo,
ssh host.com 'echo $-' (ssh runs bash -c 'echo $-')
  • ‘•’ = 是; blank = 否; ‘-‘ = 不重要.
  • inh: 从父进程继承的环境.
  • p: 加载 /etc/profile 和 ~/.bash_profile~/.bash_login~/.profile 顺序中的第一个.
  • rc: 加载 /etc/bash.bashrc 和 ~/.bashrc

补充说明

远程 shell

作为一种特殊情况,远程 shell 无论如何都会加载 .bashrc.

大多数发行版的 .bashrc 都会在开头判断避免被加载,如:

1
2
# If not running interactively, don't do anything
[ -z "$PS1" ] && return

或者

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

远程文件传输失败

对于使用远程 shell 传输数据的程序(例如 rcp 、 scp 和 sftp )来说,远程用户的 .bashrc 会被加载,而因为 scpsftp 有自己用于交换传输文件信息协议,.bashrc 中输出的任何意外文本都会被错误引入而影响传输.

交互式环境检查

echo $- 的返回中包含 i 的话说明当前为交互式 shell.

1
2
$ echo $-
himBHs

管道(og-apus)

1
2
3
4
client, err = ssh.Dial("tcp", net.JoinHostPort(sshConf.Server, sshConf.Port), remoteConfig)
client.NewSession()
...
session.Start(command)

在 go 中通过 ssh 建立的 session 本身是交互式的,但是直接执行的命令都属于远程 shell,所以会加载用户环境中 .bashrc 文件中交互式判断之前的内容.

webshell 属于 tty,所以是交互式的.

Slurm

How does Slurm establish the environment for my job?

Slurm processes are not run under a shell, but directly exec’ed by the slurmd daemon (assuming srun is used to launch the processes). The environment variables in effect at the time the srun command is executed are propagated to the spawned processes. The ~/.profile and ~/.bashrc scripts are not executed as part of the process launch. You can also look at the –export option of srun and sbatch. See man pages for details.

Slurm 进程不在 shell 下运行,而是由 slurmd 守护进程直接执行,通过 srun 运行作业时直接继承执行时的环境,既不会加载 .profile 也不会加载 .bashrc .

ref: slurm FAQ

通过管道提交 Slurm 作业

由于管道提交的作业是非交互式的,且 Slurm 会继承此环境,在作业模板中 source ~/.bashrc 大多情况下也会被判断拦截,所以用户作业所需要的环境初始化过程(如 conda init)都需要在作业模板中执行一次.

Simple Hack

1
# ssh remote_node "bash -l -c 'which mmsetquota'"

bash -l -c 可以模拟登录流程加载profile.d中的环境变量(仍然会跳过.bashrc中的有小部分,因为子 shell 是非交互式的)

参考资料

https://github.com/0cjs/sedoc/blob/master/lang/bash/init.md