Linux环境变量问题汇总

简介: ### 测试好的脚本放到 crontab 里就报错: 找不到命令 写好一个脚本,测试没有问题,然后放到crontab 想要定时执行,但是总是报错,去看日志的话显示某些命令找不到,这种一般都是因为PATH环境变量变了导致的 自己在shell命令行下测试的时候当前环境变量就是这个用户的环境变...

测试好的脚本放到 crontab 里就报错: 找不到命令

写好一个脚本,测试没有问题,然后放到crontab 想要定时执行,但是总是报错,去看日志的话显示某些命令找不到,这种一般都是因为PATH环境变量变了导致的

自己在shell命令行下测试的时候当前环境变量就是这个用户的环境变量,可以通过命令:env 看到,脚本放到crontab 里面后一般都加了sudo 这个时候 env 变了。比如你可以在命令行下执行 env 和 sudo env 比较一下就发现他们很不一样

为了解决这个问题 sudo有一个参数 -E (--preserver-env)就是为了解决这个问题的。

这个时候再比较一下

  • env
  • sudo env
  • sudo -E env

大概就能理解这里的区别了

同样一个命令ssh执行不了, 报找不到命令

比如:

ssh user@ip " ip a " 报错: bash: ip: command not found

但是你要是先执行 ssh user@ip 连上服务器后,再执行 ip a 就可以,这里是同一个命令通过两种不同的方式但是环境变量也不一样了。

同样想要解决这个问题的话可以先 ssh 连上服务器,再执行 which ip ; env | grep PATH

$ which ip
/usr/sbin/ip
$ env | grep PATH
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin

很明显这里因为 ip在/usr/sbin下,而/usr/sbin又在PATH变量中,所以可以找到。

那么接下来我们看看

$ssh user@ip "env | grep PATH"
PATH=/usr/local/bin:/usr/bin

很明显这里的PATH比上面的PATH短了一截,/usr/sbin也没有在里面,所以/usr/sbin 下的ip命令自然也找不到了,这里虽然都是同一个用户,但是他们的环境变量还不一样,有点出乎我的意料之外。

主要原因是我们的shell 分为login shell 和 no-login shell , 先ssh 登陆上去再执行命令就是一个login shell,Linux要为这个终端分配资源。

而下面的直接在ssh 里面放执行命令实际上就不需要login,所以这是一个no-login shell.

login shell 和 no-login shell又有什么区别呢?

  • login shell加载环境变量的顺序是:① /etc/profile ② ~/.bash_profile ③ ~/.bashrc ④ /etc/bashrc
  • 而non-login shell加载环境变量的顺序是: ① ~/.bashrc ② /etc/bashrc

也就是nog-login少了前面两步,我们先看后面两步。

下面是一个 .bashrc 的内容:

[ ~]$ cat .bashrc 
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

基本没有什么内容,它主要是去加载 /etc/bashrc 而他里面也没有看到sbin相关的东西

那我们再看non-login少的两步: ① /etc/profile ② ~/.bash_profile

cat /etc/profile :


if [ "$EUID" = "0" ]; then
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
fi

这几行代码就是把 /usr/sbin 添加到 PATH 变量中,正是他们的区别决定了这里的环境变量不一样。

用一张图来表述他们的结构,箭头代表加载顺序,红框代表不同的shell的初始入口
image.png

像 ansible 这种自动化工具,或者我们自己写的自动化脚本,底层通过ssh这种non-login的方式来执行的话,那么都有可能碰到这个问题,如何修复呢?

在 /etc/profile.d/ 下创建一个文件:/etc/profile.d/my_bashenv.sh 内容如下:


$cat /etc/profile.d/my_bashenv.sh 

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
  PATH=$PATH:$1
   else
  PATH=$1:$PATH
   fi
fi
}
 
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local/sbin
pathmunge /usr/local/bin
pathmunge /usr/X11R6/bin after
 
unset pathmunge

complete -cf sudo
 

alias chgrp='chgrp --preserve-root'
alias chown='chown --preserve-root'
alias chmod='chmod --preserve-root'
alias rm='rm -i --preserve-root'
 

HISTTIMEFORMAT='[%F %T] '
HISTSIZE=1000

export EDITOR=vim


export PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\H\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m\n\$'

通过前面我们可以看到 /etc/bashrc 总是会去加载 /etc/profile.d/ 下的所有 *.sh 文件,同时我们还可以在这个文件中修改我们喜欢的 shell 配色方案和环境变量等等

BASH

1、交互式的登录shell (bash –il xxx.sh)
载入的信息:
/etc/profile
~/.bash_profile( -> ~/.bashrc -> /etc/bashrc)
~/.bash_login
~/.profile

2、非交互式的登录shell (bash –l xxx.sh)
载入的信息:
/etc/profile
~/.bash_profile ( -> ~/.bashrc -> /etc/bashrc)
~/.bash_login
~/.profile
$BASH_ENV

3、交互式的非登录shell (bash –i xxx.sh)
载入的信息:
~/.bashrc ( -> /etc/bashrc)

4、非交互式的非登录shell (bash xxx.sh)
载入的信息:
$BASH_ENV

SH

1、交互式的登录shell
载入的信息:
/etc/profile
~/.profile

2、非交互式的登录shell
载入的信息:
/etc/profile
~/.profile

3、交互式的非登录shell
载入的信息:
$ENV

练习验证一下bash、sh和login、non-login

  • sudo ll 或者 sudo cd 是不是都报找不到命令
  • 先sudo bash 然后执行 ll或者cd就可以了
  • 先sudo sh 然后执行 ll或者cd还是报找不到命令
  • sudo env | grep PATH 然后 sudo bash 后再执行 env | grep PATH 看到的PATH环境变量不一样了

4、非交互式的非登录shell
载入的信息:
nothing

export命令的作用

Linux 中export是一种命令工具通过export命令把shell变量中包含的用户变量导入给子程序.默认情况下子程序仅会继承父程序的环境变量,子程序不会继承父程序的自定义变量,所以需要export让父程序中的自定义变量变成环境变量,然后子程序就能继承过来了。

我们来看一个例子, 有一个变量,名字 abc 内容123 如果没有export ,那么通过bash创建一个新的shell(新shell是之前bash的子程序),在新的shell里面就没有abc这个变量, export之后在新的 shell 里面才可以看到这个变量,但是退出重新login后(产生了一个新的bash,只会加载env)abc变量都不在了

$echo $abc


$abc="123"


$echo $abc
123

$bash

$echo $abc


$exit
exit

$export abc

$echo $abc
123

$bash

$echo $abc
123

一些常见问题

执行好好地shell 脚本换台服务器就:source: not found

source 是bash的一个内建命令(所以你找不到一个/bin/source 这样的可执行文件),也就是他是bash自带的,如果我们执行脚本是这样: sh shell.sh 而shell.sh中用到了source命令的话就会报 source: not found

这是因为bash 和 sh是两个东西,sh是 POSIX shell,你可以把它看成是一个兼容某个规范的shell,而bash是 Bourne-Again shell script, bash是 POSIX shell的扩展,就是bash支持所有符合POSIX shell的规范,但是反过来就不一定了,而这里的 source 恰好就是 bash内建的,不符合 POSIX shell的规范(POSIX shell 中用 . 来代替source)

在centos执行好好的脚本放到Ubuntu上就不行了,报语法错误

同上,如果到ubuntu上用 bash shell.sh是可以的,但是sh shell.sh就报语法错误,但是在centos上执行:sh或者bash shell.sh 都可以通过。 在centos上执行 ls -lh /usr/bin/sh 可以看到 /usr/bin/sh link到了 /usr/bin/bash 也就是sh等同于bash,所以都可以通过不足为奇。

但是在ubuntu上执行 ls -lh /usr/bin/sh 可以看到 /usr/bin/sh link到了 /usr/bin/dash , 这就是为什么ubuntu上会报错

source shell.sh 和 bash shell.sh以及 ./shell.sh的区别

source shell.sh就在本shell中展开执行
bash shell.sh表示在本shell启动一个子程序(bash),在子程序中执行 shell.sh (shell.sh中产生的一些环境变量就没法带回父shell进程了), 有读 shell.sh 权限就可以执行
./shell.sh 跟bash shell.sh类似,但是必须要求shell.sh有rx权限,然后根据shell.sh前面的 #! 后面的指示来确定用bash还是sh

$cat test.sh 
echo 

$$ $echo $$

2299

$source test.sh 
2299

$bash test.sh 
4037

$./test.sh 
4040

如上实例,只有source的时候进程ID和bash进程ID一样,其它方式都创建了一个新的bash进程,所以ID也变了

参考文章:

关于ansible远程执行的环境变量问题

Bash和Sh的区别

什么是交互式登录 Shell what-is-interactive-and-login-shell

Shell 默认选项 himBH 的解释

useful-documents-about-shell

目录
相关文章
|
7天前
|
Ubuntu Linux Shell
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
(已成功解决)Linux环境报错—bash: wget: command not found;常见Linux发行版本,Linux中yum、rpm、apt-get、wget的区别;Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
136 68
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
|
2月前
|
安全 Linux 网络安全
Linux环境中安装和使用Paramiko
Linux环境中安装和使用Paramiko
169 12
|
2月前
|
Java 关系型数据库 MySQL
Linux环境
Linux环境
41 5
|
3天前
|
消息中间件 Java Kafka
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
本文介绍了Kafka集群的搭建过程,涵盖从虚拟机安装到集群测试的详细步骤。首先规划了集群架构,包括三台Kafka Broker节点,并说明了分布式环境下的服务进程配置。接着,通过VMware导入模板机并克隆出三台虚拟机(kafka-broker1、kafka-broker2、kafka-broker3),分别设置IP地址和主机名。随后,依次安装JDK、ZooKeeper和Kafka,并配置相应的环境变量与启动脚本,确保各组件能正常运行。最后,通过编写启停脚本简化集群的操作流程,并对集群进行测试,验证其功能完整性。整个过程强调了自动化脚本的应用,提高了部署效率。
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
|
1月前
|
缓存 Ubuntu Linux
Linux环境下测试服务器的DDR5内存性能
通过使用 `memtester`和 `sysbench`等工具,可以有效地测试Linux环境下服务器的DDR5内存性能。这些工具不仅可以评估内存的读写速度,还可以检测内存中的潜在问题,帮助确保系统的稳定性和性能。通过合理配置和使用这些工具,系统管理员可以深入了解服务器内存的性能状况,为系统优化提供数据支持。
38 4
|
2月前
|
Web App开发 搜索推荐 Unix
Linux系统之MobaXterm远程连接centos的GNOME桌面环境
【10月更文挑战第21天】Linux系统之MobaXterm远程连接centos的GNOME桌面环境
488 4
Linux系统之MobaXterm远程连接centos的GNOME桌面环境
|
1月前
|
存储 运维 Linux
如何在 Linux 系统中使用 envsubst 命令替换环境变量?
`envsubst` 是 Linux 系统中用于替换文本中环境变量值的实用工具。本文分三部分介绍其工作原理、使用方法及实际应用,包括配置文件替换、脚本执行中环境变量替换和动态生成文件等场景,帮助用户高效利用 `envsubst` 进行开发和运维工作。
65 4
|
1月前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
114 3
|
1月前
|
监控 关系型数据库 MySQL
Linux环境下MySQL数据库自动定时备份策略
在Linux环境下,MySQL数据库的自动定时备份是确保数据安全和可靠性的重要措施。通过设置定时任务,我们可以每天自动执行数据库备份,从而减少人为错误和提高数据恢复的效率。本文将详细介绍如何在Linux下实现MySQL数据库的自动定时备份。
51 3
|
2月前
|
Ubuntu 应用服务中间件 Linux
Linux下搭建Nginx环境的搭建
Linux下搭建Nginx环境的搭建
76 6