Fügen Sie crontab programmgesteuert etwas hinzu (über ssh)

13

Ich habe ein Bereitstellungsskript, das einem Benutzer etwas hinzufügen muss crontab(ein Skript auslösen, das die Protokolle alle XXX Tage bereinigt). Dies muss jedoch nur während der ersten Bereitstellung erfolgen oder wenn es aktualisiert werden muss.

(Ich kann rennen xxx.py deploy envoder xxx.py update env)

also muss ich das machen:

Check if my cronJob already exist
Put my cronJob if it does not already exist
or
update my cronjob if one of the parameter of the command is different

Ich sehe nicht, wie man etwas hinzufügt / überprüft / entfernt, crontabohne crontab -edie crontabDatei zu verwenden oder zu bearbeiten (herunterladen, neu schreiben, erneut hochladen)

PS: Dies ist ein benutzerspezifischer Cronjob. "Webadmin" wird dies tun und er sollte kein Sudo verwenden, um dies zu tun.

sliders_alpha
quelle
1
Muss es sich in einer benutzerspezifischen Crontab befinden? Die meisten vorgefertigten Cron-Jobs werden in einem der Verzeichnisse /etc/cron.* gespeichert.
ein CVn
Hat CentOS /etc/cron.d? Wenn ja,
fügen Sie
Ja, es ist benutzerspezifisch. Ich kann es nicht zu /etc/cron.d hinzugefügt werden, da es sich um eine Root - Datei ist, also nur root Job alowed innen (ich konnte sudo aber , dass eine schlechte Praxis Man hat mir gesagt)
sliders_alpha
1
Ebenso /etc/crontabhaben Dateien in /etc/cron.d/ein zusätzliches Feld für den Benutzernamen, unmittelbar nach der Zeitplanspezifikation. zB * * * * * username /path/to/script. Sehen man 5 crontabund suchen SYSTEM CRON.
Cas

Antworten:

15

meine bisher beste idee

Um zuerst zu überprüfen, ob der Inhalt mit dem Inhalt übereinstimmt, und nur dann zu aktualisieren, wenn dies nicht der Fall ist:

if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
    echo $(crontab -l ; echo '* 1 * * * some_command') | crontab -
fi

Dies wird jedoch kompliziert genug, um ein separates Skript für diese Cron-Aufgabe zu erstellen.

andere Ideen

Sie können den String über stdin an crontab senden (Vorsicht, dies löscht alle vorherigen crontab-Einträge):

echo "* 1 * * * some_command" | crontab -

Dies sollte sogar direkt durch ssh funktionieren:

echo "* 1 * * * some_command" | ssh user@host "crontab -"

Wenn Sie an die Datei anhängen möchten, können Sie Folgendes verwenden:

# on the machine itself
echo "$(echo '* 1 * * * some_command' ; crontab -l)" | crontab -
# via ssh
echo "$(echo '* 1 * * * some_command' ; ssh user@host crontab -l)" | ssh user@host "crontab -"
Phillip-Zyan K Lee-Stockmann
quelle
das kommt darauf an: brauchst du was da drin gewesen wäre? :)
Phillip-Zyan K Lee-Stockmann
awww ... läuft nicht als root? ... Ich werde es umschreiben ...
Phillip-Zyan K Lee-Stockmann
eh, das gefällt mir: D
sliders_alpha
Ich hatte zwei Probleme mit dieser Lösung: 1) echo '*...erweiterte die *zu einer Liste von Dateien. 2) Die Zeilenenden in der Crontab wurden gelöscht.
Heath Raftery
Ich konnte diese Probleme beheben, indem ich: 1) zu echo "*...und 2) echo $vom Anfang der Zeile entfernte.
Heath Raftery
3

Für die Aufzeichnung werde ich vorschlagen, zu verwenden /etc/cron.d/. Hier kann nur root Dateien schreiben, aber die Einträge können wie jeder Benutzer ausgeführt werden (ohne dass dies erforderlich ist sudo).

echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' > ~/webadmin.cron
scp -p ~/webadmin.cron root@remote_host:/etc/cron.d/webadmin

Dies kann mehrmals angewendet werden, wobei die lokale webadmin.cronDatei nach Bedarf aktualisiert wird , bevor sie kopiert wird.

Sie können sogar die Bereitstellung entfernen:

ssh -q root@remote_host rm -f /etc/cron.d/webadmin

Beachten Sie, dass Sie in vielen Fällen das root-Passwort für die Befehle scp/ nicht sshangeben können. Stattdessen müssen Sie öffentliche / private Schlüsselzertifikate eingerichtet haben. Implizit hat das lokale Konto (was auch immer es ist) vollen Root-Zugriff auf den Remote-Server. Es ist im Moment unklar, ob dies für Ihr spezifisches Szenario ein Show-Stopper wäre.

Roaima
quelle
Es ist mein Client-Server, ich kann mich nicht als root anmelden, ich kann mich darauf einlassen, ABER wenn ich das tue, werden sie mich töten. Dies ist ein Webadlmin-Job, also sollte er nur im Webadmin-Bereich enthalten sein, das hat mir der Sysadmin gesagt.
sliders_alpha
@sliders_alpha Der Job wird nur als Webadmin ausgeführt. Es ist die Bereitstellung, die Root-Äquivalenz erfordert. Ich werde jedoch auch nach einer Nicht-Root-Lösung suchen.
Roaima
1
+1. /etc/cron.d/existiert genau für diesen Zweck - so dass Pakete / Bereitstellungen einfach eine Crontab-Datei hier ablegen können.
Cas
3

Ich empfehle dringend, Ansible * zu verwenden, anstatt Ihre eigenen zu rollen. Oder Puppet oder Chef - aber Ansible eignet sich gut für solche Skripts zur Bereitstellung ohne Infrastruktur.

Das liegt daran, dass es bereits Module gibt, die solche Probleme lösen sollen, und Konfigurationsverwaltungstools haben Idempotenz als grundlegendes Entwurfsziel - dies ist die Eigenschaft, nur dann zu ändern, wenn dies erforderlich ist, selbst wenn Sie es versehentlich (oder absichtlich) erneut ausführen.

Insbesondere das Cron-Modul von Ansible kann Benutzer-Crontabs ändern. Wenn Sie sich später an die Verwendung von System-Crontabs anpassen möchten, ist dies als Bonus eine sehr einfache Optimierung und kein Umschreiben.


* Haftungsausschluss: Ich arbeite für Red Hat und Ansible ist ein von Red Hat gesponsertes Projekt.

mattdm
quelle
Ja, die Sache ist, ich wusste vor 2 Monaten nichts über Ansible, und jetzt haben wir ein riesiges Python-Deployer-Skript (aber er ist MAGNIFICIENT, lesbar, wartbar;)). Das nächste Mal werde ich Ansible verwenden, aber jetzt geht es los zurück ist unmöglich (Geld Geld Geld)
sliders_alpha
1

Wenn Sie einen Cron-Job über das Zielkonto hinzufügen möchten, führen Sie ihn aus crontab -e. Dieser Befehl leitet die Crontab durch einen Editor. Weisen Sie es an, einen Editor-Befehl zu verwenden, der die Crontab nach Ihren Wünschen ändert. Der Editor-Befehl wird als Shell-Snippet mit dem Namen einer angehängten temporären Datei ausgeführt.

unset VISUAL
EDITOR='update_crontab () {
  set -e
  new=$(mktemp)
  if <"$1" grep -v "^#" | grep -w do_stuff; then
    # Remove existing entries containing do_stuff
    grep -v -w do_stuff "$1" >"$new"
  else
    cp "$1" "$new"
  fi
  # Add the new entry
  echo "1 2 3 4 5 do_stuff --new-options" >>"$new"
  mv "$new" "$1"
}
update_crontab' crontab -e

Dieser Ansatz ist zuverlässiger als der native, crontab -l | … | crontab -da dieser für eine Race-Bedingung anfällig ist, wenn die Crontab gleichzeitig bearbeitet wird: Änderungen, die zwischen dem Aufruf von crontab -lund dem Aufruf von vorgenommen werden crontab -, werden rückgängig gemacht.

Gilles 'SO - hör auf böse zu sein'
quelle
1

Dies ist eine Anpassung dessen, was @ phillip-zyan-k-lee-stockmann angeboten hat, basierend auf seinem Code "Beste Idee bisher".

Meine Änderungen von seinem (ausgezeichneter und hilfreicher Ausschnitt) sind im Grunde:

  • Regex für nicht nur den Befehlsnamen, sondern auch den gesamten Eintrag einschließlich Zeitzeichenfolgen. Auf diese Weise kann das Hinzufügen eines Befehls unterstützt werden, selbst wenn in anderen Einträgen Befehle mit demselben Namen oder mit überlappendem Namen vorhanden sind. (Es wird immer noch nicht zweimal derselbe Befehl zum gleichen Zeitplan hinzugefügt.)
  • Ein bisschen Protokollierung
  • Ich habe meine aus verschiedenen Gründen auf stündlich umgestellt (und benannt). Einfache Anpassung gemäß Crontab-Syntax

Und hier ist mein Code für das, was ich genannt habe crontab-add-hourly.sh:

#!/bin/bash

# PURPOSE:
# To allow simple, programmatic addition of commands/entries into the crontab (if not already present)

cmd=$1
entry="0 * * * * $cmd"
printf "we want to add this entry:\n$entry\n\n" 
escapedEntry=$(printf '%s\n' "$entry" | sed 's:[][\/.^$*]:\\&:g') #from: /unix//a/129063/320236
printf "but first we'll see if it's already in there using this regex pattern:\n$escapedEntry\n\n"

if [[ $(crontab -l | egrep -v '^(#|$)' | grep -q "$escapedEntry"; echo $?) == 1 ]] # from: /unix//a/297377/320236
then
    printf "all clear; pattern was not already present; adding command to crontab hourly:\n$cmd\n\n"
    (crontab -l ; printf "$entry\n\n") | crontab -
else
    printf "pattern already present; no action taken\n\n"
fi

Beispiel für Verwendung und Ausgabe:

$ ./crontab-add-hourly.sh my-script.bash

we want to add this entry:
0 * * * * my-script.bash

but first we'll see if it's already in there using this regex pattern:
0 \* \* \* \* my-script\.bash

all clear; pattern was not already present; adding command to crontab hourly:
my-script.bash
user1417853
quelle
0

TL; DR: Dies funktioniert tatsächlich, getestet in Bash 4.4.

if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
    set -f
    printf "$(crontab -l ; echo '* * * * * some_command')\n" | crontab -
    set +f
fi

Wie in den Kommentaren von @Phillip -Zyan K Lee-Stockmann erwähnt, wird diese Lösung *in alle Dateien im aktuellen Verzeichnis erweitert. Ich konnte den Vorschlag der Kommentare nicht zum Laufen bringen. set -f deaktiviert die Platzhaltererweiterung, siehe /programming//a/11456496/915441 .

Yngvar Kristiansen
quelle