浅谈Bash shell的几种运行模式和cron环境变量导致command not found

浅谈Bash shell的几种运行模式和cron环境变量导致command not found

参考

Linux shell脚本编程大全

bash的三种运行模式:

不同模式运行,自然加载的环境变量就不同,command not found 就是因为PATH环境变量不正确导致命令找不到,由于cron是一种非登录非交互模式运行,PATH环境变量只有少量的路径

1.登录(login shell)##

用户登录机器获得的shell自然就是登录和交互的shell,想一想,你是不是需要登入,并且你这个shell是不是执行一条命令,它就给你反馈一个结果。

当登入Linux系统时,bash shell会作为登入shell启动。同时会从下面的5个不同的启动文件读取命令:

  1. /etc/profile
  2. $HOME/.bash_profile
  3. $HOME/.bashrc
  4. $HOME/.bash_login
  5. $HOME/.profile

shell会按照按照下列顺序,运行第一个被找到的文件,余下的则被忽略:

  1. $HOME/.bash_profile
  2. $HOME/.bash_login
  3. $HOME/.profile

CentOS Linux系统中的.bash_profile有一个 . ~/.bashrc 它会去检查是否存在这个文件,如果有先执行这个文件

创建的方式

1. bash -l yourshell.sh
2. #!/bin/bash --login 脚本的开头

2.交互式 (interactive)

  1. 如果你的bash shell不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),那
    么你启动的shell叫作交互式shell。交互式shell不会像登录shell一样运行,但它依然提供了命令行
    提示符来输入命令。

  2. 如果bash是作为交互式shell启动的,它就不会访问/etc/profile文件,只会检查用户HOME目录
    中的.bashrc文件,在这个文件中会调用/etc/bashrc。

  3. .bashrc文件有两个作用:一是查看/etc目录下通用的bashrc文件,二是为用户提供一个定制自
    己的命令别名

创建的方式

  • 当然你可以通过在命令行 bash -i your_script.sh 表示启动交互式shell,确实也会加载交互式shell该加载的文件

3.非交互式shell(interactive)

系统执行shell脚本时用的就是这种shell。不同的地方在于它没有命令行提示符。但是当你在系统上运行脚本时,也许希望能够运行一些特定启动的命令。

  • 为了处理这种情况, bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来查看要执行的启动文件。如果有指定的文件, shell会执行该文件里的命令,这通常包括shell脚本变量设置。

  • 那如果BASH_ENV变量没有设置(通常都没有设置可以通过echo $BASH_ENV 查看), shell脚本到哪里去获得它们的环境变量呢?别忘了有些
    shell脚本是通过启动一个子shell来执行的。子shell可以继承父shell导出过的变量。

  • 举例来说,如果父shell是登录shell,在/etc/profile、 /etc/profile.d/*.sh和$HOME/.bashrc文件中设置并导出了变量,用于执行脚本的子shell就能够继承这些变量。要记住,由父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。对于那些不启动子shell的脚本,变量已经存在于当前shell中了。所以就算没有设置BASH_ENV,也可以使用当前shell的局部变量和全局变量

cron 执行出现command not found 问题:

错误方式:

  1. cron执行的是非交互的,自然也是非登入的定时任务。PATH环境变量就少的可伶,导致出现各种命令找不到。通过上面讲的,是不是以为设置了BASH_ENV变量,让它去找加载这个文件就OK了?毕竟我们登入的shell 也就是加载 /etc/profile ~/.bashrc … 等等。
  2. 或者在你的脚本中加上 source /etc/profile source $HOME/.bashrc 等等
  3. 通过上面的两种方式还是解决不了command not found

原因:

  1. 通过我的不断尝试,在每个配置文件中加入 echo /etc/profile(不同文件名不同) >> /home/splend/env.out 观察发现。通过设置BASH_ENV source等方式,这些文件确实有加载,但是你如果加上echo $PATH >> /home/splend/env.out 会发现,在加载第一个/etc/profile的时候PATH已经是一大串的路径了。

  2. 也就是login shell根本在执行/etc/profile之前 PATH已经是一大堆了。而cron执行的是非登录,非交互的,在执行/etc/profile BASH_ENV指定的也好 source指定的也好,你会发现PATH就两个路径(我的是这样)

解决方式:

  1. 把export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 写入my_env.sh然后在你的脚本开头执行source my_env.sh
  2. 当然也可以把export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin写在你脚本的开头
  3. 注意:如果是export PATH=$PATH这样还是不行的。因为原来的PATH就一两个,你重新导入也没用

判断脚本属于哪一种模式运行

   1.echo $-  如果是himBHs其中出现i说明是交互的
   2.echo $PS1 如果不是空说明是登入的

总结

通过一次运行cron脚本出现的问题,然后翻阅shell脚本编程,外加自己不断傻傻的一个一个文件查看PATH的变化,最后才发现在加载这些对应的文件之前就把PATH限定了,如果后面不直接加入PATH的话还是不行,在不断尝试的过程中,以为应该把这个定时脚本用 bash -li my_script.sh强行指定为登录交互式应该就能和正常登入用的shell一样的PATH了吧,然而并不可以。

你可能感兴趣的:(linux服务器)