Linux下计划任务的crontab命令以及无法执行shell脚本的原因

背景


在上午探索了Windows下时间任务创建运行的可视化界面和Schtasks命令行工具且默默失败后,下午我决定不依不饶地去看一下Linux系统下是怎么创建时间任务的。其实我Linux接触得不多,而且今天也是新接触的crontab命令,所以不免会踩坑踩雷,但是最后还是解决了问题,于是噫吁嚱记录一下。

这里我主要想试验一下这个功能,在我的test.py文件里是一个简单的python命令,里面主要做的事就是向一个网站爬一点数据,由于我想使用这个数据,并且这个数据具有时势性,所以打算在每天固定的某个时间点去重新爬一次(相当于更新数据),与其在python中去写定时器然后一直挂起这个程序我觉得还倒不如在系统的层面上去写,因为这个定时的逻辑和代码的逻辑其实是可以也需要分开的。所以我新装了python3(一般Linux下会自带python,但是版本是2,所以可以装一下高版本的,但是千万别傻乎乎把原来的版本删掉,不然到时候你想救你的系统都不知道怎么办),接着开始去了解crontab。

crontab


crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。
Linux下输入 cat /etc/crontab,这里可以看到后面所需参数的含义,其中user-name可以不写,默认为当前用户,我这里是rootLinux下计划任务的crontab命令以及无法执行shell脚本的原因_第1张图片
Cent7OS 下的部分操作

注意cent下自带crontab

crond管理命令:
	#systemctl status crond  查看运行状态
	#systemctl stop  crond  关闭服务
	#systemctl start  crond  启动服务
	#systemctl restart  crond  重启服务
	#systemctl enable  crond  开机自动启动
	#systemctl is-enable  crond  查看开机自动启动状态
	
crontab命令介绍:
	crontab -u			//指定用户
	crontab -l			//查看用户所有的cron任务
	crontab -e			//创建cron任务
	crontab -r			//删除cron任务
	(还有其他参数可以自行查找)

crond配置格式:
	*分		*时		*日		*月		*周					任务执行命令
	0-59	0-23	1-31	1-12	0-6(0表示周日)		xxx.sh
	
	几个特殊符号的含义:
		"*"代表取值范围内的数字,
		"/"代表"每",
		"-"代表从某个数字到某个数字,
		","分开几个离散的数字
		
	举例说明:
	5	*		*		*		*	date	//每时的第5分执行date命令
	5	5		*		*		*	date	//每天5:05分执行date命令
	5	5		5		*		*	date	//每月的5号5:05分执行date命令
	5	5		5		5		*	date	//每年5月5号5:05分执行date命令
	5	5		*		*		0	date	//每周日5:05分执行date命令
	5	5-19	1-15	1-6		*	date	//每天年的1-6月,每月1-15号,每天5-19点的5分执行date命令
	5	5,8		1,3		1,3,5	*	date	//每年的1,3,5,月,每3,5日,每5,8时的5分钟执行date命令
	*/5			*		*		0	date	//周日每5分钟执行date命令
	

例:创建任务
//本环境下都可以省略-u以及后面的参数
	crontab -e -u test			//指定test用户crond
		*/1 * * * *   date >>/mnt/test.com		//每一份周执行date命令其结果追加到/mnt/test.com
	crontab -l -u test
		*/1 * * * *   date >>/mnt/test.com		//查看当前用户的crond执行定时任务
	crontab -r -u test
		no crontab for test				//当前用户没有创建定时任务
验证方法(查看启动日志与文件)
	tail -f /var/log/conr      				//查看日志文件
	cat /mnt/test.com						//查看文件内容是否有更新
	
(感谢ID为nimeidiecao提供的部分资料)

开始踩坑


首先我确保了一下 test.py 文件确实是可以执行的,然后创建了个简单的shell脚本:

#!/bin/sh
python3 /home/COVID-19/test.py

(提醒一下如果是从Windows直接拷过来的需要处理一下,这是因为不同系统的结束标志不一样,^M这个符号让Linux无法理解,具体可以参考 解决Linux服务器执行出现-bash: ./xxx.sh: /bin/sh^M: bad interpreter: No such file or directory)

这里我们可以把文件的权限rwx提高一下,不管你用可视化传输界面右击更改文件权限也好还是用chmod也好,这样是为了方便我们可以直接键入这个文件就可以执行,不然就需要在前面加上sh 或者 bash。
同时也需要注意一下,为了防止各种由于疏忽造成的bug,最好统一使用绝对路径。
同样我直接运行这个文件也是可以执行的。

我开始用crontab创建时间任务后,神奇的事情发生了

crontab -e
10 18 * * * /home/COVID-19/test.sh
//意思是想让它在待会儿的18:10分(实际是每天)执行这个sh脚本
crontab -l

但是18:10分到了之后啥事也没发生,都说Unix这类操作系统的哲学是只要无事发生就是好事,但是这回确实是什么事情都没有发生,好的那我换种写法:

crontab -e
10 18 * * * python3 /home/COVID-19/test.py
crontab -l

不出意料的,它同样默默失败,在var/log下有些cron开头的log文件应该是一些打印日志,但是里面内容实在太多而且密密麻麻的我就没有细看。

深入踩坑


所以问题到底出在哪里呢?明明一个可以直接执行的文件,为什么到了时间任务下就不成立了呢?所以第一肯定是去怀疑这是我使用crontab命令本身出现的疏忽,所以各种查用法查参数,还是没有发现它的问题,这时我换了种执行命令:

crontab -e
34 18 * * * date >> /home/COVID-19/test.txt
39 18 * * * echo "茶茶" >> /home/COVID-19/cha.txt

然后我惊奇地发现这两句都是可以生效的,都会对应有txt的创建,那这里应该可以说明,并不是我使用crontab这个命令写法的问题。

所以现在的线索是:执行crontab命令和写法没毛病,单独执行sh脚本也没问题,但是它们一组合起来就失效。

原因

crontab执行脚本的时候不会读取用户的环境变量等相关配置

在Linux系统中,使用crontab执行脚本,由于crontab没有环境变量,它是找不到你使用的命令的,需要使用命令的全路径,才可使用命令

解决方案


既然知道了原因,那我们就可以有自己的解决方案了!

  1. crontab执行计划任务时,它并不会从用户的profile文件中读取环境变量,所以会导致命令执行失败。

既然它不读,那我们就让它读取呗!修改我们的sh文件(用vim修改)

#!/bin/sh
--------------------
source /etc/profile
source /etc/profile && python /myfile/myscript.py params
source ~/.bash_profile
--------------------
python3 /home/COVID-19/test.py

加入短线框中任意一句即可,有朋友说如果是对于python文件需要第二句但是亲测第一句就可以了,关于第三句:
.bash_profile会读取.bashrc
.bashrc 会读取 /etc/bashrc
/etc/bashrc 会读取 /etc/profile
Linux下的环境变量基本上就在这里面了~

  1. 我们还可以使用命令路径的全名

这里我们直接抛弃sh文件,直接在crontab中执行python文件,之前执行这个命令是因为我们的command直接用的python3,而上面提到crontab是不会去拿环境变量的,那么我们创建的软连接肯定也是失效的,所以我们直接用创建的地址:

ln -s /usr/local/python363/bin/python3.6 /usr/local/bin/python //软连接

crontab -e
1 20 * * * /usr/local/bin/python3 /home/COVID-19/test.py

或者更直接一点,直接使用源程序文件地址:

crontab -e
1 22 * * * /usr/local/python363/bin/python3.6 /home/COVID-19/test.py
  1. export $PATH

我们也可以在脚本开头export一下路径,不过这种方法我还没试,而且确实也没上面的方法来得直接。

迷糊复盘


以前写东西的时候就有个需求,就是一个为了日更所以每天都需要执行一遍的脚本,那个时候觉得应该在操作系统层面上会有这么一个timer,但是当时没有深入去查阅资料而是采用间接甚至有点蠢的方法来实现,这回算是体会了一把timer的命令。
说实话我对于Linux的极不熟悉也很拖慢找bug的进度,但是总归是找到了,所以赶紧趁热打铁地记下来以免以后重蹈覆辙。现在是晚上12点整,希望以后都可以保持这样一份追求的心态吧!
(天哪我这说的都是什么骚话,果然半夜让人迷糊)

你可能感兴趣的:(linux,python,shell,centos,运维)