无人值守脚本_构建智能,无人值守的脚本

关于本系列

典型的UNIX®管理员经常使用一系列重要的实用程序,技巧和系统,以协助管理过程。 有一些关键实用程序,命令行链和脚本可用来简化不同的过程。 这些工具中的一部分随操作系统一起提供,但是大多数技巧来自多年的经验以及减轻系统管理员生活的渴望。 本系列的重点是从各种不同UNIX环境中的可用工具中获取最大收益,包括简化异构环境中管理的方法。

无人值守脚本问题

执行无人看管的脚本存在很多问题,即您通过cron等服务或at命令自动运行的脚本。

例如,cron和at命令的默认模式是捕获脚本的输出,然后通过电子邮件将其发送给运行脚本的用户。 您并不总是希望用户收到cron默认发送的电子邮件(尤其是在一切正常的情况下),有时运行脚本的用户和负责监视输出的实际人员是不同的。

因此,您需要更好的方法来捕获和识别脚本中的错误,更好的方法来传达问题,以及向相关人员提供可选的成功方法。

正确设置脚本至关重要。 您需要确保以易于维护且脚本有效运行的方式配置脚本。 您还需要能够捕获错误和程序输出,并确保脚本执行环境的安全性和有效性。 继续阅读以了解如何完成所有这些操作。

搭建环境

在开始使用无人看管的脚本之前,您需要确保已正确设置环境。 有许多元素需要作为脚本的一部分进行显式配置,并且花一些时间这样做不仅可以确保脚本正常运行,还可以使脚本易于维护。

您可能需要考虑的一些事项包括:

  • 应用程序的搜索路径
  • 库的搜索路径
  • 目录位置
  • 创建目录或路径
  • 共同文件

这些元素中的一些很容易组织。 例如,您可以在大多数Bourne兼容的shell(sh,Bash,ksh和zsh)中使用以下命令设置路径:

PATH=/usr/bin:/bin:/usr/sbin

对于目录和文件位置,只需在脚本的标题处设置一个变量。 然后,您可以在每个使用文件名的位置使用该变量。 例如,在写入日志文件时,您可以使用清单1

清单1.编写日志文件
LOGFILE=/tmp/output.log

do_something >>$LOGFILE
do_another >>$LOGFILE

通过一次设置名称,然后使用变量,可以确保您不会弄错文件名,并且,如果您需要更改文件名,则只需要更改一次即可。

使用单个文件名和变量也使创建复杂文件名变得非常容易。 例如,通过使用带有格式规范的date命令,可以更轻松地在日志文件名中添加日期:

DATE='date +%Y%m%d.%H%M'

上面的命令创建一个包含日期的字符串,其格式为YYYYMMDD.HHMM,例如20070524.2359。 您可以将该日期变量插入文件名,以便根据日志文件的创建日期对日志文件进行标记。

如果您在日志文件名中未使用日期/时间唯一标识符,则最好插入一些其他唯一标识符,以防两个脚本同时运行。 如果您的脚本是通过两个不同的进程写入同一文件,则最终将导致信息损坏或信息丢失。

所有外壳程序都基于外壳程序进程ID支持唯一的外壳程序ID,并且可以通过特殊的$$变量名进行访问。 通过使用全局日志变量,您可以轻松创建用于日志记录的唯一文件:

LOGFILE=/tmp/$$.err

您还可以将相同的全局变量原则应用于目录:

LOGDIR=/var/log/my_app

要确保已创建目录,请对mkdir使用-p选项来创建要使用的目录的完整路径:

mkdir -p $LOGDIR

幸运的是,如果目录已经存在,则此格式不会出现问题,这使其非常适合在无人值守的脚本中运行。

最后,通常最好在无人参与的脚本中使用完整路径名而不是本地化路径,以便您可以一起使用先前的原则。

清单2.在无人参与的脚本中使用完整路径名
DATE='date +%Y%m%d.%H%M'
LOGDIR=/usr/local/mcslp/logs/rsynclog
mkdir -p $LOGDIR
LOGNAME=$LOGDIR/$DATE.log

现在,您已经设置了环境,让我们看看如何使用这些原理来帮助通用,无人值守的脚本。

编写日志文件

您可能对脚本所做的最简单的改进就是将脚本的输出写入日志文件。 您可能没有必要这样做,但是cron的默认操作是保存执行的脚本或命令的输出,然后将其通过电子邮件发送给拥有crontab或工作的用户。

由于许多原因,这并不完美。 首先,可能正在运行脚本的已配置用户可能与需要处理输出的实际用户不同。 即使运行时脚本或命令的输出需要交给其他人,您也可能以root用户身份运行脚本。 如果要将不同命令的输出发送给不同用户,则设置常规过滤器或重定向将不起作用。

第二个原因是更根本的原因。 除非出现问题,否则不必从脚本接收输出。 cron守护程序将从stdout和stderr向您发送输出,这意味着即使脚本成功执行,您也可以得到输出的副本。

最终的原因是有关所产生的信息和输出的管理和组织。 电子邮件并不总是一种有效的方式来记录和跟踪自动运行的脚本的输出。 也许您只想保留成功的日志文件的存档,或者在出现问题时通过电子邮件发送错误日志的副本。

可以多种不同方式处理写出到日志文件的操作。 最直接的方法是将每个命令的输出重定向到文件(请参见清单3 )。

清单3.将输出重定向到文件
cd /shared
rsync --delete --recursive . /backups/shared >$LOGFILE

如果要将错误和标准输出合并到一个文件中,请使用编号重定向(请参见清单4 )。

清单4.将错误和标准输出组合到一个文件中
cd /shared
rsync --delete --recursive . /backups/shared >$LOGFILE 2>&1

清单4将信息写到同一日志文件中。

您可能还希望将信息写出到单独的文件中(请参见清单5 )。

清单5.将信息写到单独的文件中
cd /shared
rsync --delete --recursive . /backups/shared >$LOGFILE 2>$ERRFILE

对于多个命令,重定向可能变得复杂且重复。 例如,您必须确保将信息添加到日志文件中,而不是覆盖它们(请参见清单6 )。

清单6.将信息追加到日志文件
cd /etc
rsync --delete --recursive . /backups/etc >>$LOGFILE >>$ERRFILE

如果外壳程序支持,一个简单的解决方案是对一组命令使用一个内联块,然后将整个块的输出重定向。 结果是您可以使用清单8中的结构重写清单7中的行。

清单7.以长格式登录
cd /shared
rsync --delete --recursive . /backups/shared >$LOGFILE 2>$ERRFILE

cd /etc
rsync --delete --recursive . /backups/etc >>$LOGFILE 2>>$ERRFILE

清单8显示了用于分组命令的内联块。

清单8.使用块记录
{
    cd /shared
    rsync --delete --recursive . /backups/shared 

    cd /etc
    rsync --delete --recursive . /backups/etc

} >$LOGFILE 2>$ERRFILE

括起来的括号表示一个子shell,因此该块中的所有命令都像单独进程的一部分一样被执行(尽管未创建任何辅助shell,但该封闭块仅被视为不同的逻辑环境)。 使用子shell,您可以为整个块而不是每个单独的命令共同重定向其标准和错误输出。

捕获错误并报告错误

子外壳程序的主要优点之一是,您可以在脚本的主要内容周围放置一个包装器,重定向错误,然后发送带有脚本执行状态的格式化电子邮件。

例如, 清单9显示了一个更完整的脚本,该脚本设置环境,执行实际命令和整个流程,捕获输出,然后发送包含输出和错误信息的电子邮件。

清单9.使用子shell通过电子邮件发送更有用的日志
LOGFILE=/tmp/$$.log
ERRFILE=/tmp/$$.err
ERRORFMT=/tmp/$$.fmt

{
    set -e

    cd /shared
    rsync --delete --recursive . /backups/shared

    cd /etc
    rsync --delete --recursive . /backups/etc

} >$LOGFILE 2>$ERRFILE

{
    echo "Reported output"
    echo
    cat /tmp/$$.log
    echo "Error output"
    echo
    cat /tmp/$$.err
} >$ERRORFMT 2>&1

mailx -s 'Log output for backup' root <$ERRORFMT

rm -f $LOGFILE $ERRFILE $ERRORFMT

如果您使用subshel​​l技巧,并且您的外壳程序支持外壳程序选项(Bash,ksh和zsh),那么您可能需要选择设置一些外壳程序选项,以确保在发生错误时正确终止该块。 例如,Bash中的-e (errexit)选项可确保当简单命令(例如,通过脚本调用的任何外部命令)导致shell立即终止时,shell终止。

例如,在清单9中 ,如果第一个rsync失败,那么子shell将继续并运行下一个命令。 但是,有时您想在命令失败时停止,因为继续执行可能会造成更大的破坏。 通过设置errexit,子外壳将在第一个命令停止时立即终止。

设置选项并确保安全

自动化脚本的另一个问题是确保脚本的安全性,尤其是确保脚本不会因配置错误而失败。 您可以在此过程中使用外壳程序选项。

您可能希望以与外壳程序无关的方式设置其他选项(外壳程序越丰富,通常在捕获这些实例时越好)。 例如,在Bash shell中, -u确保将所有未设置的变量都视为错误。 这对于确保在未正确配置必需变量时确保无人参与脚本不会尝试执行很有用。

-C选项(noclobber)确保文件不存在(如果文件已经存在),并且可以防止脚本覆盖它也不应访问的文件(例如,系统文件),除非脚本具有正确的命令首先删除原始文件。

可以使用set命令来设置每个选项(请参见清单10 )。

清单10.使用set命令设置选项
set -e
set -C

您可以在选项前使用加号将其禁用。

您可能要改善脚本的安全性和环境的另一个领域是使用资源限制。 可以通过ulimit命令设置资源限制,该命令通常特定于Shell,并且使您能够限制文件的大小,内核,内存使用甚至脚本的持续时间,以确保脚本不会随着运行而消失。本身。

例如,您可以使用以下命令以秒为单位设置CPU时间:

ulimit -t 600

尽管ulimit不能提供完全的保护,但是它在那些脚本可能会自行消失或程序突然使用大量内存的脚本中有所帮助。

捕获故障

您已经了解了如何捕获错误,输出和创建日志,这些日志可以在发生错误时通过电子邮件发送给适当的人,但是如果您想更详细地了解错误和响应,该怎么办?

这里有两个工具很有用。 第一个是命令的返回状态,第二个是Shell中的trap命令。

命令的返回状态可用于识别特定命令是否正确运行,或者它是否产生某种错误。 特定返回状态代码的确切含​​义对于特定命令(检查手册页)而言是唯一的,但是普遍接受的原则是错误代码为零表示该命令正确执行。

例如,假设您想在尝试创建目录时捕获错误。 您可以检查$? mkdir之后的变量,然后通过电子邮件发送输出,如清单11所示。

清单11.陷阱返回状态
ERRLOG=/tmp/$$.err

mkdir /tmp 2>>$ERRLOG
if [ $? -ne 0 ]
then
    mailx -s "Script failed when making directory" admin <$ERRLOG
    exit 1
fi

顺便说一句,您可以通过将命令与&&或||链接起来来内联使用返回状态代码信息。 用作andor ,或type语句的符号。 例如,假设您要确保创建目录并执行命令,但是,如果未创建目录,则不会执行命令。 您可以使用if语句来做到这一点(请参见清单12 )。

清单12.确保在执行命令之前创建目录
mkdir /tmp/out
if [ $? -eq 0 ]
then
    do_something
fi

您可以将清单12修改为一行:

mkdir /tmp/out && do_something

上面的语句基本上显示为:“创建目录,如果成功完成,则还要运行命令”。 本质上,只有第一个命令正确完成后,才执行第二个命令。

|| 符号以相反的方式起作用; 如果第一个命令未成功完成,则执行第二个。 这对于捕获命令会引发错误但提供替代解决方案的情况很有用。 例如,当切换到目录时,您可以使用以下行:

cd /tmp/out || mkdir /tmp/out

这行代码尝试更改目录,如果失败,则可能会更改目录(可能因为目录不存在)。 此外,您可以将这些语句组合在一起。 当然,在上一个示例中,您要做的是更改目录,或者创建该目录,然后如果该目录尚不存在,则更改为该目录。 您可以将其写为:

cd /tmp/out || mkdir /tmp/out && cd /tmp/out

trap命令是一种更通用的解决方案,用于根据命令失败时发出的信号来捕获更严重的错误,例如核心转储,内存错误或当命令被kill命令强行终止时。

要使用trap,可以指定在捕获信号时要执行的命令或函数,以及要捕获的信号编号,如清单13所示。

清单13.陷阱信号
function catch_trap
{
    echo "killed" mailx -s "Signal trapped" admin
}

trap catch_trap 1 2 3 4 5 6 7 8 9 10 11

sleep 9000

您可以通过这种方式捕获任何信号,这是确保崩溃的程序被有效捕获和捕获并报告的一种好方法。

识别可报告的错误

在整个本文中,您研究了捕获错误,保存输出和记录问题的方式,以便可以对其进行处理和报告。 但是,如果您正在使用的脚本或命令自然地输出了您希望能够使用和报告的错误信息,但是您却并不总是想知道这些错误信息,该怎么办?

没有简单的解决方案可以解决此问题,但是您可以结合使用本文中显示的技术来记录错误和信息,读取或过滤信息,以及相应地发送邮件和报告或显示。

一种简单的方法是选择要输出的命令部分并向日志报告。 或者,您可以对日志进行后处理,以选择或过滤出所需的输出。

例如,假设您有一个脚本,该脚本使用Apache的格式对象处理器(FOP)系统在后台构建文档,以生成文档的PDF版本。 不幸的是,在此过程中,会产生许多有关连字的错误。 这些是您知道的错误,但它们不会影响输出质量。 在生成文件的脚本中,只需从错误日志中过滤掉以下几行:

sed -e '/hyphenation/d' <error.log >mailerror.log

如果没有其他错误,mailerror.log文件将为空,并发送包含错误信息的电子邮件。

摘要

在本文中,您研究了如何在无人值守的脚本中运行命令,捕获其输出以及监视脚本中不同命令的执行情况。 您可以通过多种方式(例如,逐个命令或全局)记录信息,并检查并报告进度。

对于错误陷阱,您可以监视输出和结果代码,甚至可以设置全局陷阱以识别问题,并在执行过程中为报告目的对其进行陷阱。 结果是产生了一系列选项,这些选项可以处理和报告自己运行的脚本的问题,并且从错误和问题中恢复的能力至关重要。


翻译自: https://www.ibm.com/developerworks/aix/library/au-satbuildscript.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值