RHCS - Atd 一次性的定时执行命令
安装命令
默认 Ubuntu 或 Debian 是没有预装 at
命令的,我们需要先用以下命令安装,其它的操作系统或发行版,可遵照 Command not found 网站的说明进行安装。
$ sudo apt install at
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
at
0 upgraded, 1 newly installed, 0 to remove and 174 not upgraded.
Need to get 39.5 kB of archives.
After this operation, 155 kB of additional disk space will be used.
Get:1 http://mirrors.aliyun.com/ubuntu bionic/main amd64 at amd64 3.1.20-3.1ubuntu2 [39.5 kB]
Fetched 39.5 kB in 0s (237 kB/s)
Selecting previously unselected package at.
(Reading database ... 423876 files and directories currently installed.)
Preparing to unpack .../at_3.1.20-3.1ubuntu2_amd64.deb ...
Unpacking at (3.1.20-3.1ubuntu2) ...
Setting up at (3.1.20-3.1ubuntu2) ...
Created symlink /etc/systemd/system/multi-user.target.wants/atd.service → /lib/systemd/system/atd.service.
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for ureadahead (0.100.0-21) ...
Processing triggers for systemd (237-3ubuntu10.54) ...
检查 Atd 服务状态
At
命令需要有后台服务 atd
作支撑,其提供时间解析、存储、定时执行、查询、删除等功能接口给 at
调用。
因此,在安装完毕后,让我们检查一下 atd
服务启动状态:
$ sudo systemctl status atd
● atd.service - Deferred execution scheduler
Loaded: loaded (/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2022-09-04 21:55:04 CST; 57s ago
Docs: man:atd(8)
Main PID: 13929 (atd)
Tasks: 1 (limit: 6143)
CGroup: /system.slice/atd.service
└─13929 /usr/sbin/atd -f
Sep 04 21:55:04 dell-7920-tower systemd[1]: Started Deferred execution scheduler.
从服务日志可以看到,At Daemon 的延迟(定时)执行的调度器已经启动,并且已经设置了开机自启动。
设置开机自启动
如果在上一步没有显示开机自启动,可用下面的命令启用:
sudo systemctl enable --now atd
使用方法
命令用法
$ which at
/usr/bin/at
$ at --help
Usage: at [-V] [-q x] [-f file] [-mMlbv] timespec ...
at [-V] [-q x] [-f file] [-mMlbv] -t time
at -c job ...
atq [-V] [-q x]
at [ -rd ] job ...
atrm [-V] job ...
batch
不带参数
$ at
Garbled time
At 提示我们未指定时间或时间有误,接下来我们在命令后面附加时间参数。
指定时间提交作业
先看一眼时钟,确认当前时间。
$ date
Sun Sep 4 21:58:12 CST 2022
添加一条命令,它将在晚上十点钟(绝对时间),写入当前时间日期到主目录下 .myjobs
文件中。
(base) muwaii@dell-7920-tower:~$ at 10pm
warning: commands will be executed using /bin/sh
at> echo `date` > /home/muwaii/.myjobs
at> <EOT>
job 1 at Sun Sep 4 22:00:00 2022
在添加命令时,一次性可以输入多个命令或脚本地址,最后需要以 ctrl+d
作为结尾,告诉 at
结束输入并保存定时作业。如果输入的命令错了,可按 ctrl+c
取消本次输入,重新用 at
命令输入。
注:
At
命令中对于时间的格式是有要求的,大体上分为绝对时间和相对时间,在正文中仅介绍常见的格式,关于完整的定义读者可参见附录。
查询已提交的作业
输出已经提交了,但是还没有执行的作业列表。注意,已经执行过的作业不会出现在这里!
$ atq
1 Sun Sep 4 22:00:00 2022 a muwaii
输出的内容,从左到右,分别是:
作业编号 | 待执行时间 | 任务队列名称 | 提交该命令的用户名 |
---|---|---|---|
1 | Sun Sep 4 22:00:00 2022 | a | muwaii |
可通过
-q
参数来指定加入的队列,候选值为 26 个字母之一。
查看命令的上下文
查看执行该命令的上下文环境,内容比较长,一般看最后一段中待执行的命令明文即可。前面的运行检查、环境变量等,可以先不细究,留着在命令执行出错或未符合预期时排错使用。
$ at -c 1
#!/bin/sh
# atrun uid=1001 gid=1001
# mail muwaii 0
umask 2
CUDNN_INSTALL_DIR=/usr/local/cuda; export CUDNN_INSTALL_DIR
CONDA_SHLVL=1; export CONDA_SHLVL
LIBVA_DRIVER_NAME=iHD; export LIBVA_DRIVER_NAME
LC_ALL=en_US.utf8; export LC_ALL
CONDA_EXE=/home/muwaii/anaconda3/bin/conda; export CONDA_EXE
SSH_CONNECTION=10.1.10.210\ 63875\ 10.1.10.251\ 22; export SSH_CONNECTION
VIRTUALENVWRAPPER_WORKON_CD=1; export VIRTUALENVWRAPPER_WORKON_CD
LIBVA_DRIVERS_PATH=/opt/intel/mediasdk/lib64; export LIBVA_DRIVERS_PATH
VIRTUALENVWRAPPER_HOOK_DIR=/home/.envs; export VIRTUALENVWRAPPER_HOOK_DIR
LANG=en_US.utf8; export LANG
WORKON_HOME=/home/.envs; export WORKON_HOME
CONDA_PREFIX=/home/muwaii/anaconda3; export CONDA_PREFIX
NOCONDA_PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin; export NOCONDA_PATH
TR_PATH=/home/muwaii/tensorrt/TensorRT-7.0.0.11; export TR_PATH
_CE_M=; export _CE_M
XDG_SESSION_ID=489; export XDG_SESSION_ID
USER=muwaii; export USER
VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv; export VIRTUALENVWRAPPER_VIRTUALENV
PWD=/home/muwaii; export PWD
HOME=/home/muwaii; export HOME
CONDA_PYTHON_EXE=/home/muwaii/anaconda3/bin/python; export CONDA_PYTHON_EXE
SSH_CLIENT=10.1.10.210\ 63875\ 22; export SSH_CLIENT
CUDA_HOME=/usr/local/cuda; export CUDA_HOME
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop; export XDG_DATA_DIRS
OPENNI2_REDIST=/home/muwaii/pcl/projects/3dReconstruction/OpenNI_2.3.0.63/Linux/OpenNI-Linux-x64-2.3.0.63/Redist; export OPENNI2_REDIST
_CE_CONDA=; export _CE_CONDA
OPENNI2_INCLUDE=/home/muwaii/pcl/projects/3dReconstruction/OpenNI_2.3.0.63/Linux/OpenNI-Linux-x64-2.3.0.63/Include; export OPENNI2_INCLUDE
CONDA_PROMPT_MODIFIER=\(base\)\ ; export CONDA_PROMPT_MODIFIER
SSH_TTY=/dev/pts/6; export SSH_TTY
VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3.8; export VIRTUALENVWRAPPER_PYTHON
MAIL=/var/mail/muwaii; export MAIL
MFX_HOME=/opt/intel/mediasdk; export MFX_HOME
VIRTUALENVWRAPPER_SCRIPT=/usr/local/bin/virtualenvwrapper.sh; export VIRTUALENVWRAPPER_SCRIPT
SHLVL=1; export SHLVL
LANGUAGE=en_US.utf8; export LANGUAGE
LOGNAME=muwaii; export LOGNAME
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus; export DBUS_SESSION_BUS_ADDRESS
XDG_RUNTIME_DIR=/run/user/1001; export XDG_RUNTIME_DIR
VIRTUALENVWRAPPER_PROJECT_FILENAME=.project; export VIRTUALENVWRAPPER_PROJECT_FILENAME
PATH=/home/muwaii/anaconda3/bin:/home/muwaii/anaconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/muwaii/.dotnet/tools:/usr/local/cuda/bin:/home/muwaii/tensorrt/TensorRT-7.0.0.11/bin:/usr/local/bin/:/home/muwaii/.local/bin; export PATH
PKG_CONFIG_PATH=:/usr/local/lib/pkgconfig; export PKG_CONFIG_PATH
CONDA_DEFAULT_ENV=base; export CONDA_DEFAULT_ENV
CUDA_INSTALL_DIR=/usr/local/cuda; export CUDA_INSTALL_DIR
cd /home/muwaii || {
echo 'Execution directory inaccessible' >&2
exit 1
}
echo `date` > /home/muwaii/.myjobs
删除作业
在定时的一次性任务还没有执行/触发之前,我们可以将其删除。
$ at noon + 5 MINUTE
warning: commands will be executed using /bin/sh
at> echo `ssh-agent -s` > /home/muwaii/.ssh_agents
at> <EOT>
job 2 at Mon Sep 5 12:05:00 2022
$ atq
2 Mon Sep 5 12:05:00 2022 a muwaii
$
$
$ atrm 2
$ atq
$ atrm 2
Cannot find jobid 2
可见,我们先是创建了一个在中午(12点) + 5 分钟(相对时间)的定时作业,接着我们使用 atq
确认该作业确实已提交。紧随其后,我们再用 atrm
命令结合 at
创建作业时或 atq
查询作业时返回的作业编号,将之前提交的编号为 2 的作业删除。最后,我们再次查询作业时,发现队列中空空如也,而且删除过的作业,是不能重复删除的,at
提示作业编号未找到。
附录之日期时间标准格式
标准格式中定义了合法的时间格式各个组成部分,而每个组成部分又可以包含更小的构件。所以,timespec
中以编译器文法的方式进行描述。
基本符号
首先是定义了常见符号,如
DOTTEDDATE
:.
隔开的日期HYPHENDATE
:-
隔开的日期NOW
: 现在AM
,PM
: 上午,下午Noon
,MidNight
,TeaTime
: 正午 12:00 (12:00 PM), 午夜 00:00 (12:00 AM), 下午茶 16:00 (04:00 PM)SUN
,MON
,TUE
,WED
,THU
,FRI
,SAT
: 星期几TODAY
,TOMORROW
: 今天,明天JAN
,FEB
,MAR
,APR
,MAY
,JUN
,JUL
,AUG
,SEP
,OCT
,NOV
,DEC
: 12 个月之一
$ cat /usr/share/doc/at/timespec
/*
* Abbreviated version of the yacc grammar used by at(1).
*/
%token <charval> DOTTEDDATE
%token <charval> HYPHENDATE
%token <charval> HOURMIN
%token <charval> INT1DIGIT
%token <charval> INT2DIGIT
%token <charval> INT4DIGIT
%token <charval> INT5_8DIGIT
%token <charval> INT
%token NOW
%token AM PM
%token NOON MIDNIGHT TEATIME
%token SUN MON TUE WED THU FRI SAT
%token TODAY TOMORROW
%token NEXT
%token MINUTE HOUR DAY WEEK MONTH YEAR
%token JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
%token UTC
时间日期类型
接着介绍了,24 小时和 12 小时格式的绝对时间日期类型,以及相对时间类型。
%type <charval> concatenated_date
%type <charval> hr24clock_hr_min
%type <charval> int1_2digit
%type <charval> int2_or_4digit
%type <charval> integer
%type <intval> inc_dec_period
%type <intval> inc_dec_number
%type <intval> day_of_week
timespec 语法
最后便是解析的主入口,开始自顶而下地将 timespec
分解成一个个组件,直到不能继续分的符号(Token
)为止,比如五月 MAY
和下午 PM
就属于不可继续划分的原子单位。
%start timespec
%%
timespec : spec_base
| spec_base inc_or_dec
;
spec_base : date
| time
| time date
| NOW
;
time : time_base
| time_base timezone_name
;
time_base : hr24clock_hr_min
| time_hour am_pm
| time_hour_min
| time_hour_min am_pm
| NOON
| MIDNIGHT
| TEATIME
;
hr24clock_hr_min: INT4DIGIT
;
time_hour : int1_2digit
;
time_hour_min : HOURMIN
;
am_pm : AM
| PM
;
timezone_name : UTC
;
date : month_name day_number
| month_name day_number year_number
| month_name day_number ',' year_number
| day_of_week
| TODAY
| TOMORROW
| HYPHENDATE
| DOTTEDDATE
| day_number month_name
| day_number month_name year_number
| month_number '/' day_number '/' year_number
| concatenated_date
| NEXT inc_dec_period
| NEXT day_of_week
;
concatenated_date: INT5_8DIGIT
;
month_name : JAN | FEB | MAR | APR | MAY | JUN
| JUL | AUG | SEP | OCT | NOV | DEC
;
month_number : int1_2digit
;
day_number : int1_2digit
;
year_number : int2_or_4digit
;
day_of_week : SUN | MON | TUE | WED | THU | FRI | SAT
;
inc_or_dec : increment | decrement
;
increment : '+' inc_dec_number inc_dec_period
;
decrement : '-' inc_dec_number inc_dec_period
;
inc_dec_number : integer
;
inc_dec_period : MINUTE | HOUR | DAY | WEEK | MONTH | YEAR
;
int1_2digit : INT1DIGIT | INT2DIGIT
;
int2_or_4digit : INT2DIGIT | INT4DIGIT
;
integer : INT | INT1DIGIT | INT2DIGIT | INT4DIGIT | INT5_8DIGIT
;
%%