Keep it running

INPUT · Slides

Running at a settled time

01 / 08

While you are not watching

Every tool from the first eleven chapters was one that runs when you type.

~ $ ./checkup.sh

Type, and it runs. Do not type, and it does not. Obvious enough.

But there is work like this.

every morning at five, clear away the logsevery five minutes, look at the free disk spaceevery Sunday, make a summary

Getting up at five to type it yourself is no good. From here on it is about running without typing.

The arrangement is very simple.

> Hand over one table with a time and a doing on each line

The one you hand it to is called crond. That one is already running.

~ $ ps -o pid,args | grep crond   77 /usr/sbin/crond -f

There it is! Since before you opened a terminal it has been looking at the clock every minute, waiting to see if there is anything to do.

The table you hand it is called a crontab — cron's table.

Three things today.

1. how to write the table (five fields)2. handing it over, looking, clearing (crontab)3. inside cron is another world (environment, output)

Give the scripts of chapter 11 a time and they become an arrangement for the first time.

02 / 08

Five fields, and the doing

One line of the table goes like this.

min hour day month weekday  the doing

Five numbers, then the command. Let us read one.

0 5 * * * /bin/echo morning
0    when the minute is 05    when the hour is 5*    any day*    any month*    any weekday

Every day at 5:00. The * is the mark for "any" (a different thing from the * of chapter 10, so do not mix them).

Here are the ranges.

FieldWhatRange
1minute0-59
2hour0-23
3day1-31
4month1-12
5weekday0-6 (0 is Sunday)

Look at the shapes you use often.

* * * * *       every minute0 * * * *       at minute 0 of every hour (hourly)0 5 * * *       every day at 50 5 * * 1       every Monday at 50 5 1 * *       the 1st of every month at 5*/5 * * * *     every 5 minutes0 9,17 * * *    at 9 and at 170 9-17 * * *    hourly from 9 to 17

The bottom three are the handy writings.

*/5    only when it divides by 5 (0,5,10,...)9,17   either (listed with a comma)9-17   that range (joined with a hyphen)

Here is where people come unstuck most.

5 * * * *     at minute 5 of every hour (once an hour)*/5 * * * *   every 5 minutes (twelve times an hour)

Many write the top one meaning "every five minutes". The first field is "when the minute is such and such", not "every so many minutes".

03 / 08

Handing over, looking, clearing

The tool for handing the table over is crontab.

crontab file        hand over what is in that filecrontab -l          look at the table you have handed overcrontab -r          clear it allcrontab -e          edit it on the spot (vi opens)

Let us type.

~ $ echo "0 5 * * * /bin/echo morning" > plan.txt~ $ crontab plan.txt~ $ crontab -l0 5 * * * /bin/echo morning

Write a file, hand it over.

There are two reasons for this way.

1. the table stays with you as a file (look back, mend it)2. crontab -e opens vi (next chapter, not today)

The first matters in the field too. If only the handed-over table is the truth, an accidental -r leaves you nothing. Written in a file, you hand it over again with crontab plan.txt.

There is one pitfall.

crontab newone.txt

This is a replacement. The table you handed over before is gone.

before   0 5 * * * A         | crontab other.txt (which says B)now      0 6 * * * B      <- A has gone!

Not an addition. So to add, write every line in the file and hand it over again.

crontab -l > now.txt            take out the table as it isecho "0 6 * * * B" >> now.txt   addcrontab now.txt                 hand it over again

Take out, add, hand back — three moves. The redirection of chapter 4 earns its keep.

04 / 08

Inside cron is another world

This is where cron trips people most.

> When it runs from cron, the environment is not your terminal's

Three differences.

1. The environment variables differ

Compared for real in this environment, it goes like this.

VariableTerminalInside cron
PATH/bin:/sbin:/usr/bin:/usr/sbin/sbin:/usr/sbin:/bin:/usr/bin
TERMxtermvt102
LANGC.UTF-8not there
EDITOR/bin/vinot there
PS1\w $not there

Made separately. The variables you exported and the settings in your .profile do not reach it. Remember "environment variables go from parent to child" from chapter 10. Cron's parent is crond, not your shell.

So the manner is this.

* * * * * /bin/echo x        write the full path

Write without leaning on PATH and it works whatever the environment.

2. The place is home

PWD=/home/learner

Wherever you are, inside cron it starts from home. So write relative paths as if from home. When in doubt, use a full path.

3. The output goes nowhere

This is the surprising one.

* * * * * /bin/echo hello

Put that in and nothing appears on the screen. It is not joined to your terminal. The output is thrown away.

On a real server this output is arranged to arrive by mail. But there is no mail tool here, so it goes quietly.

So you write this.

* * * * * /bin/echo hello >> /home/learner/result.log 2>&1

Keep it in a file yourself. The 2>&1 matters too (chapter 4). If only the errors vanish, you never notice a failure.

05 / 08

Checking that it ran

If the output vanishes, how do you check? You look at the record.

~ $ grep crond /var/log/messages... crond[77]: crond (busybox 1.37.0) started, log level 8... crond[77]: USER learner pid 88 cmd /bin/echo tick

The lower line is the record saying "I ran it".

USER learner    whose tablepid 88           what number it ran ascmd /bin/echo …  what it ran

That it ran is on record. But look closely.

on record        that it was runnot on record    the result (output, success)

Whether it went well is not there. So you use the exit status of chapter 11.

* * * * * /home/learner/checkup.sh >> /home/learner/checkup.log 2>&1 || /bin/echo NG >> /home/learner/failed.log

Keep a failure in another file. Look at failed.log later and you know.

In the next lesson you learn logger, which lets you write this more tidily.

Here is where to look when it does not run.

SymptomCommon cause
nothing happensthe time fields are wrong
a record but no resultyou did not redirect the output
not foundyou did not write the full path
cannot runyou forgot chmod +x

And the check that works best.

> Before putting it in cron, type that one line by hand

What does not work by hand will not work in cron. Trying by hand first looks like the long way round and is the quickest.

06 / 08

It joins up with chapter 11

Here the scripts of chapter 11 come alive.

#!/bin/shset -euDATE=$(date "+%Y-%m-%d")COUNT=$(ls /home/learner | wc -l)echo "$DATE $COUNT" >> /home/learner/record.log

Put it in cron and it goes like this.

0 5 * * * /home/learner/record.sh

Every morning at five, that day's file count is recorded, while you are asleep.

That is what "building an arrangement" means.

a tool (a script) + a time (cron) = an arrangement

Either on its own is not enough.

the script alone   you type it every timecron alone         only one line, so nothing complicated

With both, you can take your hands off.

Three manners for a script you put in cron.

1. write full paths (do not lean on PATH)2. write set -eu (so failures show)3. keep the output (>> log 2>&1)

And the most important manner.

> Write it short, and so that running it again gives the same result

Because cron sometimes runs twice out of step, and the next go can start before the last has finished.

>> to add        run it any number of times, it only adds (safe)> to overwrite   only the last is left (safe)rm -rf ...       two at once is an accident (dangerous)

A shape that does not break however often it runs lets you take your hands off in peace.

07 / 08

Do not make it every minute

Last, one difference between practice and the field.

In this lesson's exercises you write this.

* * * * *      every minute

Because you want to see it run at once. But in the field you do not use every minute.

running every minute  | 1440 times a day  | for anything at all heavy, the machine is busy on that alone  | the next go starts before the last has finished

You settle it by thinking about the interval you need.

must it really be every minute?  ->  almost neverwould every 5 minutes do?        ->  */5would once a day do?             ->  0 5 * * *

Choosing the longest interval is the manner.

There is another manner, about shifting the time.

0 0 * * *      what everyone picks (midnight exactly)7 3 * * *      shifted

Round times are what everyone picks, so the machine is busy just there. An odd number on purpose keeps you out of the crowd.

And after practising, always clear up.

crontab -r

Leave a table that runs every minute and the log grows and grows. Put in, then take out — one pair, remember.

08 / 08

Now have a go

Here are the shapes for this lesson.

crontab plan.txt       hand over the table (a replacement)crontab -l             look at the table nowcrontab -r             clear it allmin hour day mon wday command   the shape of a line*                      any*/5                    every 59,17                   either9-17                   that range>> log 2>&1            keep the output

And how to check.

ps -o pid,args | grep crond      is the one on duty runninggrep crond /var/log/messages     the record of running it

Three things to remember most today.

1. the first field is "when the minute is", not "every so many minutes"2. crontab file is a replacement (not an addition)3. output vanishes unless you keep it yourself

And today's conclusion.

> Hand over a time and you can take your hands off

Someone who looks at the clock every minute is already on duty. All you do is write one line of what to do and hand it over.

This lesson has two exercises that wait a minute. Cron does nothing until the minute turns, so waiting is correct. Sit and wait.

Next lesson you learn logger, the tool for keeping proper records of what cron ran. Let us type.