Back to Knowledege base

Controlling When an Xi-Batch Job Runs

The option letters for run time, repeats, avoided days and missed slots, and why a repeat stops after one failure

Understanding Time Settings

A job carries one time record. Everything in it is optional, and with none of it set the job runs as soon as the load level allows.

No time at all
The job is eligible immediately and runs when the load level allows. This is what you get if you give none of the time options.
A next run time
The earliest moment the job may start. The scheduler will start a job up to three seconds early rather than wait for the exact second.
A repeat specification
After each run that ends inside the job's normal exit-code range, the next run time is recalculated and the job stays on the queue.
Days to avoid
Days of the week, and optionally holidays, that a recalculated run time must not fall on.
An action for a missed slot
What to do if the scheduler reaches the job after its run time has already gone by.

Two further settings are about the end of the job rather than the start: a maximum elapsed run time, and a time after which a finished job is removed from the queue.

Which Option Sets Which

Several of these letters are easy to confuse, and three of the wrong ones are accepted silently rather than refused. Check this table before writing a command line.

OptionLong formSets
-T--timeThe next run time
-r--repeatThe repeat specification
-A--avoiding-daysDays of the week and holidays to avoid
-S--skip-if-heldMissed slot: skip it
-H--hold-currentMissed slot: run late, keep the schedule (the default)
-R--reschedule-allMissed slot: run late, move the schedule
-9--catch-upMissed slot: one run, keep the schedule
-j--advance-time-errorAdvance the time after a failed run (the default)
-J--no-advance-time-errorLeave the time alone after a failed run
-d--delete-at-endRun once, then delete from the queue (the default)
-o--no-repeatRun once, then retain on the queue marked Done
-U--no-timeRemove the time setting entirely
-t--delete-timeHours after a job ends before it is deleted
-Y--run-timeMaximum elapsed run time
-W--which-signalSignal to send when the run time is exceeded
-2--grace-timeGrace period before SIGKILL follows that signal

The four that are commonly written by mistake:

  • -t is the delete time in hours, not the run time. btr -t "03:00" job.sh is accepted, sets a three-hour auto-delete, and leaves the job with no run time at all - the argument is read with atoi, so everything after the first number is discarded without a message.
  • -a is --argument. btr -a "Sat,Sun" job.sh passes the string Sat,Sun to the job as a command-line argument.
  • -E is --local-environment, which applies only to rbtr and is ignored by btr.
  • -D is --directory. btr -D 24 job.sh sets the job's working directory to a directory called 24.

Setting the Run Time

The formats -T accepts:

hh:mm                   Time today, or tomorrow if it has passed
dd/mm                   Date this year, midnight
dd/mm,hh:mm             Date and time
yy/mm/dd                Two-digit year
yy/mm/dd,hh:mm
yyyy/mm/dd,hh:mm        Four-digit year

The date and the time are separated by a comma. Nothing else is accepted: there are no words such as tomorrow or Monday, no ISO dates such as 2026-02-15, no space between the date and the time, and no seconds field. Anything else is refused with Bad time specification and exit status 2, and a stray character after an otherwise valid time gives Bad char `c' in time spec.

# Run at 03:00 - today if 03:00 is still to come, otherwise tomorrow
btr -T "03:00" script.sh

# Run at 14:30 on 15 February 2026
btr -T "2026/2/15,14:30" script.sh

# Run at 18:00 on 5 January, next occurrence
btr -T "5/1,18:00" script.sh

Two-digit dates depend on the time zone. In zones four or more hours behind UTC a bare 3/4 is read as month/day; everywhere else as day/month. Four-digit years and the yy/mm/dd form are unambiguous, and are the safer choice for a site that spans time zones.

A bare time rolls forward. If the hour and minute you give are earlier than now, or the same minute, the date is advanced by one day. So -T "03:00" never produces a time in the past.

A date in the past is treated differently for the two kinds of job. For a job that runs once, btr refuses it - Time given is not in future, exit status 2. For a repeating job the time is advanced by whole repeat intervals until it lands in the future; add -v and the command prints the result as a warning.

Seconds are always zero. The value stored is built from whole minutes.

In btq:

  1. Move to the job on the job list
  2. Press t
  3. Answer the prompts in turn: whether a time applies, the date and time, the repeat, the days to avoid, and the missed-slot action
  4. On the day and avoid rows, t s y set a value and f u n unset it; + and - step a number; h and l (or the arrow keys) move

Repeat Specifications

The argument to -r is a unit name, a colon and a count, and for the two monthly units a second colon and a target day. The unit names are matched without regard to case, and a bare number with no unit is read as hours.

UnitMeaningLargest count accepted
MinutesEvery n minutes527040
HoursEvery n hours (the unit assumed if none is given)8784
DaysEvery n days1000
WeeksEvery n weeks520
MonthsbEvery n months, on a target day counted from the start of the month50
MonthseEvery n months, on a target day counted back from the end of the month50
YearsEvery n years10

A count above the limit is refused with Bad repeat statement, as is an unknown unit name or a missing colon.

Simple Repeat Examples

Every 10 minutes:

btr -r Minutes:10 script.sh

Every 2 hours:

btr -r Hours:2 script.sh

Every day at the same time:

btr -T "03:00" -r Days:1 backup.sh

Every 2 weeks, starting on a particular Monday:

btr -T "2026/2/2,09:00" -r Weeks:2 report.sh

A repeat on its own is enough to give the job a time: -r sets the "time applies" marker, and if you give no -T the first run time is the moment the job was submitted, rounded up to the next minute.

Monthly Repeats

Months relative to the beginning. The target day is the day of the month to aim for, and it is used unchanged every month. If a month is shorter than the target day, the last day of that month is used.

# The 5th of every month at 10:00
btr -T "2026/2/5,10:00" -r Monthsb:1:5 invoice.sh

# The 15th of every third month at 14:00
btr -T "2026/2/15,14:00" -r Monthsb:3:15 quarterly.sh

Months relative to the end. The target day is interpreted once, against the length of the month named in -T, and is then stored as a number of days back from the end of the month. From then on it applies to every month whatever its length, so there is nothing to change for February.

# Last day of every month at 23:00 - March has 31 days, so 31
btr -T "2026/3/31,23:00" -r Monthse:1:31 month-end.sh

# The same result, set against February - 28 days, so 28
btr -T "2026/2/28,23:00" -r Monthse:1:28 month-end.sh

# Second to last day of every month
btr -T "2026/3/30,23:00" -r Monthse:1:30 pre-month-end.sh

A target day at or beyond the length of the -T month is taken as the last day. A target day that would put the run more than twenty days before the end of the month is refused with The effective "target day" of your months-relative-to-end parameter could take you back past the beginning of the month on the next repeat, so Monthse is for the last third of the month only. Use Monthsb for anything earlier.

How the Next Run Time Is Calculated

The new time is calculated from the scheduled time, not from the time the job actually ran. A job that starts late because of load or a condition returns to its original grid.

Minutes, Hours, Days and Weeks
The interval in seconds is added to the scheduled time.
Monthsb
The day of the month is moved to the target day, then the length of each intervening month is added.
Monthse
The stored number of days back from the end of the month is applied to the month reached.
Years
The year is incremented and the time rebuilt from the calendar, so the local clock time is preserved.

Example - every 2 hours:

Scheduled:  10:00   ran at 10:00
Next:       12:00   ran at 12:47 (held up by load)
Next:       14:00   not 14:47

The one exception is -R, described under When a Run Time Is Missed below, which deliberately rebases the schedule on the time the job actually started.

Avoiding Days

The argument to -A is a comma-separated list of day names, matched without regard to case. The names are the short ones only.

  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • Hday - holidays, from the holiday calendar

As shipped, Sat and Sun are already avoided. The defaults come from the help file, and a job submitted with no -A at all inherits them. This matters twice over: adding -A "Sat,Sun" changes nothing, and a job that really must run every day needs the defaults cleared.

# Replaces the list - this job now avoids ONLY holidays, not weekends
btr -T "09:00" -r Days:1 -A "Hday" job.sh

# Adds to the list - a leading comma keeps the defaults
btr -T "09:00" -r Days:1 -A ",Hday" job.sh

# Clears the list - runs every day including weekends
btr -T "09:00" -r Days:1 -A "-" job.sh

# Clears the list and then sets one day
btr -T "09:00" -r Days:1 -A "-,Wed" job.sh

An unrecognised name is refused with a message that restates the syntax, and a list naming every day of the week is refused separately. Confirm what a job actually carries with btjlist -N -F "%a" <job_number>.

In btq:

  1. Move to the job on the job list
  2. Press t
  3. Step past the time and repeat prompts to the Avoiding row
  4. Move along it with h and l or the arrow keys; t s y set a day, f u n unset it, ! toggles

Avoid Days Behaviour

Avoidance is applied when a run time is calculated - at the end of a run, and when a past time given to btr is advanced into the future. A time you set explicitly with -T is used exactly as given, so the first run of a job can fall on an avoided day even though every later one will not.

Every unit except Monthse moves forward:

Schedule: Daily 09:00, avoiding Sat and Sun
Friday:     09:00 - runs
Saturday:   09:00 - avoided
Sunday:     09:00 - avoided
Monday:     09:00 - runs

Monthse moves backward:

Schedule: last day of month, avoiding Sat, Sun and Hday
Month ends Saturday:  Friday is used
Month ends Sunday:    Friday is used
Month ends a holiday: the previous acceptable day is used

This is why "last working day of the month" is a Monthse job and cannot be built out of any other unit. A weekly job that lands on a holiday moves forward to the next acceptable day, so a Friday payroll job avoiding weekends and holidays runs on the following Monday rather than on the Thursday before.

When a Run Time Is Missed

The scheduler compares each job's next run time with the clock on every pass. If the time has already gone by - because the scheduler was stopped, because the previous run overran, because the load level or a condition held the job, or simply because the machine was busy - what happens next depends only on the job's missed-slot setting. Conditions and load levels are tested afterwards, and they do not change this decision.

-H, hold current - the default
Run now. The next run time then advances by one interval from the slot that was missed, so if several slots went by the job runs once for each of them, one after another, until it has caught up.
-R, reschedule all
Run now, and reset the next run time to the moment the job actually started. The whole schedule moves by the amount of the delay.
-9, catch up
Discard the intervening slots, run once, and stay on the original grid.
-S, skip if held
Run anyway if the job is only slightly late; otherwise do not run at all - move the next run time forward past the present and wait for it.

Example - an hourly job whose 10:00 and 11:00 slots both go by, unblocked at 11:30:

-H (default): runs at 11:30, runs again immediately, next slot 12:00
-R:           runs at 11:30, next slot 12:30
-9:           runs once at 11:30, next slot 12:00
-S:           does not run, next slot 12:00

A job that runs once always runs, however late. The missed-slot setting applies to repeating jobs; a -d or -o job whose time has passed is started at the first opportunity.

How late is "slightly late". Only -S uses a tolerance. It is a tenth of the repeat interval for Minutes and Hours, and a tenth of half the nominal interval for everything above.

RepeatStill runs if less than this late
Minutes:nn × 6 seconds
Hours:nn × 6 minutes
Days:nn × 72 minutes
Weeks:nn × 8 hours 24 minutes
Monthsb:n, Monthse:nn × 1 day 10 hours
Years:nn × 16 days 19 hours

Nothing is recorded when a slot is skipped. There is no job-log entry and no line in the scheduler report file. The only evidence is that the time column has moved on without a corresponding run.

Read a job's setting with btjlist -N -F "%w" <job_number>; it prints Skip, Delay, Delall or Ctchup.

Daylight Saving and Time Zones

Xi-Batch holds every time as an internal clock value, and the repeat arithmetic for Minutes, Hours, Days, Weeks and both monthly units is done on that value in whole seconds. A daylight-saving change therefore moves the displayed time of those jobs: a daily job set for 03:00 runs at 02:00 or 04:00 local time once the clocks change, and stays there. Years is the exception - a yearly repeat is rebuilt through the calendar and keeps its local clock time.

There is no automatic correction. The command that makes it is btdst, which is a link to btstart and is installed with it.

btdst [-R] start-date end-date adjustment

The two dates take the same forms as the btdst reference page describes - dd/mm, mm/dd or yy/mm/dd, chosen by time zone exactly as for -T, optionally followed by a comma and hh:mm, with midnight assumed. The adjustment is a number of seconds, and the sign is the one that surprises people:

  • Clocks going forward (into summer time): use a negative adjustment, normally -3600. The stored time is unchanged by the transition, so it now displays an hour later than it did; subtracting an hour puts it back.
  • Clocks going back: use a positive adjustment, normally 3600.
# UK, clocks forward on 29 March 2026: correct the jobs due that week
btdst 29/3 5/4 -3600

# UK, clocks back on 25 October 2026
btdst 25/10 1/11 3600

What btdst changes, and what it leaves alone. It adds the adjustment to the next run time of every job whose next run time falls between the two dates, and writes a dst adjust entry to the job log for each one. It leaves alone: jobs with no time set; jobs repeating in Minutes or Hours, on the grounds that an hour makes no difference to them; and jobs owned by another host, unless -R is given. The reference manual advises against -R, and the code supports the advice - the adjustment is applied to this scheduler's copy only and is not sent to the owning host.

It is a one-off sweep, not a policy. Nothing runs btdst automatically and the repeat rule is unchanged, so it has to be run again at every transition. The natural arrangement is an Xi-Batch job of its own, repeating yearly, for each of the two transitions.

It requires a running scheduler and Write administration file privilege, and reports Scheduler not running (exit 6) or No permission for DST adjustment (exit 3) if either is missing. A start date at or after the end date, or an adjustment of zero, is refused.

One more effect worth knowing. When btr converts the time you type, it uses the UTC offset in force at midday on the date you named. On the date of a transition that is the offset for the afternoon, so a time set for the small hours of that one date can come out an hour away from what you asked for. Setting such a job for a time well clear of the transition avoids the question entirely.

What Happens After a Run

When a job finishes, the scheduler moves it to the back of the queue and recalculates its next run time. Whether it becomes eligible again depends on how it ended.

Ended inside the normal exit-code range
The time is advanced and the progress code is cleared, so the job is eligible for its next slot.
Ended in error, aborted, or cancelled
The time is advanced, and the progress code is left at Err, Abrt or Canc. A job in one of those states is passed over by every later pass of the scheduler, so the job never runs again until someone clears it.

The normal and error ranges are a property of the job, set with -X; the defaults are 0 for normal and 1 to 255 for error.

-j and -J control only the time, not the progress code. -j, the default, advances the next run time after a failed run. -J leaves it where it was. Neither makes the job repeat after a failure. What -J does give you is the evidence: the job's time column stops moving, which is visible in a listing, whereas with the default the schedule goes on looking healthy while nothing runs.

Clearing a stuck job:

# Look for a repeat specification alongside a progress code of Err, Abrt or Canc
btjlist -H -F "%N %H %P %T %r"

# Make one eligible again
btjchange -N <job_number>

In btq the same thing is the P key on the job list, setting the progress code back to nil.

Running Once, Retaining and Deleting

Run once and delete (the default):

btr script.sh

The job is removed from the queue when it finishes.

Run once and retain:

btr -o script.sh

The job stays on the queue marked Done. It can be made eligible again with btjchange -N.

Delete a finished job after a delay:

# Remove it 24 hours after it ends
btr -o -t 24 script.sh

The delete time is a number of hours, counted from the moment the job ended. It applies only to jobs sitting in Done, Err, Abrt or Canc, and only on the machine that owns the job; a job that has never run is never removed by it. The removal is written to the job log as auto delete. A value of 0, the default, keeps the job indefinitely.

Remove the time settings altogether:

btjchange -U <job_number>

Limiting How Long a Job May Run

-Y sets a maximum elapsed run time, as seconds, mm:ss or hh:mm:ss. When it is exceeded the scheduler kills the job. With -W and -2 it does so in two stages: the signal named by -W first, logged as exceeded runtime, and then SIGKILL after the grace period given by -2, logged as exceeded grace period. Without a signal or a grace period it goes straight to SIGKILL.

# Give the job an hour, then SIGTERM, then SIGKILL 30 seconds later
btr -T "02:00" -r Days:1 -Y 1:00:00 -W 15 -2 30 long-report.sh

A job killed this way ends outside its normal exit range, so a repeating job stops repeating - see What Happens After a Run.

Running a Job Outside Its Schedule

Three commands override the run time without changing it permanently. All three are links to btjdel and take job numbers.

btjgo
Run the job now, ignoring the run time. The next run time is untouched, so this inserts an extra run.
btjgoadv
Run the job now and advance the next run time, bringing the next run forward and then resuming the sequence.
btjadv
Advance the next run time without running the job and without looking at conditions.

Conditions and load levels still apply to btjgo and btjgoadv. On the btq job list the same three operations are f (run now, leaving the time alone), g (run now and advance the time) and a (advance the time only) - note that the two letters are the opposite way round from the way the command names read.

Reading and Changing a Job's Time Settings

The listing escapes are the reliable way to see what a job actually carries.

btjlist -H -F "%N %T %r %a %w %d" <job_number>
EscapeShows
%tNext run time, short - hh:mm if it is within a day, otherwise a date
%TNext run time in full, dd/mm/yyyy hh:mm
%rRepeat specification, or Delete or Retain
%aDays avoided
%wMissed-slot action: Skip, Delay, Delall or Ctchup
%dDelete time in hours
%l %y %gMaximum run time, signal, grace period
%b %fTime the last run started and ended

btjlist -T switches the default format from %t to %T, which is the quickest way to see full dates without writing a format string.

Every time option above is also accepted by btjchange, which takes job numbers and applies the change to each one.

# Move a job to 04:00 and make it skip missed slots
btjchange -T "04:00" -S <job_number>

Complete Scheduling Examples

Daily Backup, Weekdays Only

btr -T "02:00" \
    -r Days:1 \
    -A "Sat,Sun,Hday" \
    -J \
    -f S -s 'backup_status=Running' \
    -f N -s 'backup_status=Complete' \
    -f EA -s 'backup_status=Failed' \
    /usr/local/bin/backup.sh

The assignment syntax is covered in the conditions and assignments article; -J is here so that a failure leaves the stale time visible.

Month-End Processing on the Last Working Day

btr -T "2026/3/31,18:00" \
    -r Monthse:1:31 \
    -A "Sat,Sun,Hday" \
    /usr/local/bin/month-end-close.sh

31 March 2026 is a Tuesday. Choose a start date that is itself a working day: avoidance is applied to calculated times, never to the date you give with -T, so a first run set on a Saturday will happen on that Saturday.

Frequent Monitoring, Every 5 Minutes, Every Day

btr -r Minutes:5 \
    -A "-" \
    -S \
    /usr/local/bin/monitor-system.sh

-A "-" clears the shipped weekend defaults, which a monitoring job does not want, and -S stops a restart from firing a burst of catch-up runs.

Quarterly Report, Every 3 Months on the 1st

btr -T "2026/4/1,09:00" \
    -r Monthsb:3:1 \
    -A "Sat,Sun,Hday" \
    -h "Quarterly report" \
    /usr/local/bin/quarterly-report.sh

Best Practices

Check what you actually submitted:

Run btjlist -H -F "%N %H %T %r %a %w" immediately after submitting. Three of the options in this article are accepted with a wrong argument rather than refused, and the listing is the only thing that will tell you.

Set an explicit time on every repeating job:

Without -T the first run time is whenever the job happened to be submitted, and the whole schedule is built from it.

State the avoid days even when they are the defaults:

The defaults come from the help file rather than from the command, so a job that relies on them is relying on something the command line does not show. Writing -A "Sat,Sun" or -A "-" makes the intention explicit.

Choose the missed-slot action deliberately:

The default, -H, catches up run for run. For a five-minute monitoring job that is a burst of runs after every restart; use -S. For a nightly job that must happen even if it happens late, the default is right.

Use -J on repeating jobs that matter:

A repeating job stops repeating after a failure either way, but with -J its time stops advancing, which is something a listing or a monitoring script can see.

Plan for the daylight-saving changes:

Anything that repeats in days or longer moves by an hour at each transition until btdst is run. Schedule the two btdst runs as jobs rather than relying on someone remembering.

Test Monthse before you rely on it:

The target day is interpreted against the month named in -T and stored as an offset from the end of the month. Submit it, then read %T and %r back and confirm the first date is the one you expect.

Working with Xi-Batch Variables
What a variable holds, the limits, and the real btvar and btvlist options for creating, reading and exporting one