Question Utiliser python3 en script shell dans crontab


J'essaie d'exécuter un script shell avec crontab qui exécute les scripts python3. La crontab est destinée à un groupe d'utilisateurs. Maintenant, il exécute le script mais pas les scripts python3 qu'il contient. J'essaie de le déboguer mais je n'arrive pas à comprendre ce qui se passe. Cela peut être un problème de permission ou un problème de chemin mais je ne peux pas le comprendre. Ceci est la ligne crontab

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

Comme je l'ai dit le travail cron est exécuté ou du moins c'est ce que je pense depuis quand je cours sudo grep CRON /var/log/syslog J'ai des lignes comme

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

juste en dessous, je reçois aussi une ligne qui pourrait avoir quelque chose à voir avec le problème

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

et enfin run.sh ressemble à ça

#!/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

Cependant, lorsque la tâche cron est exécutée, rien ne se produit, lorsque je l'exécute manuellement, les modifications apportées à la base de données se produisent comme prévu. Le groupe a les mêmes droits que moi. Le fichier shell peut être exécuté par moi et le groupe et les fichiers python ne peuvent pas être exécutés par moi, donc je ne sais pas pourquoi le groupe en aurait besoin.

PS: Je veux exécuter le script python dans un shell, car nous avons beaucoup de scripts avec beaucoup d’arguments et le crontab sera surpeuplé et certains scripts devront être exécutés dans un certain ordre.

MODIFIER: Ajouter exec >> /tmp/output 2>&1 juste après #! /bin.bash écrit les échos à /tmp/output chaque fois que je l'exécute manuellement, mais pas lorsque je l'exécute dans cron, même pas avant d'exécuter un script python.

L'exécution d'un des scripts python directement depuis cron fonctionne, mais même si je copie la même ligne que celle qui fonctionne dans cron, dans le fichier shell, rien ne se passe.


11
2018-02-16 20:48


origine


Réponses:


Il y a beaucoup de composants à ce problème. J'ignore l'erreur MTA car il s'agit juste d'une notification par e-mail lorsque votre travail cron est terminé. Je suppose également que vos autorisations sont correctement définies et que votre script fonctionne correctement lorsqu'il est exécuté manuellement dans le shell.

Le plus gros problème est que la commande CRON n'est pas la même chose que d'exécuter la commande à partir du "shell" du terminal. Vous devez spécifier que votre script est exécuté avec bash. Changez votre travail cron à partir de:

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

à:

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

Cette question a plus d'informations et d'options supplémentaires pour résoudre le problème.


3
2018-02-26 16:41



Changer cette ligne:

*/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

En ce qui concerne / var / log / syslog, lorsque vous regardez dans / var / log / syslog, examinez les horodatages pour déterminer si le travail cron est en cours d'exécution.

En ce qui concerne le travail cron ne pouvant pas écrire dans log.txt, cela pourrait avoir à faire avec les autorisations. Essayez de changer cette ligne:

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

à:

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

Voyez s'il y a une différence. cron devrait pouvoir écrire dans / tmp.


2
2018-02-21 06:21



1) Le message "No MTA installed" ne signifie aucune erreur, cela indique seulement que sans le serveur de messagerie cron ne peut pas rapporter de détails.

Modifier le travail cron pour enregistrer sa sortie dans syslog:

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

Ensuite, vérifiez les résultats via sudo tail -f /var/log/syslog (ou sudo tail -f /var/log/messages sur RedHat et SuSE)

Alternativement, installer Postfix et configurez-le pour une livraison "locale uniquement":

sudo apt-get install postfix

Ensuite, vérifiez le courrier comme le group_name utilisateur.

2) la redirection > ./log1.txt dans run.sh devrait écraser le fichier journal à chaque exécution. Si le script python a échoué avec une exception, alors log1.txtresterait tronqué à la longueur zéro. Dans ce cas, modifiez la dernière ligne de run.sh:

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

et vérifier les résultats.

Si log1.txt ne soit ni tronqué ni contenant de nouvelles sorties, le script python n’est pas du tout lancé. Référez-vous à l'étape 1) pour déboguer le run.sh.


2
2018-02-20 23:05



La dernière ligne de votre script bash contient des chemins relatifs (./) Je crois que c'est le problème


1
2018-02-16 20:56



Il y a actuellement beaucoup de devinettes sur le problème, car votre système ne peut pas vous envoyer le courrier électronique d'échec pour expliquer le problème. J'ai rencontré un problème similaire il y a quelque temps, j'ai été submergé en essayant de mettre en place un système de messagerie réel. envoyer un mail remplaçant: 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)

Il a été écrit pour Python 2.5 et devrait fonctionner avec 2.6 et 2.7.

Il doit être copié dans /usr/sbin/sendmail avec des permissions de 0755 et appartenant à root:

sudo cp pygeon_mail / usr / sbin / sendmail

racine sudo chown: root / usr / sbin / sendmail

sudo chmod 0755 / usr / sbin / sendmail

Vous devez créer un /etc/pygeon_mail.rc fichier de configuration (voir le code pour un exemple).

Vous pouvez ensuite le tester avec quelque chose comme:

$ echo des informations utiles | sendmail moi-même "un sujet important"

et vous espérez voir cet email dans votre compte de messagerie normal (que vous avez configuré dans le /etc/pygeon_mail.rc  fichier).

Après cela, vous devriez pouvoir obtenir l'erreur réelle et nous pouvons réellement vous aider


1
2018-02-26 20:21



Si vous enregistrez la sortie du script que vous appelez avec cron, vous pouvez trouver l'erreur assez facile. Essayez quelque chose comme ça:

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


0
2018-02-26 22:15