我是一个相对Linux新手。我正在尝试学习如何使用,at
以便我可以安排任务在稍后的时间开始,而不使用sleep
.我一直在看上一个问题求助。
我的问题是,在我创建的以下示例 bash 脚本中,为什么“跑步”据我所知,从来没有打印到标准输出(即我的 bash 控制台)?
#!/bin/bash
echo "Started"
at now + 1 minutes <<EOF
echo "Running"
EOF
echo "Finished"
我看到的唯一输出是,例如:
Started warning: commands will be executed using /bin/sh job 3 at Fri Jul 12 17:31:00 2013 Finished
我的问题的答案可以在警告中找到吗?如果是这样,/bin/sh
与标准输出有何不同?
答案1
因为at
不会在您登录的用户会话的上下文中执行命令。这个想法是,您可以安排命令在任意时间运行,然后注销,系统将负责在指定时间运行该命令。
请注意,手册页at(1)
特别指出(我的重点):
用户将被邮寄命令的标准错误和标准输出(如果有)。将使用命令 /usr/sbin/sendmail 发送邮件。
因此,您应该检查本地邮件池,或者如果失败,则检查本地系统邮件日志。 /var/spool/mail/$USER 可能是一个不错的起点。
at
另请注意,“开始”和“完成”源自外部脚本,其本身完全无关。您可以取出它们或at
调用,您将得到本质上相同的结果。
答案2
我正在运行 Debian 8.1 (jessie)
您可以使用 tty 将“at”输出转到终端。
$ tty
/dev/pts/1
$ at now + 1 min
warning: commands will be executed using /bin/sh
at> echo 'ZZZZZ' > /dev/pts/1
at> <EOT>
一分钟后,“ZZZZZ”将出现在您的终端中......
答案3
正如 @MichaelKjörling 所解释的,您的工作产生的任何输出at
都将被捕获并通过电子邮件发送给您。如果你没有跑步MTA-邮件传输代理如果电子邮件出现在您的邮箱上,那么该电子邮件可能会陷入困境,您甚至不会知道它at
正在尝试执行此操作。
MTA 是一个诸如sendmail
或postfix
之类的程序,可以将电子邮件“传送”到适当的目的地。在这种情况下,它将把它传送到/var/spool/mail
本地系统上的邮件队列(目录下的文件)。系统上的每个用户都可以在此目录中拥有一个队列。
在我的 Fedora 系统上,如果我启动,sendmail
则可以进行本地邮件传递。不过我通常会把它关掉。
$ sudo service start sendmail
现在我们可以看到我的用户帐户的邮件队列saml
是空的:
$ ll /var/spool/mail/|grep saml
-rw-rw----. 1 saml mail 0 Jul 12 19:33 saml
现在我们运行该at
作业:
$ at now + 1 minutes <<EOF
echo "Running"
EOF
job 96 at Fri Jul 12 19:38:00 2013
我们可以看到该作业正在等待运行atq
:
$ atq
96 Fri Jul 12 19:38:00 2013 a saml
几分钟后再次运行,我们可以看到at
工作已完成:
$ atq
$
顺便说一句,当我的 MTA 运行时,我现在在终端中收到以下消息:
/var/spool/mail/saml 中有新邮件
那么让我们检查一下:
$ ll /var/spool/mail/|grep saml
-rw-rw----. 1 saml mail 651 Jul 12 19:38 saml
是的,我们收到了邮件,所以让我们使用以下命令查看它mutt
:
$ mutt -f /var/spool/mail/saml
我们的邮件队列的“收件箱”中有这个:
我们来看看这封电子邮件:
它奏效了。
答案4
上述答案是标准/“正确”的方法。
从更“最终用户”的角度来看,另一种更简单的方法是让任何计划或后台任务将其输出写入“日志”文件。该文件可以位于系统上的任何位置,但如果任务以 root 身份运行(从cron
等),那么下面的某个位置/var/log
是放置该文件的好地方。
我创建了该/var/log/maint
目录并使其对每个人都可读,并且在名为“备份”的目录下有一个可读文件,我在其中记录备份脚本的输出。
我创建了自己的目录,这样我的文件就不会与系统生成的内容混合在一起。
将东西放在那里(在 bash 中):
BACKUP="/var/log/maint/backup"
echo "my message" >> "${BACKUP}"
这>>
会导致消息被附加到文件中,而不是每次都覆盖它。
如果我的脚本有很多输出,我会使用脚本或函数进行输出,这样一切都会以相同的方式完成。下面是我当前的(过度杀伤版本):(当我从终端运行脚本并希望查看正在发生的情况以进行调试时,会出现 VERBOSE 内容。)
#!/bin/bash
## backup_logger
## backup system logging module
## Copyleft 01/20/2013 JPmicrosystems
## Usage is ${SCRIPT_NAME} [-v] [<caller> <log message text>]
## If present, -v says log to console as well as to the log file
## <caller> is the name of the calling script
## If <caller> <log message text> is not present, write a blank line to the log
## Must be placed in path, like ~/bin
## If log is owned by root or another user, then this must run as root ...
## If not, it just aborts
##source "/home/bigbird/bin/bash_trace" ## debug
SCRIPT_NAME="$(basename $0)"
USAGE="Usage is ${SCRIPT_NAME} [-v] [<caller> <log message text>]"
SYSLOGDIR='/var/log/maint'
SYSLOGFILE="${SYSLOGDIR}/backup.log"
LOGGING=1
VERBOSE=0
if [ "${1}" == "-v" ]
then
VERBOSE=1
shift
fi
##LOGGING=0 ## debug
##VERBOSE=1 ## debug
## Only zero or two parameters allowed - <caller> <log message text>
RC=0
if [ "$#" -eq 1 ] || [ "$#" -gt 2 ]
then
echo "${USAGE}"
RC=1
else
if [ ! -w "${SYSLOGFILE}" ]
then
touch "${SYSLOGFILE}"
if [ $? -ne 0 ]
then
echo -e "$(date) ${1} ${2}"
echo "${SCRIPT_NAME} Can't write to log file [${SYSLOGFILE}]"
RC=1
exit ${RC}
fi
fi
if [ -n "${1}" ]
then
(( LOGGING )) && echo -e "$(date) ${1} ${2}" >> "${SYSLOGFILE}"
(( VERBOSE )) && echo -e "$(date) ${1} ${2}"
else
(( LOGGING )) && echo "" >> "${SYSLOGFILE}"
(( VERBOSE )) && echo ""
fi
fi
exit $RC
编辑:at
写入用户文件的简单示例
一直没有使用过这个,所以我用几个简单的脚本解决了这个问题。
第一个脚本只是使用 来安排事件at
。命令本身可以直接输入到终端中,但我很懒 - 特别是当我在测试它时必须多次执行该命令而不欺骗命令历史记录时。
#!/bin/bash
## mytest_at_run
## Schedule a script to run in the immediate future
echo "/home/bigbird/bin/mytest_at_script" | at 00:56
第二个脚本是计划运行的脚本
#!/bin/bash
## mytest_at_script
## The script to be run later
echo "$(date) - is when this ran" >> /home/bigbird/log/at.log
我在文本编辑器中创建了两个脚本,保存它们,然后使用chmod 700 script-file-name
.为了方便起见,我将它们都放在我的$HOME/bin
目录中,但它们可以位于我的用户具有完全访问权限的任何位置。我使用700
任何仅用于测试的脚本,但在单个用户系统上,它也可以是755
.
我已经有一个名为/home/bigbird/log
保存输出的 目录mytest_at_script
。这也可以是您的用户具有完全访问权限的任何地方。只需确保它在脚本运行之前存在或让脚本创建它即可。
为了运行它,我只是确保命令的时间at
是mytest_at_run
在未来一段时间,然后从终端运行它。然后我等到它运行并检查$HOME/log/at.log
.
bigbird@sananda:~/bin$ cat ~/log/at.log
Fri Sep 14 00:52:18 EDT 2018 - is when this ran
Fri Sep 14 00:56:00 EDT 2018 - is when this ran
bigbird@sananda:~/bin$
一些注意事项:
即使我at
从我的用户运行,它也不知道我的环境,例如我的PATH
目录和我的主目录,所以我不这么认为。我像任何cron
工作一样使用完整路径。如果我想让它成为一项cron
工作,我不需要为了让它运行而改变任何东西。
我>>
曾经mytest_at_script
将输出附加到日志文件中,而不是>
在每次运行时都会替换它。使用最适合您的应用程序的一种。