首页 文章

在crontab的shell脚本中使用python3

提问于
浏览
11

我尝试使用运行python3脚本的crontab运行shell脚本 . crontab用于用户组 . 现在它运行脚本但不运行其中的python3脚本 . 我尝试调试它,但我无法弄清楚会发生什么 . 它可能是一个权限问题或路径问题,但我无法弄清楚 . 这是行crontab

*/5 * * * * /home/group_name/path/to/script/run.sh

正如我所说的cron工作被执行或至少就是我的想法,因为当我运行 sudo grep CRON /var/log/syslog 时,我得到了像

Feb 16 20:35:01 ip-**-**-*-*** CRON[4947]: (group_name) CMD (/home/group_name/path/to/script/run.sh)

在下面我也得到一条可能与问题有关的线

Feb 16 20:35:01 ip-**-**-*-*** CRON[4946]: (CRON) info (No MTA installed, discarding output)

最后 run.sh 看起来像这样

#!/bin/bash

# get path to script and path to script directory
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")

echo "set directory"
cd "$SCRIPTPATH"

echo "run first script"
/usr/bin/python3 ./first_script.py > ./log1.txt

但是当cron作业执行时没有任何反应,当我手动运行它时,数据库的cahnges按预期发生 . 该集团拥有与我相同的权利 . shell文件可以由我执行,组和python文件不能由我执行,所以我不知道为什么该组需要这个 .

PS:我想在shell中执行python脚本,因为我们有很多脚本,有时会有很多参数,因此crontab会变得人满为患,而且某些脚本必须以特定的顺序执行 .

编辑:在 #! /bin.bash 之后添加 exec >> /tmp/output 2>&1 将回波写入 /tmp/output ,无论何时我手动运行它,但不是当我在cron中运行它时,甚至在运行任何python脚本之前都没有 .

直接从cron运行其中一个python脚本,但即使我复制粘贴与在cron中工作的行完全相同的行,也没有任何反应 .

6 回答

  • 3

    这个问题有很多组成部分 . 我忽略了MTA错误,因为这只是当您的cron作业完成时的电子邮件通知 . 我还假设您已正确设置权限,并且在shell中手动运行时脚本运行正常 .

    最大的问题是CRON命令与从终端“shell”运行命令不同 . 您必须指定使用bash运行脚本 . 从以下位置更改您的cron作业:

    */5 * * * * /home/group_name/path/to/script/run.sh
    

    至:

    */5 * * * * bash /home/group_name/path/to/script/run.sh
    

    This question有更多信息和解决问题的其他选项 .

  • 2

    改变这一行:

    */5 * * * * /home/group_name/path/to/script/run.sh
    

    至:

    */5 * * * * cd /home/group_name/path/to/script && /home/group_name/path/to/script/run.sh
    

    关于/ var / log / syslog,当您查看/ var / log / syslog时,请查看时间戳以确定是否正在运行cron作业 .

    关于cron作业无法写入log.txt,它可能与权限有关 . 尝试更改此行:

    /usr/bin/python3 ./first_script.py > ./log1.txt
    

    至:

    /usr/bin/python3 /full/path/to/first_script.py > /tmp/log1.txt
    

    看看是否有任何区别 . cron应该能够写入/ tmp .

  • 1

    1)消息“没有安装MTA”并不意味着任何错误,它只表示没有邮件服务器的cron无法报告任何细节 .

    修改cron作业以记录其输出into syslog

    */5 * * * * /home/group_name/path/to/script/run.sh 2>&1 | /usr/bin/logger -t run.sh
    

    然后通过 sudo tail -f /var/log/syslog (或RedHat和SuSE上的 sudo tail -f /var/log/messages )检查结果

    或者,install Postfix并将其配置为"local only"交付:

    sudo apt-get install postfix
    

    然后将邮件检查为 group_name 用户 .

    2) run.sh 中的重定向 > ./log1.txt 应在每次执行时覆盖日志文件 . 如果python脚本失败并出现异常,则 log1.txt 将保持截断为零长度 . 在这种情况下修改 run.sh 的最后一行:

    /usr/bin/python3 ./first_script.py 2>&1 > ./log1.txt
    

    并检查结果 .

    如果 log1.txt 既未被截断也未包含新输出,则根本不会启动python脚本 . 请参阅步骤1)以调试 run.sh .

  • 1

    bash脚本中的最后一行包含相对路径(./)我相信这是问题所在

  • 2

    目前有很多关于这个问题的猜测,那是因为您的系统无法向您发送故障电子邮件以准确解释问题所在 . 一段时间我遇到了类似的问题,试图 Build 一个实际的邮件系统不堪重负,于是写了一封短邮件转发sendmail替身: pygeon_mail

    #!/usr/bin/python
    from __future__ import with_statement
    from email.mime.text import MIMEText
    import email
    import os
    import pwd
    import smtplib
    import stat
    import sys
    import syslog
    import traceback
    
    
    CONFIG = '/etc/pygeon_mail.rc'
    # example config file
    #
    # server=mail.example.com
    # port=25
    # domain=example.com
    # host=this_pc_host_name
    # root=me@example.com,you@example.com
    # ethan=me@example.com
    # debug=debug@example.com
    
    
    def check_dangerously_writable(filename):
        "return the bits of group/other that are writable"
        mode = stat.S_IMODE(os.stat(filename)[0])       # get the mode bits
        if mode & (stat.S_IWGRP | stat.S_IWOTH):        # zero means not set
        syslog.syslog("%s must only be writable by root, aborting" % (filename, ))
        sys.exit(1)
    
    def get_config(filename, config=None):
        "return settings from config file"
        check_dangerously_writable(filename)
        if config is None:
        config = {}
        with open(filename) as settings:
        for line in settings:
            line = line.strip()
            if line and line[:1] != '#':
            key, value = line.split('=')
            key, value = key.strip(), value.strip()
            config[key] = value
        return config
    
    def mail(server, port, sender, receiver, subject, message):
        """sends email.message to server:port
    
        receiver is a list of addresses
        """
        msg = MIMEText(message.get_payload())
        for address in receiver:
        msg['To'] = address
        msg['From'] = sender
        msg['Subject'] = subject
        for header, value in message.items():
        if header in ('To','From', 'Subject'):
            continue
        msg[header] = value
        smtp = smtplib.SMTP(server, port)
        try:
        send_errs = smtp.sendmail(msg['From'], receiver, msg.as_string())
        except smtplib.SMTPRecipientsRefused as exc:
        send_errs = exc.recipients
        smtp.quit()
        if send_errs:
        errs = {}
        for user in send_errs:
            if '@' not in user:
            errs[user] = [send_errs[user]]
            continue
            server = 'mail.' + user.split('@')[1]
            smtp = smtplib.SMTP(server, 25)
            try:
            smtp.sendmail(msg['From'], [user], msg.as_string())
            except smtplib.SMTPRecipientsRefused as exc:
            if send_errs[user] != exc.recipients[user]:
                errs[user] = [send_errs[user], exc.recipients[user]]
            else:
                errs[user] = [send_errs[user]]
            smtp.quit()
        for user, errors in errs.items():
            for code, response in errors:
            syslog.syslog('%s --> %s: %s' % (user, code, response))
        return errs
    
    
    if __name__ == '__main__':
        syslog.openlog('pygeon', syslog.LOG_PID)
        try:
        config = get_config(CONFIG)
        root = config.get('root')
        domain = config.get('domain', '')
        if domain:
            domain = '@' + domain
        sender = None
        receiver = []
        dot_equals_blank = False
        ignore_rest = False
        next_arg_is_subject = False
        redirect = False
        subject = ''
        for i, arg in enumerate(sys.argv[1:]):
            if next_arg_is_subject:
            subject = arg
            next_arg_is_subject = False
            sys.argv[i] = '"%s"' % (arg, )
            elif arg == '-s':
            next_arg_is_subject = True
            elif arg == '-i':
            dot_equals_blank = True
            elif arg[:2] == '-F':
            sender = arg[2:]
            elif arg[0] != '-':
            receiver.append(arg)
            else:
            pass
        command_line = ' '.join(sys.argv)
        if sender is None:
            sender = pwd.getpwuid(os.getuid()).pw_name
        sender = '%s@%s' % (sender, config['host'])
        if not receiver:
            receiver.append(pwd.getpwuid(os.getuid()).pw_name)
        limit = len(receiver)
        for i, target in enumerate(receiver):
            if i == limit:
            break
            if '@' not in target:
            receiver[i] = ''
            receiver.extend(config.get(target, target+domain).split(','))
        receiver = [r for r in receiver if r]
        server = config['server']
        port = int(config['port'])
        all_data = []
        text = []
        while True:
            data = sys.stdin.read()
            if not data:
            break
            all_data.append(data)
            if ignore_rest:
            continue
            for line in data.split('\n'):
            if line == '.':
                if dot_equals_blank:
                line = ''
                else:
                ignore_rest = True
                break
            text.append(line)
        text = '\n'.join(text)
        message = email.message_from_string(text)
        errs = mail(server, port, sender, receiver, subject, message)
        except Exception:
        exc, err, tb = sys.exc_info()
        lines = traceback.format_list(traceback.extract_tb(tb))
        syslog.syslog('Traceback (most recent call last):')
        for line in lines:
            for ln in line.rstrip().split('\n'):
            syslog.syslog(ln)
        syslog.syslog('%s: %s' % (exc.__name__, err))
        sys.exit(1)
        else:
        receiver = []
        debug_email = config.get('debug', None)
        if debug_email:
            receiver.append(debug_email)
        if errs and root not in receiver:
            receiver.append(root)
        if receiver:
            debug = [
                'command line:',
                '-------------',
                repr(command_line),
                '-' * 79,
                '',
                'sent email:',
                '-----------',
                text,
                '-' * 79,
                '',
                'raw data:',
                '---------',
                ''
                ]
            all_data = ''.join(all_data)
            while all_data:
            debug_text, all_data = repr(all_data[:79]), all_data[79:]
            debug.append(debug_text)
            debug.append('-' * 79)
            if errs:
            debug.extend([
                '',
                'errors:',
                '-------',
                ])
            for address, error in sorted(errs.items()):
                debug.append('%r: %r' % (address, error))
            debug.append('-' * 79)
            text = '\n'.join(debug)
            message = email.message_from_string(text)
            mail(server, port, 'debug@%s' % config['host'], receiver, subject+'  [pygeon_mail debugging info]', message)
        if errs:
            sys.exit(1)
    

    它是为Python 2.5编写的,应该与2.6和2.7一起使用 .

    它需要复制到 /usr/sbin/sendmail ,权限为0755并由root拥有:

    sudo cp pygeon_mail / usr / sbin / sendmail sudo chown root:root / usr / sbin / sendmail sudo chmod 0755 / usr / sbin / sendmail

    您需要创建一个 /etc/pygeon_mail.rc 配置文件(请参阅代码示例) .

    然后,您可以使用以下内容进行测试:

    $ echo一些有用的信息| sendmail我自己 - “一些重要的主题”

    并且您希望在普通电子邮件帐户(您在 /etc/pygeon_mail.rc 文件中设置)中看到该电子邮件 .

    在那之后,你应该能够得到实际的错误,我们实际上可以提供帮助您

  • 0

    如果您记录使用cron调用的脚本的输出,则可以非常轻松地找到错误 . 尝试这样的事情:

    */10 * * * * sh /bin/execute/this/script.sh >> /var/log/script_output.log 2>&1

相关问题