Go ssh连接的env

Go ssh连接的env


  
  

env找不到

  
最近使用 Go 语言做了一个 ssh CentOS 远程执行 bash 命令的功能。被运行的命令使用到了 Java,但是报出了如下错误:

Error: JAVA_HOME is not set and java could not be found in PATH.

  
手工登录到执行机器上,查看 env ,却看到这个环境变量是存在的:

[root@VM_21_126_centos ~]# env | grep JAVA_HOME
JAVA_HOME=/usr/local/matrix

  

这个环境变量仅仅定义在 /etc/profile 中,如果程序报找不到其中已经定义的环境变量,只能说明这个文件并没有被执行过。

  

/etc/profile 运行时机

  
这里都基于默认的 bash 进行说明。
  

可视化连接情况

  
这里使用 bash 内建命令 shopt 来查看连接属性。
  
当 ssh 可视化客户端连接后,连接属性如下:

[root@VM_28_182_centos ~]# shopt login_shell && echo "JAVA_HOME="$JAVA_HOME
login_shell     on
JAVA_HOME=/usr/local/matrix

login_shell on 可知,其为 interactive login shell,它可以获取到 JAVA_HOME。

  

这里从 man bash 可以得到如下说明:

When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.

一个 interactive login shell 初始化 env 时,会运行 /etc/profile,所以手工登录机器查看 env, 环境变量会按照预期的设置。

  

Go ssh 连接情况

  
Go 的 ssh 连接执行的简要代码如下:

client, err := ssh.Dial("tcp", addr, config)
session, err := client.NewSession()
session.Run(cmd)

  
设置 cmd

cmd = shopt | grep login && echo "JAVA_HOME=" $JAVA_HOME

结果如下:

login_shell     off
JAVA_HOME=

login_shell off 可知,这个连接不是 interactive login shell,它没有获取到 JAVA_HOME。

  

这里从 man bash 可以得到如下说明:

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists. This may be inhibited by using the --norc option. The --rcfile file option will force bash to read and execute commands from file instead of ~/.bashrc.

一个非 interactive login shell 初始化 env 时,会运行 ~/.bashrc,但不会运行 /etc/profile, 而 JAVA_HOME 仅存在于后者内,所以此 env 中没有 JAVA_HOME。

  

修改 Go 程序

  
如果 Go ssh 程序需要 /etc/profile 中的环境变量,那该怎么办呢?
  
只需要在 cmd 中添加运行 /etc/profile 处理即可 。

cmd = source /etc/profile && shopt | grep login && echo "JAVA_HOME=" $JAVA_HOME

运行结果如下:

login_shell     off
JAVA_HOME= /usr/local/matrix

此时可知,其仍然不是 interactive login shell,但是它已经可以获取到 JAVA_HOME 了。

  

控制login_shell

  
在使用可视化工具连接 ssh 服务时,任意窗口的第一个连接基本都是 interactive login shell ,它会运行 /etc/profile 来初始化环境变量。

查看属性如下:

[root@VM_28_182_centos ~]# shopt | grep login && echo "JAVA_HOME=" $JAVA_HOME
login_shell     on
JAVA_HOME= /usr/local/matrix

  

当在此窗口执行子程序时,就会生成一个次级的 bash 进程,这个次级进程的 bash 默认就不是 interactive login shell 了,不会再次运行 /etc/profile 。它的所有环境变量都是继承自父 bash 进程的已经生效的环境变量。
  
如果父进程初始化结束后,手动修改了 /etc/profile , 但是没有执行 source /etc/profile, 那么父进程的环境变量列表肯定不会反映出这些修改。直接启动子进程,在子进程中也不会生效。

  

下面看下在窗口中打开新 bash 进程的例子。

[root@VM_28_182_centos ~]# bash  // 打开新的 bash 进程

[root@VM_28_182_centos ~]# shopt | grep login && echo "JAVA_HOME=" $JAVA_HOME
login_shell     off
JAVA_HOME= /usr/local/matrix

可以看到 login_shell 已经为 off 状态,JAVA_HOME 因为继承的关系,还是可以获取到。

  

如果想要打开一个 interactive login shell子进程呢?
  
bash 提供了 -l--login 选项来满足这个需求。

[root@VM_28_182_centos ~]# bash -l
[root@VM_28_182_centos ~]# shopt | grep login && echo "JAVA_HOME=" $JAVA_HOME
login_shell     on
JAVA_HOME= /usr/local/matrix

可以看到 login_shell 已经为 on 了。

  
  

阅读感受肿么样?

平均分: / 5. 打分次数:

发表评论

电子邮件地址不会被公开。 必填项已用*标注