jenkins执⾏shell读不到环境变量问题
从系列中,了解到jenkins执⾏shell的原理。在使⽤jenkins过程中,发现执⾏shell读取不到/etc/profile以及⽤户下.bash_profile设置的环境变量。环境:Red Hat Enterprise 5.5
根据jenkins-core项⽬Shell.java的buildCommandLine⽅法
public String
[]
buildCommandLine
(FilePath script
)
{
if
(command
.
startsWith
(
"#!"
))
{
// interpreter override
int end
= command
.
indexOf
(
'\n'
);
if
(end
<
)  end
=command
.
length
();
List
<String
> args
=
new ArrayList
<String
>();
args
.
addAll
(Arrays
.
asList
(Util
.
tokenize
(command
.
substring
(
,end
).
trim
())));
args
.
add
(script
.
getRemote
());
args
.
set
(
,args
.
get
(
).
substring
(
2
));
// trim off "#!"
return args
.
toArray
(
new String
[args
.
size
()]);
}
else
return
new String
[]
{ getDescriptor
().
getShellOrDefault
(script
.
getChannel
()),
"-xe"
,
script
.
getRemote
()};
}
在默认的情况下,执⾏shell会在节点上tmp⽬录⽣成类似hudson224519953209659762.sh(后⾯数字根据规则⽣成),具体执⾏的命令如:
/bin/sh -xe /tmp/hudson224519953209659762.sh。
如果Execute Shell⾥⾯具体命令为以下内容:
#!/bin/bash +x
...
...
那么根据上⾯代码,具体执⾏的命令就会变成/bin/bash +x /tmp/hudson224519953209659762.sh
知道jenkins执⾏shell的原理后,接下来我们要谈谈交互式和⾮交互式shell、登录和⾮登录shell之间的区别
什么是交互式shell(interactive shell)和⾮交互式shell(non-interactive shell)
交互式的shell会有⼀个输⼊提⽰符,并且它的标准输⼊、输出和错误输出都会显⽰在控制台上。这种模式也是⼤多数⽤户⾮常熟悉的:登录、执⾏⼀些命令、退出。当你退出后,shell也终⽌了。
⾮交互式shell是bash script.sh这类的shell。在这种模式下,shell不与你进⾏交互,⽽是读取存放在⽂件中的命令,并且执⾏它们。当它读到⽂件的结尾EOF,shell也就终⽌了。
什么是登录式shell(login shell)和⾮登陆式shell(no-login shell)
需要输⼊⽤户名和密码的shell就是登陆式shell。因此通常不管以何种⽅式登陆机器后⽤户获得的第⼀个shell就是login shell。不输⼊密码的ssh是公钥打通的,某种意义上说也是输⼊密码的。
⾮登陆式的就是在登陆后启动bash等,即不是远程登陆到主机这种。
通过man bash了解login shell和interactive shell,如下
INVOCATION
A login shell is one whose first character of argument zero is a -, or one started with the --login option.
An interactive shell is one started without non-option arguments 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.
The following paragraphs describe how bash executes its startup files.  If any of the files exist but cannot be
read, bash reports an error.  Tildes are expanded in file names as described below under Tilde Expansion in the
EXPANSION section.
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.
When a login shell exits, bash reads and executes commands from the file ~/.bash_logout,
if it exists.
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.
When  bash is started non-interactively, to run a shell script,
for example, it looks
for the variable BASH_ENV
in the environment, expands its value
if it appears there, and uses the expanded value as the name of a file to
read and execute.  Bash behaves as
if the following
command were executed:
if
[ -n
"
$BA
SH_ENV"
];
then .
"
$BA
SH_ENV";
fi
but the value of the PATH variable is not used to search
for the file name.
对man bash解读:
如果⼀个bash是交互式登录Shell或者使⽤--login参数的⾮交互式Shell。⾸先会执⾏/etc/profile⽂件。然
后按顺序查~/.bash_profile, ~/.bash_login,~/.profile,这三个⽂件谁存在并且有读权限就执⾏谁,然后后⾯的就不会再执⾏。可以通过指定--noprofile参数来禁⽌这种默认⾏为。当登录Shell退出之后,bash会读取~/.bash_logout⽂件并执⾏。
如果 ~/.bash_profile⽂件存在的话,⼀般还会执⾏ ~/.bashrc⽂件。因为在~/.bash_profile⽂件中⼀般会有下⾯的代码:
if
[ -f ~/.bashrc
];
then
. ~/.bashrc
fi
~/.bashrc中,⼀般还会有以下代码:
if
[ -f /etc/bashrc
];
then
. /etc/bashrc
fi
所以执⾏顺序为:
/etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc -> /etc/bashrc -> ~/.bash_logout
如果是⼀个交互式⾮登录Shell,bash会读取~/.bashrc⽂件。同时,可以指定--norc参数来禁⽌该⾏为,或者通过--rcfile指定其它⽂件。
如果是⼀个⾮交互式⾮登录Shell,⽐如运⾏⼀个Shell脚本,它会在环境查BASH_ENV变量。
通过上⾯的分析,对于常⽤环境变量设置⽂件,整理出如下加载情况表:
⽂件⾮交互+登陆式交互+登陆式交互+⾮登陆式⾮交互+⾮登陆式
/etc/profile加载加载
/etc/bashrc加载加载
~/.bash_profile加载加载
~/.bashrc加载加载加载
BASH_ENV加载
执⾏脚本,如bash script.sh是属于non-login + non-interactive
所以jenkins默认情况下/bin/sh -xe /tmp/hudson224519953209659762.sh 是属于non-login + non-interactive
解决⽅案
通过man bash可知:
OPTIONS
In  addition  to  the  single-character shell options documented in the description of the
set
builtin command,
bash interprets the following options when it is invoked:
-c string If the -c option is present,
then commands are
java环境变量csdn
read from string.  If there are  arguments  after  the
string, they are assigned to the positional parameters, starting with
$0.
-i        If the -i option is present, the shell is interactive.
-l        Make bash act as
if it had been invoked as a login shell
(see INVOCATION below
).
-r        If the -r option is present, the shell becomes restricted
(see RESTRICTED SHELL below
).
-s        If  the  -s  option  is present, or
if no arguments remain after option processing,
then commands are
read from the standard input.  This option allows the positional parameters to be
set  when  invoking
an interactive shell.
-D        A  list  of all double-quoted strings preceded by $ is printed on the standard output.  These are the
strings that are subject to language translation when the current locale is not  C  or  POSIX.  This
implies the -n option; no commands will be executed.
可以通过-i参数和-l参数让bash为login shell and interactive shell,就可以读取/etc/profile和~/.bash_profile等⽂件。即在jenkins Execute Shell⾥可以这么写
#!/bin/bash -ilex
...
...
对于e参数表⽰⼀旦出错,就退出当前的shell,x参数表⽰可以显⽰所执⾏的每⼀条命令

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。