ubuntuusers.de

Cron Syntax - Crontab - Script etc

Status: Gelöst | Ubuntu-Version: Ubuntu 14.04 (Trusty Tahr)
Antworten |

itoss

Avatar von itoss

Anmeldungsdatum:
4. April 2014

Beiträge: 419

Hallo Forum,

Ich habe mich gestern mit cron beschäftigt und der hat mich fast zu Verzweiflung gebracht. es geht um ein script das jede Minute die Uhrzeit in eine Datei schreibt und nach einem Neustart die Zeit aus der Datei wieder neu setzt ( der raspberrypi hat keine Hardwareuhr und nach nem Reboot steht die unixzeit wieder auf 0 ( 1.1.1970 0:00 ) mit dem Datum passen aber die VPN Zertifikate nicht mehr und das System kommt nicht ins Netz um sich per ntp und co die aktuelle zeit zu holen, ein paar Minuten zeit Differenz lässt das VPN zu aber keine 44 Jahre )

dazu habe ich mit script geschrieben welche 2 crontab Einträge erstellt :

1
2
*/1  *    *    *   *    date +%m%d%H%M%y > /var/log/timestatus
@reboot                 date -s $(cat /var/log/timestatus)

packe ich die beiden Befehle in Scripte und lasse die per cronjob aufrufen klappt es. Ich tippe auf das Umleitungszeichen > und das dies nur als Umleitung der cronjob Ausgabe genutzt wird anstatt als Teil des Befehls.

versucht habe ich : Alles in "" zu setzen, absolute Pfade angeben /bin/./date .... , > mit \ zu escapen.

Jetzt meine Frage : kann ich Befehle direkt von crontab ausführen lassen, wenn ja wie lautet die genaue Syntax für o.g. Problem ? gibt es einen einfach befehl mit ein cronjob per script angelegt werden kann ?

1
crontab -l | { cat; echo "@reboot /usr/local/bin/./rpi-set_time"; } | crontab - 

kann ich mir auch recht schwer merken. Bevor ich den Befehlssatz fand hab ich einfach direkt in die /var/spool/cron/crontabs/root Datei geschrieben. Da wird zwar vor gewarnt lt. root crontab header - aber warum ?

PS : hat jmd vllt einmal ein guten Link zu Infos mit der verarbeitung von Sonderzeichen wie '"*/!= usw. Ich Beis mir da regelmäßig die Zähne dran aus sei es mit sed die oder o.g. cronjob Geschichte oder oder oder ...

Für Infos wär ich sehr dankbar.

g00d.morning

Anmeldungsdatum:
20. Februar 2013

Beiträge: 330

Hallo itoss,

die Ausgabe-Umleitung (>) stellt kein Problem dar, wohl aber die "%" in dem date-Befehl. Die müssen escaped werden, da sie sonst innerhalb der crontab von cron als Befehlsende verstanden werden. Richtig wäre also

*/1 * * * * /usr/bin/date +\%m\%d\%H\%M\%y > /var/log/timestatus

Die absoluten Pfade sind tatsächlich notwendig (es sei denn, dass Du Variable PATH definiert hast. Das ist im Wiki im Cron-Artikel aber auch gut erklärt.

Zu Deiner zweiten Frage. Natürlich gibt es einen Grund, dass man mit crontab arbeitet. Hier wird die Syntax gecheckt und Fehler angemahnt, so wird zumindest bis zu einem gewissen Grad garantiert, dass Du Dein System nicht schrottest (sehr drastisch und salopp ausgedrückt).

Dieser Befehl

crontab -l | { cat; echo "@reboot /usr/local/bin/./rpi-set_time"; } | crontab -               #(Nachtrag: Der Pfad kann aber so auch nicht stimmen)

Hängt etwas an die crontab an. (Erst wird die crontab ausgegeben, dann ein echo Befehl, dann geht es wieder an die crontab).

Aber warum benutzt Du nicht einfach crontab -e um einen Befehl einzufügen? Lies Dir nochmal den Wiki-Artikel durch und schau in die manpage von crontab.

Grüße

itoss

(Themenstarter)
Avatar von itoss

Anmeldungsdatum:
4. April 2014

Beiträge: 419

AHHH die % warn es ... werd das gleich mal ändern.

warum ich crontab -e nicht nutze ? crontab -e hat keine möglichkeit per script zeilen hinzuzufügen; zur manuellen bearbeitung ist crontab -e sicher ne super sache, aber zu automitschen erstellen von cronjobs hab ich noch keine syntax für den befehlt crontab -e gefunden ( eine ähnliche syntax wie oben gibs mit crontab -e | bla und so bla | bla ), di sah allerdigs noch komplizierter aus als die o.g.

zum Pfad : setzte ich kein ./ vor den Befehl wird dieser nicht ausgeführt ( War bei 12.04 so, seit dem setz ich vor jeden befehl ein ./ )

g00d.morning

Anmeldungsdatum:
20. Februar 2013

Beiträge: 330

itoss schrieb:

warum ich crontab -e nicht nutze ? crontab -e hat keine möglichkeit per script zeilen hinzuzufügen; zur manuellen bearbeitung ist crontab -e sicher ne super sache, aber zu automitschen erstellen von cronjobs hab ich noch keine syntax für den befehlt crontab -e gefunden ( eine ähnliche syntax wie oben gibs mit crontab -e | bla und so bla | bla ), di sah allerdigs noch komplizierter aus als die o.g.

OK, das war mir nicht so klar. Generell sieht es so aus.

echo <cronbefehl> | crontab -

Fertig. Die crontab wird aber eben überschrieben. Soll es angehängt werden würde ich es so lösen:

( crontab -l; echo <cronbefehl> ) | crontab -

zum Pfad : setzte ich kein ./ vor den Befehl wird dieser nicht ausgeführt ( War bei 12.04 so, seit dem setz ich vor jeden befehl ein ./ )

Da liegt bei Dir aber ein Missverständnis vor. Der "." steht nur für das aktuelle Verzeichnis. Wenn Du es absolut angibst entfällt der "." natürlich.

Nochmal ein Nachtrag. Wenn Du generell viele cron-Befehlszeilen manipulieren willst, kannst Du das auch in einer separaten Datei machen, die der cron-Syntax folgt. Da hast Du dann völlige Narrenfreiheit bis Du das Ganze per

cat <meinecrondatei> | crontab -

in die crontab überträgst.

itoss

(Themenstarter)
Avatar von itoss

Anmeldungsdatum:
4. April 2014

Beiträge: 419

Danke für die tipps und syntax 😉

zum ./ : scripte führe ich mit vorangestelltem ./scriptname.sh aus ( im Terminal ) daher dachte ich das muss in den crontab auch rein. bei try and error kommt sowas dann raus ;/ aber ic hweis ja nu wie es sich mit der cronsyntax verhält und worauf ich achten muss.

in dem sinne danke für die Hilfe 😉

das ist bei der ganzen geschichte herausgekommen :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/bash
#
# name          : rpi-timestatus_crondirect.sh
# desciption    : save restore timestatus for RPI power loss / crash
# autor         : Speefak
# licence       : (CC) BY-NC-SA
#
########################
### define variables ###
########################

ROOTCRONTAB=/var/spool/cron/crontabs/root
LOGFILE=/var/log/timestatus
TIMEINTERVAL=1

########################
### define functions ###
########################

########################################
######## check root/user status ########
########################################
clear
##deny root execution
#echo
#if [ "$(whoami)" != "root" ];then echo "Configure system for $USER";else echo "execution for root denied";exit 1;fi
#echo
#------------------------------------------------------------------------------------------------------------
#deny user execution
if [ "$(whoami)" = "root" ];then echo "";else echo "Are You Root ?";exit 1;fi

# install cron deamon
if [[ "$(which cron | grep -c cron)" == "1" ]] ;then
        echo "cron daemon already installed"
else
        echo "installing cron deamon"
        apt-get install cron -y
fi

# check existing root crontab
if	[[ -f $ROOTCRONTAB ]];then
	echo "root crontab found in $ROOTCRONTAB"
else
	touch $ROOTCRONTAB
	echo "create root crontab in $ROOTCRONTAB"
fi

# check / create crontab saving timestatus to logfile
if	[[ "$(crontab -l | grep -c '.*/1  .*    .*    .*    .*   /bin/date +.*%m.*%d.*%H.*%M.*%y > '$LOGFILE'')" == "1" ]];then
	echo "rpi-timestatus entry in $ROOTCRONTAB found => $(cat $ROOTCRONTAB|grep '.*/1  .*    .*    .*    .*   /bin/date +.*%m.*%d.*%H.*%M.*%y > '$LOGFILE'')"
else
	echo "Add root crontab entry ==> */$TIMEINTERVAL  *    *    *    *   "'/bin/date +\%m\%d\%H\%M\%y > '$LOGFILE''" <== in '$ROOTCRONTAB'"
	crontab -l | { cat; echo "*/$TIMEINTERVAL  *    *    *    *   "'/bin/date +\%m\%d\%H\%M\%y > '$LOGFILE''""; } | crontab -
fi

# check / create crontab loading timestatus from logfile
if	[[ "$(crontab -l | grep -c '@reboot                 /bin/date -s $(cat /var/log/timestatus)')" == "1" ]];then
	echo "rpi-timestatus entry in $ROOTCRONTAB found => $(crontab -l | grep '@reboot                 /bin/date -s $(cat /var/log/timestatus)')"
else
	echo 'Add root crontab entry ==> @reboot                 /bin/date -s "$(cat $LOGFILE)"                   <== in '$ROOTCRONTAB''
	crontab -l | { cat; echo "@reboot                 /bin/date -s "'$(cat '$LOGFILE')'""; } | crontab -
fi

/etc/init.d/cron restart

g00d.morning

Anmeldungsdatum:
20. Februar 2013

Beiträge: 330

Da kann man aber noch einiges optimieren. Ich habe mal ein paar Anmerkungen eingefügt:

itoss schrieb:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/bash
#
# name          : rpi-timestatus_crondirect.sh
# desciption    : save restore timestatus for RPI power loss / crash
# autor         : Speefak
# licence       : (CC) BY-NC-SA

########################
### define variables ###
########################

ROOTCRONTAB=/var/spool/cron/crontabs/root
LOGFILE=/var/log/timestatus
TIMEINTERVAL=1
# Variablen besser klein schreiben. Das System "schreibt" seine Variablen groß, so kommt man sich nicht in die Quere

########################################
######## check root/user status ########
########################################

# würde ich so testen:
#if [ "$UID" -ne 0 ]; then
if [ "$(whoami)" = "root" ];then echo "";else echo "Are You Root ?";exit 1;fi

# install cron deamon
if [[ "$(which cron | grep -c cron)" == "1" ]] ;then
# könnte man einfacher checken:
# if which cron > /dev/null; then
        echo "cron daemon already installed"
else
        echo "installing cron deamon"
        apt-get install cron -y
fi

# check existing root crontab # komplett überflüssig > wird wenn nötig erstellt 
if	[[ -f $ROOTCRONTAB ]];then
	echo "root crontab found in $ROOTCRONTAB"
else
	touch $ROOTCRONTAB
	echo "create root crontab in $ROOTCRONTAB"
fi

# check / create crontab saving timestatus to logfile
if	[[ "$(crontab -l | grep -c '.*/1  .*    .*    .*    .*   /bin/date +.*%m.*%d.*%H.*%M.*%y > '$LOGFILE'')" == "1" ]];then
	echo "rpi-timestatus entry in $ROOTCRONTAB found => $(cat $ROOTCRONTAB|grep '.*/1  .*    .*    .*    .*   /bin/date +.*%m.*%d.*%H.*%M.*%y > '$LOGFILE'')"
else
	echo "Add root crontab entry ==> */$TIMEINTERVAL  *    *    *    *   "'/bin/date +\%m\%d\%H\%M\%y > '$LOGFILE''" <== in '$ROOTCRONTAB'"
	crontab -l | { cat; echo "*/$TIMEINTERVAL  *    *    *    *   "'/bin/date +\%m\%d\%H\%M\%y > '$LOGFILE''""; } | crontab -
fi
#
# Dein grep-Befehl ist unsinnig. ".*" hintereiander? Ich weiß nicht wo ich anfangen soll. Lies Dich nochmal ein. es geht auch wieder über:
# if grep xyz > /dev/null
# (wie man sich das "cat" sparen kann habe ich oben auch schon gezeigt)
# gilt auch beides für das nächste

# check / create crontab loading timestatus from logfile
if	[[ "$(crontab -l | grep -c '@reboot                 /bin/date -s $(cat /var/log/timestatus)')" == "1" ]];then
	echo "rpi-timestatus entry in $ROOTCRONTAB found => $(crontab -l | grep '@reboot                 /bin/date -s $(cat /var/log/timestatus)')"
else
	echo 'Add root crontab entry ==> @reboot                 /bin/date -s "$(cat $LOGFILE)"                   <== in '$ROOTCRONTAB''
	crontab -l | { cat; echo "@reboot                 /bin/date -s "'$(cat '$LOGFILE')'""; } | crontab -
fi

/etc/init.d/cron restart # überflüssig

Hier wäre mal eine alternative Version, die nicht nur unter Raspbian funktioniert:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
#depends cron(d)
interval=1
logfile=/var/log/timestatus
dateP=$(which date)
catP=$(which cat)

#check root privileges
if [ "$UID" -ne 0 ]; then
    echo "This script must run as root"
    exit 1
fi

#check whether cron is installed
if ! which crontab > /dev/null; then
    echo "cron is not installed on your system."
    exit 1
fi

# creating entry for writing rpi-timestatus
if crontab -l | grep "#rpi-timestatus_write"$ > /dev/null; then
    echo "entry for saving your timestatus already found in crontab"
else
    echo "adding crontab entry: \"*/$interval * * * * $dateP +\%s > $logfile\""
    ( crontab -l; echo "*/$interval * * * * $dateP +\%s > $logfile #rpi-timestatus_write" ) | crontab -
fi

# creating entry for reading rpi-timestatus
if crontab -l | grep "#rpi-timestatus_read"$  > /dev/null; then
    echo "entry for reading your timestatus already found in crontab"
else
    echo "adding crontab entry: \"@reboot $dateP -s @\$($catP $logfile)\""
    ( crontab -l; echo "@reboot $dateP -s @\$($catP $logfile) #rpi-timestatus_read" ) | crontab -
fi

itoss

(Themenstarter)
Avatar von itoss

Anmeldungsdatum:
4. April 2014

Beiträge: 419

Dein script sieht irgendwie "ordentlicher" aus 😉

beim Vergleichen sind ein paar Fragen aufgetaucht, hauptsächlich die Syntax betreffend ( die Sache mit dem path variablen is auch super, da hätte ich auch noch drauf kommen können, genau wie den Zeitstempel zu nutzen 😉 )

was mir noch nicht so klar ist :

- wann müssen die if Abfrage Parameter in Klammern und wann nicht und wann in doppelte und wann nicht ? (   if [[ ]] ... , if [ ] ... )
- wie verarbeitet if die Zeilen ohne jegliche Angaben ( if crontab -l | grep "#rpi-timestatus_write"$ > /dev/null; then ... ) und woher weiß if/else ob die Bedingung wahr ist oder nicht ? geht if nach dem Prinzip :  Ausgabe = irgendetwas = true ; keine Ausgabe = false ?

 - alles was in diesem Ausdruck zwischen den \...\ ( \escape expression\ ) steht wird nicht interpretiert/geparst und so als string 1:1 weiterverarbeitet ? Das wäre VIEL einfacher als alles mit 
'' oder "" einzeln zu escapen ( gibs da eigentlich auch ein deutschen Ausdruck für ? ausklammern passt nicht so ganz ), wobei mir der Unterschied zwischen "" und '' auch nicht so richtig klar ist ( bei '' mehr Zeichen ausgeklammert als bei "" )

g00d.morning

Anmeldungsdatum:
20. Februar 2013

Beiträge: 330

itoss schrieb:

- wann müssen die if Abfrage Parameter in Klammern und wann nicht und wann in doppelte und wann nicht ? ( if [[ ]] ... , if [ ] ... )
- wie verarbeitet if die Zeilen ohne jegliche Angaben ( if crontab -l | grep "#rpi-timestatus_write"$ > /dev/null; then ... ) und woher weiß if/else ob die Bedingung wahr ist oder nicht ? geht if nach dem Prinzip : Ausgabe = irgendetwas = true ; keine Ausgabe = false ?

if macht eigentlich sehr wenig. Es schaut nur nach dem exit-Status eines Programmes. Wenn ein Programm 0 (Erfolg) meldet, nimmt es diesen Weg. (Wenn z.B. grep nichts findet, gibt es 1 aus)

Oft Kombiniert man das mit dem test-Kommando die einfachen Klammern sind nur eine andere Form dazu. test gibt 0 aus, wenn der Test erfolgreich war. Aber das Test-Kommando ist optional. if ist nur an der 0 interessiert, wie es dazu kommt ist Herrn if schuppe. Und wenn man etwas weglassen kann, dann sollte man das auch tun. Die doppelten Klammern schalten ein paar erweiterte Funktionen von bash dazu. Es ist ein anderes Programm, was die ansonsten aber gleich ähnlich funktioniert. Das kannst Du aber alles leicht ergooglen.

- alles was in diesem Ausdruck zwischen den \...\ ( \escape expression\ ) steht wird nicht interpretiert/geparst und so als string 1:1 weiterverarbeitet ? Das wäre VIEL einfacher als alles mit oder "" einzeln zu escapen ( gibs da eigentlich auch ein deutschen Ausdruck für ? ausklammern passt nicht so ganz ), wobei mir der Unterschied zwischen "" und auch nicht so richtig klar ist ( bei mehr Zeichen ausgeklammert als bei "" )

Das liebe Quoting.

Du hast bei Deinem Code ein ziemlich wüstes Quoting veranstaltet. Wenn man (beim Quoting) etwas absolut nicht mehr lesen kann, dann ist da etwas schief gelaufen 😉 Das escaping bei bash Funktioniert aber relativ einfach. Wenn ein Zeichen nicht geparsed werden soll, kommt eben der backslash davor. Da ist nichts zwischen zwei backslashes. Das Folgezeichen wird nicht geparsed, Punkt. In einfachen Anführungszeichen wird gar nichts geparsed und das Escapen (Ist doch schon eingedeutscht phon.: Eskäpen 😉) entfällt.

itoss

(Themenstarter)
Avatar von itoss

Anmeldungsdatum:
4. April 2014

Beiträge: 419

soo hab mich nochmal an das script gesetzt und überarbeitet und einiges nun wirklich verstanden 😉

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/bin/bash
#
# name          : rpi-timestatus_crondirect.sh
# desciption    : save & restore timestatus for RPI power loss / crash
# autor         : Speefak
# licence       : (CC) BY-NC-SA
# version	: 1.0
# infosource	: http://forum.ubuntuusers.de/topic/cron-syntax-crontab-script-etc/#post-6976078
#
########################
### define variables ###
########################

ROOTCRONTAB=/var/spool/cron/crontabs/root
LOGFILE=/var/log/timestatus
UPDATEINTERVAL=2	# save every X minutes timestatus
UTSINTERVAL=3		# update every X houer unixtimestamp

date_PATH=$(which date)
cat_PATH=$(which cat)
grep_PATH=$(which grep)
cut_PATH=$(which cut)
curl_PATH=$(which curl)

########################
### define functions ###
########################

#------------------------------------------------------------------------------------------------------------
############################################
######## check root/user privileges ########
############################################
clear
#deny user execution
if [ "$UID" != 0 ]; then echo "Are You Root ?"; exit 1; fi
##deny root execution
#if [ "$UID" = 0 ]; then echo "You are root, please execute this script as user"; exit 1; fi
echo
#------------------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------------------
# install cron deamon
if ! which crontab > /dev/null; then
	echo "installing cron deamon"
	apt-get install cron -y
else
	echo "cron daemon already installed"
fi
#------------------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------------------
# check existing root crontab
if	[[ -f $ROOTCRONTAB ]];then
	echo "root crontab found in $ROOTCRONTAB"
else
	touch $ROOTCRONTAB
	echo "create root crontab in $ROOTCRONTAB"
fi
#------------------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------------------
# creating root crontab entry for reading rpi-timestatus | set timestatus @ reboot
if crontab -l | grep "#rpi-timestatus_read"$  > /dev/null; then
    echo "entry for reading raspberrypi timestatus already found in $ROOTCRONTAB"
else
    echo "adding crontab entry in $ROOTCRONTAB : @reboot                 $date_PATH -s @\$($cat_PATH $LOGFILE) #rpi-timestatus_read"
    ( crontab -l; echo "@reboot                 $date_PATH -s @\$($cat_PATH $LOGFILE) #rpi-timestatus_read" ) | crontab -
fi
#------------------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------------------
# creating root crontab entry for writing rpi-timestatus
if crontab -l | grep "#rpi-timestatus_write"$ > /dev/null; then
    echo "entry for writing raspberrypi timestatus already found in $ROOTCRONTAB"
else
    echo "adding crontab entry in $ROOTCRONTAB : */$UPDATEINTERVAL  *   *   *   *      $date_PATH +\%s > $LOGFILE #rpi-timestatus_write"
    ( crontab -l; echo "*/$UPDATEINTERVAL  *   *   *   *      $date_PATH +\%s > $LOGFILE #rpi-timestatus_write" ) | crontab -
fi
#------------------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------------------
# creating root crontab entry for rpi unixtime update
if crontab -l | grep "#rpi-update_unixtime"$ > /dev/null; then
    echo "entry for updating raspberrypi unixtime  already found in $ROOTCRONTAB"
else
echo "adding crontab entry in $ROOTCRONTAB :  *  */$UTSINTERVAL  *   *   *      $date_PATH -s @\$($curl_PATH http://www.unixtimestamp.com/index.php | $grep_PATH \"(UTC)\" | $cut_PATH -d  \" \" -f8 | $cut_PATH -d \">\" -f2) #rpi-update_unixtime"
( crontab -l; echo " *  */$UTSINTERVAL  *   *   *      $date_PATH -s @\$($curl_PATH http://www.unixtimestamp.com/index.php | $grep_PATH \"(UTC)\" | $cut_PATH -d  \" \" -f8 | $cut_PATH -d \">\" -f2) #rpi-update_unixtime" ) | crontab -
fi
#------------------------------------------------------------------------------------------------------------

/etc/init.d/cron restart

da der ntpport in dem VPN nicht durchgeleitet wird, holt sich alle 3 stunden der pi über den port 80 ( website ) den aktuellen zeitstempel,

Antworten |