yannstatic/static/2019/02/11/Systemd-timer-cron-anacron.html

2738 lines
228 KiB
HTML
Raw Permalink Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Utiliser les temporisateurs (Timers Oncalendar) Systemd pour remplacer Cron - YannStatic</title>
<meta name="description" content="Linux cron a fonctionné comme le planificateur de tâches basé sur le temps Unix pendant de nombreuses années mais les minuteries Systemd commencent à remplac...">
<link rel="canonical" href="https://static.rnmkcy.eu/2019/02/11/Systemd-timer-cron-anacron.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Utiliser les temporisateurs (Timers Oncalendar) Systemd pour remplacer Cron</h1></header></div><meta itemprop="headline" content="Utiliser les temporisateurs (Timers Oncalendar) Systemd pour remplacer Cron"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=outils">outils</a>
2024-10-31 20:18:37 +01:00
</li><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=timer">timer</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">11&nbsp;févr.&nbsp;2019</span>
<span title="Modification" style="color:#00FF7F">30&nbsp;mars&nbsp;&nbsp;2023</span></li></ul></div><meta itemprop="datePublished" content="2023-03-30T00:00:00+02:00">
<meta itemprop="keywords" content="outils,timer"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><p><em>Linux cron a fonctionné comme le planificateur de tâches basé sur le temps Unix pendant de nombreuses années mais les minuteries Systemd commencent à remplacer cron</em></p>
<ul>
<li><a href="#cron">Cron</a>
<ul>
<li><a href="#exécuter-cron">Exécuter cron</a></li>
</ul>
</li>
<li><a href="#systemd-timer">Systemd Timer</a>
<ul>
<li><a href="#création-service-et-timer">Création service et timer</a></li>
<li><a href="#spécifications-des-événements-du-calendrier">Spécifications des événements du calendrier</a></li>
<li><a href="#tester-les-spécifications-du-calendrier">Tester les spécifications du calendrier</a>
<ul>
<li><a href="#syntaxe-date-oncalendar">Syntaxe date “OnCalendar”</a></li>
<li><a href="#explication-du-format-des-temporisateurs-systemd-oncalendar-cron">Explication du format des temporisateurs systemd onCalendar (cron)</a></li>
</ul>
</li>
<li><a href="#activation-et-démarrage-du-timer">Activation et démarrage du timer</a></li>
<li><a href="#gestion-et-suivi-dun-timer">Gestion et suivi dun timer</a></li>
<li><a href="#archlinux-nettoyage-du-cache-systemd-timer">Archlinux Nettoyage du cache (systemd-timer)</a></li>
</ul>
</li>
<li><a href="#tutoriel-systemd-timer-avec-un-exemple-denvoi-automatique-de-mails">Tutoriel Systemd Timer avec un exemple denvoi automatique de-mails</a>
<ul>
<li><a href="#unité-de-service-service-unit">Unité de service (Service Unit)</a></li>
<li><a href="#unité-de-temporisarion-timer-unit">Unité de temporisarion (Timer Unit)</a></li>
<li><a href="#installer-et-cible-install-and-target">Installer et cible ([Install] and target)</a></li>
<li><a href="#commandes-relatives-à-la-minuterie">Commandes relatives à la minuterie</a></li>
<li><a href="#commandes-relatives-au-journal">Commandes relatives au journal</a></li>
</ul>
</li>
<li><a href="#liens">Liens</a></li>
</ul>
<h2 id="cron">Cron</h2>
<h3 id="exécuter-cron">Exécuter cron</h3>
<p><em>un jour spécifique dans le mois (e.g. deuxième lundi)</em></p>
<p>Comment exécuter un cron sur un jour spécifique de la semaine une fois par mois ?<br />
Ceci pourrait sembler simple au premier abord, puisque cette ligne pourrait semblait faire laffaire :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Run on every second Tuesday of the month
15 3 8-14 * 2 /usr/bin/bash /opt/myscriptfortuesday.sh
</code></pre></div></div>
<p>Mais ceci ne marcherait pas car le 2 pour vérifier que nous sommes bien un mardi vient comme une condition OR, et donc la commande pourrait sexécuter du jour 8 au jour 14 et tous les mardis du mois.</p>
<p>Pour contourner cela, vous pouvez utiliser cette commande :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Run on every second Tuesday of the month
15 3 8-14 * * test $(date +\%u) -eq 2 &amp;&amp; /usr/bin/bash /opt/myscriptfortuesday.sh
</code></pre></div></div>
<p>Voici lexplication de cette ligne de cron :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>15 = 15th minute
3 = 3am
8-14 = between day 8 and day 14 (second week)
* = every month
* = every day of the week
test $(date +\%u) -eq 2 &amp;&amp; /usr/bin/bash /opt/myscriptfortuesday.sh = the command to execute with a check on the date
</code></pre></div></div>
<p>En effectuant cette vérification, nous vérifions alors dabord que nous sommes bien un mardi avant dexécuter la commande. Noubliez pas dajouter un antislash avant le caractère % pour léchapper.</p>
<h2 id="systemd-timer">Systemd Timer</h2>
<p>Le fonctionnement de <strong>systemd</strong> impose davoir deux fichiers :<br />
<strong>service</strong>, qui contient la définition du programme<br />
<strong>timer</strong>, qui dit “quand” le lancer.</p>
<blockquote>
<p>Ils doivent porter le même nom et se situer dans <strong>/etc/systemd/system/</strong>.</p>
</blockquote>
<h3 id="création-service-et-timer">Création service et timer</h3>
<p>Si vous gérez déjà vos services via systemd, vous avez déjà utilisé des “unit” systemd de type “service”.<br />
Ces “unit” permettent de définir un process et son mode déxécution.<br />
Pour implémenter un “timer” sous systemd, il va nous falloir un fichier “service”.</p>
<p>Pour notre tâche à planifier, nous allons avoir au final 3 fichiers :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Le script à exécuter
Le fichier “service” qui va dire quel script exécuter
Le fichier “timer” qui va indiquer quand il doit être exécuté.
</code></pre></div></div>
<blockquote>
<p>A noter que par convention, les fichiers service et timer doivent avoir le même nom</p>
</blockquote>
<p>Nous devons exécuter ,une fois par jour , un script de sauvegarde <strong>/home/yannick/scripts/savarch</strong> sur un ordinateur qui nest pas sous tension 24/24h.</p>
<p>Pour le fichier service <strong>/etc/systemd/system/savarch.service</strong>, une base simple</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Sauvegarde jour
[Service]
Type=simple
ExecStart=/bin/bash /home/yannick/scripts/savarch
StandardError=journal
Restart=on-abort
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<p>Je fournis une description à mon service, indique que cest un process de type simple, le chemin vers mon script et je rajoute que le flux derreur est envoyé dans le journal.Il ne faut pas de section [Install] car le script va être piloté par le fichier timer.<br />
La ligne <code class="language-plaintext highlighter-rouge">Type=oneshot</code> est importante, cest elle qui dit à systemd de ne pas relancer le service en boucle.</p>
<p>Le fichier “timer” <strong>/etc/systemd/system/savarch.timer</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Sauvegarde jour
[Timer]
# lisez le man systemd.timer(5) pour tout ce qui est disponible
OnCalendar=daily
# Autoriser la persistence entre les reboot
Persistent=true
Unit=savarch.service
[Install]
WantedBy=timers.target
</code></pre></div></div>
<ul>
<li><strong>OnCalendar</strong> permet dindiquer loccurrence et la fréquence dexécution du script. Il y a les abréviations classiques (“minutely”, “hourly”, “daily”, “monthly”, “weekly”, “yearly”, “quarterly”, “semiannually”, etc) mais vous pouvez avoir des choses plus complexes comme “Mon,Tue <em>-</em>-01..04 12:00:00” - voir <strong>systemd.time</strong></li>
<li><strong>Persistent</strong> va forcer lexécution du script si la dernière exécution a été manquée suite à un reboot de serveur ou autre événement.</li>
<li><strong>Install</strong> va créer la dépendance pour que votre “timer” soit bien exécuté et pris en compte par systemd.</li>
</ul>
<h3 id="spécifications-des-événements-du-calendrier">Spécifications des événements du calendrier</h3>
<p>Les spécifications des événements du calendrier sont un élément clé du déclenchement des minuteries aux moments répétitifs souhaités. Commencez par examiner certaines spécifications utilisées avec le paramètre <strong>OnCalendar</strong>.</p>
<p><em>systemd et ses minuteries utilisent un style différent pour les spécifications dheure et de date que le format utilisé dans crontab. Il est plus flexible que crontab et permet des dates et des heures floues à la manière de la commande at. Il devrait également être suffisamment familier pour être facile à comprendre.</em></p>
<p>Le format de base des minuteries systemd utilisant <code class="language-plaintext highlighter-rouge">OnCalendar=</code> est <code class="language-plaintext highlighter-rouge">DOW YYYY-MM-DD HH:MM:SS</code><br />
DOW (jour de la semaine) est facultatif, et les autres champs peuvent utiliser un astérisque (*) pour correspondre à toute valeur pour cette position. Toutes les formes dheure du calendrier sont converties en une forme normalisée. Si lheure nest pas spécifiée, elle est supposée être 00:00:00. Si la date nest pas spécifiée mais que lheure lest, la prochaine correspondance peut être aujourdhui ou demain, selon lheure actuelle. Des noms ou des nombres peuvent être utilisés pour le mois et le jour de la semaine. Des listes séparées par des virgules peuvent être spécifiées pour chaque unité. Des plages dunités peuvent être spécifiées avec .. entre les valeurs de début et de fin.</p>
<p>Il existe quelques options intéressantes pour spécifier les dates. Le tilde (~) peut être utilisé pour indiquer le dernier jour du mois ou un certain nombre de jours avant le dernier jour du mois. Le “/” peut être utilisé pour spécifier un jour de la semaine comme modificateur.</p>
<p>Voici quelques exemples de spécifications temporelles typiques utilisées dans les instructions <strong>OnCalendar</strong>.</p>
<table>
<thead>
<tr>
<th>Evénement</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">DOW YYYY-MM-DD HH:MM:SS </code></td>
<td> </td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">*-*-* 00:15:30 </code></td>
<td>Chaque jour de chaque mois de chaque année à 15 minutes et 30 secondes après minuit.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Weekly </code></td>
<td>hebdomadaire, chaque lundi à 00:00:00</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon *-*-* 00:00:00 </code></td>
<td>Identique à hebdomadaire (weekly)</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon </code></td>
<td>Identique à la semaine (weekly)</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Wed 2020-*-* </code></td>
<td>Tous les mercredis de 2020 à 00:00:00</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon..Fri 2021-*-* </code></td>
<td>Tous les jours de la semaine en 2021 à 00:00:00 (hors samedi et dimanche)</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">2022-6,7,8-1,15 01:15:00 </code></td>
<td>Les 1er et 15 juin, juillet et août de lannée 2022 à 01:15:00</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon *-05~03 </code></td>
<td>La prochaine occurrence dun lundi en mai de nimporte quelle année qui est aussi le 3ème jour à partir de la fin du mois.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon..Fri *-08~04 </code></td>
<td>Le 4ème jour précédant la fin du mois daoût pour toutes les années où il tombe également un jour de semaine.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">*-05~03/2 </code></td>
<td>Le 3ème jour à partir de la fin du mois de mai, puis à nouveau deux jours plus tard. Se répète chaque année. Notez que cette expression utilise le tilde (<code class="language-plaintext highlighter-rouge">~</code>).</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">*-05-03/2 </code></td>
<td>Le troisième jour du mois de mai et ensuite tous les deux jours pour le reste du mois de mai. Se répète chaque année. Notez que cette expression utilise le tiret (<code class="language-plaintext highlighter-rouge">-</code>).</td>
</tr>
</tbody>
</table>
<h3 id="tester-les-spécifications-du-calendrier">Tester les spécifications du calendrier</h3>
<p><strong>systemd-analyze calendar</strong><br />
systemd fournit un excellent outil pour valider et examiner les spécifications des événements temporels dun calendrier dans une minuterie.<br />
Loutil <code class="language-plaintext highlighter-rouge">systemd-analyze calendar</code> analyse une spécification dévénement temporel de calendrier et fournit la forme normalisée ainsi que dautres informations intéressantes telles que la date et lheure du prochain “elapse”, cest-à-dire la correspondance, et le temps approximatif avant que lheure de déclenchement ne soit atteinte.</p>
<p>Tout dabord, regardez une date dans le futur sans heure (notez que les heures pour Next elapse et UTC seront différentes en fonction de votre fuseau horaire local) :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze calendar 2030-06-17
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Original form: 2030-06-17
Normalized form: 2030-06-17 00:00:00
Next elapse: Mon 2030-06-17 00:00:00 CEST
(in UTC): Sun 2030-06-16 22:00:00 UTC
From now: 7 years 11 months left
</code></pre></div></div>
<p>Ajoutez maintenant une heure. Dans cet exemple, la date et lheure sont analysées séparément comme des entités non liées :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze calendar 2030-06-17 15:21:16
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Original form: 2030-06-17
Normalized form: 2030-06-17 00:00:00
Next elapse: Mon 2030-06-17 00:00:00 CEST
(in UTC): Sun 2030-06-16 22:00:00 UTC
From now: 7 years 11 months left
Original form: 15:21:16
Normalized form: *-*-* 15:21:16
Next elapse: Thu 2022-06-23 15:21:16 CEST
(in UTC): Thu 2022-06-23 13:21:16 UTC
From now: 6h left
</code></pre></div></div>
<p>Pour analyser la date et lheure comme une seule unité, mettez-les entre guillemets. Veillez à supprimer les guillemets lorsque vous les utilisez dans la spécification de lévénement OnCalendar= dans une unité de temps, sinon vous obtiendrez des erreurs :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze calendar "2030-06-17 15:21:16"
</code></pre></div></div>
<p>Testez maintenant les entrées du tableau 2. Jaime particulièrement la dernière :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze calendar "2022-6,7,8-1,15 01:15:00"
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Normalized form: 2030-06-17 15:21:16
Next elapse: Mon 2030-06-17 15:21:16 CEST
(in UTC): Mon 2030-06-17 13:21:16 UTC
From now: 7 years 11 months left
</code></pre></div></div>
<p>Examinons un exemple dans lequel nous listons les cinq prochains écoulements pour lexpression timestamp.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze calendar --iterations=5 "Mon *-05~3"
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Original form: Mon *-05~3
Normalized form: Mon *-05~03 00:00:00
Next elapse: Mon 2023-05-29 00:00:00 CEST
(in UTC): Sun 2023-05-28 22:00:00 UTC
From now: 11 months 4 days left
Iter. #2: Mon 2028-05-29 00:00:00 CEST
(in UTC): Sun 2028-05-28 22:00:00 UTC
From now: 5 years 11 months left
Iter. #3: Mon 2034-05-29 00:00:00 CEST
(in UTC): Sun 2034-05-28 22:00:00 UTC
From now: 11 years 11 months left
Iter. #4: Mon 2045-05-29 00:00:00 CEST
(in UTC): Sun 2045-05-28 22:00:00 UTC
From now: 22 years 11 months left
Iter. #5: Mon 2051-05-29 00:00:00 CEST
(in UTC): Sun 2051-05-28 22:00:00 UTC
From now: 28 years 11 months left
</code></pre></div></div>
<p>Cela devrait vous donner suffisamment dinformations pour commencer à tester vos spécifications temporelles <strong>OnCalendar</strong>.</p>
<h4 id="syntaxe-date-oncalendar">Syntaxe date “OnCalendar”</h4>
<table>
<thead>
<tr>
<th>Forme minimale</th>
<th></th>
<th>Forme normalisée</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">Sat,Thu,Mon-Wed,Sat-Sun </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon-Thu,Sat,Sun *-*-* 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon,Sun 12-*-* 2,1:23 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon,Sun 2012-*-* 01,02:23:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Wed *-1 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Wed *-*-01 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Wed-Wed,Wed *-1 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Wed *-*-01 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Wed, 17:48 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Wed *-*-* 17:48:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Wed-Sat,Tue 12-10-15 1:2:3</code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Tue-Sat 2012-10-15 01:02:03</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">*-*-7 0:0:0 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-07 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">10-15 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-10-15 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">monday *-12-* 17:00 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon *-12-* 17:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Mon,Fri *-*-3,1,2 *:30:45 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon,Fri *-*-01,02,03 *:30:45</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">12,14,13,12:20,10,30 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* 12,13,14:10,20,30:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">mon,fri *-1/2-1,3 *:30:45 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon,Fri *-01/2-01,03 *:30:45</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">03-05 08:05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-03-05 08:05:40</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">08:05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* 08:05:40</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* 05:40:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Sat,Sun 12-05 08:05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Sat,Sun *-12-05 08:05:40</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">Sat,Sun 08:05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Sat,Sun *-*-* 08:05:40</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">2003-03-05 05:40 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">2003-03-05 05:40:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">2003-03-05 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">2003-03-05 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">03-05 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-03-05 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">hourly </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* *:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">daily </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">monthly </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-01 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">weekly </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">Mon *-*-* 00:00:00</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">*:20/15 </code></td>
<td></td>
<td><code class="language-plaintext highlighter-rouge">*-*-* *:20/15:00</code></td>
</tr>
</tbody>
</table>
<p><code class="language-plaintext highlighter-rouge">Note : *:20/15 signifie *:20:00, *:35:00, *:50:00, en recommençant l'heure suivante à *:20:00</code></p>
<h4 id="explication-du-format-des-temporisateurs-systemd-oncalendar-cron">Explication du format des temporisateurs systemd onCalendar (cron)</h4>
<p><a href="https://silentlad.com/systemd-timers-oncalendar-(cron)-format-explained">Systemd timers onCalendar (cron) format explained</a></p>
<p><u>Format de tâche cron</u></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* * * * *
minute heures jour du mois mois jour de la semaine
</code></pre></div></div>
<p>Ainsi, par exemple, si nous voulons quun travail sexécute chaque fois quil est minuit. Nous mettrons minuteà 00 et hoursà 00 et tout le reste tel quel, cela nous donnera -</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00 00 * * *
# tous les jours minuit
</code></pre></div></div>
<p>De même, si nous voulons que ce soit tous les jours de la semaine (plage) minuit, ce sera</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00 00 * * 1-5
# tous les jours minuit
</code></pre></div></div>
<p>Et le dernier concept est de répéter ce qui se passe si nous voulons quun travail sexécute toutes nles minutes. Ensuite, nous utilisons ce format.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*/n * * * *
# toutes les n minutes
</code></pre></div></div>
<p>De même, ces répétitions peuvent être utilisées pour lun des 5 champs avec le temps absolu.</p>
<p><u>Format Systemd Timer OnCalendar</u></p>
<p>Maintenant que vous connaissez les bases du format de cron, il vous sera plus facile de comprendre le format OnCalendar qui vous donne plus de granularité et de contrôle.<br />
Donc, le format de base de lévénement Oncalnedar est le suivant <br />
<code class="language-plaintext highlighter-rouge">* *-*-* *:*:*</code><br />
Il est divisé en 3 parties</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">*-</code> Pour signifier le jour de la semaine, par exemple : - Sat, Thu, Mon</li>
<li><code class="language-plaintext highlighter-rouge">*-*-*-</code> Pour signifier la date du calendrier. Ce qui signifie quil se décompose en - <code class="language-plaintext highlighter-rouge">year-month-date</code>.
<ul>
<li><code class="language-plaintext highlighter-rouge">2021-10-15</code> est le 15 octobre</li>
<li><code class="language-plaintext highlighter-rouge">*-10-15</code> signifie chaque année au 15 octobre</li>
<li><code class="language-plaintext highlighter-rouge">*-01-01</code> signifie chaque nouvelle année.</li>
<li><code class="language-plaintext highlighter-rouge">*:*:*</code> est de signifier la composante temporelle de lévénement calendaire. Donc cest -<code class="language-plaintext highlighter-rouge">hour:minute:second</code></li>
</ul>
</li>
</ul>
<blockquote>
<p>Remarque- Contrairement à cronjob, nous pouvons ignorer un composant de oncalendar event si nous navons aucune modification à lui apporter.</p>
</blockquote>
<p>Ce qui signifie</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Mer *-*-* 17:48:00
#Peut aussi s'écrire -&gt;
mer, 17:48
</code></pre></div></div>
<p>Désormais, onCalendar respecte également toutes les règles du format cron. Nous allons donc aborder chaque concept un par un.</p>
<p><u>Minuteries Systemd qui s'exécutent à un moment précis</u></p>
<p>Cest assez simple, il suffit de remplir les champs ci-dessous avec le moment souhaité.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* *-*-* *:*:*
#Jour de la semaine Année-Mois-Date Heure :Minute :Seconde
</code></pre></div></div>
<p><u>Systemd Timers Oncalendar Exemples qui s'exécutent à une fréquence donnée</u></p>
<p>Donc, comme nous avons utilisé /la fréquence dans le travail cron, nous faisons la même chose ici.
Ainsi, par exemple, pour tous les 2 jours, nous disons</p>
<p><code class="language-plaintext highlighter-rouge">*/1 *-*-* *:*:*</code></p>
<p>De même, nous pouvons également définir la période initiale. Donc, tous les jours à partir de lundi</p>
<p><code class="language-plaintext highlighter-rouge">Lun/1 *-*-* *:*:*</code></p>
<p>Et nous pouvons définir une plage pour la fréquence à exécuter, donc tous les jours mais seulement les jours de la semaine</p>
<p><code class="language-plaintext highlighter-rouge">Lun,Mar,Mer,Jeu,Ven *-*-* *:*:*</code></p>
<p>Ainsi, des exemples de gamme Systemd Timers Oncalendar et des exemples normaux sont donnés dans le tableau ci-dessous.<br />
Utilisez la commande calendar de systemd-analyze (<a href="https://opensource.com/article/20/7/systemd-calendar-timespans">Analyzing systemd calendar and timespans</a>) pour valider et normaliser les spécifications dheure calendaire à des fins de test. Loutil calcule également quand un événement de calendrier spécifié se produirait ensuite.</p>
<p><u>Exemples OnCalendar, exemples Systemd Timer</u></p>
<table>
<thead>
<tr>
<th style="text-align: left">Explication</th>
<th style="text-align: left">Systemd Timer</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">Toutes les minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 2 minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/2:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 5 minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/5:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 15 minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/15:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les quarts dheure</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/15:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 30 minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/30:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les demi-heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:*/30:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 60 minutes</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */1:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 1 heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* *:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 2 heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */2:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 3 heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */3:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les autres heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */2:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 6 heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */6:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Toutes les 12 heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* */12:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Plage horaire</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 9-17:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Entre certaines heures</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 9-17:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Une fois par jour</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Chaque nuit</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 01:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours à 1 heure du matin</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 01:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours à 2 heures du matin</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 02:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les matins</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 07:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours à minuit</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours à minuit</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Chaque nuit à minuit</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les dimanches dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les vendredis Ven</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 01:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les vendredis à minuit Ven</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les samedis sam</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les jours de la semaine Lun…Ven</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">en semaine uniquement Lun…Ven</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">du lundi au vendredi Lun…Ven</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les week-ends sam, dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">week-end uniquement sam,dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les 7 jours</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Chaque semaine Dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">chaque semaine dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">une fois par semaine Dim</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">*-*-* 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Chaque mois</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-*-01 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">mensuel</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-*-01 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">une fois par mois</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-*-01 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les trimestres</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-01,04,07,10-01 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Tous les 6 mois</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-01,07-01 00:00:00</code></td>
</tr>
<tr>
<td style="text-align: left">Chaque année</td>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">* *-01-01 00:00:00</code></td>
</tr>
</tbody>
</table>
<h3 id="activation-et-démarrage-du-timer">Activation et démarrage du timer</h3>
<p>Il est possible de tester le service avec un simple <code class="language-plaintext highlighter-rouge">systemctl start savarch.service</code>, de regarder les logs avec <code class="language-plaintext highlighter-rouge">journalctl -u savarch.service</code>.</p>
<p>Ensuite, pour quil soit actif, il faut prévenir systemd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable savarch.timer
sudo systemctl start savarch.timer
</code></pre></div></div>
<h3 id="gestion-et-suivi-dun-timer">Gestion et suivi dun timer</h3>
<p>Pour voir la liste des “timers” actifs et la date de leur dernière et prochaine exécution</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl list-timers
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2018-03-02 00:00:00 CET 15h left Thu 2018-03-01 07:49:43 CET 56min ago logrotate.timer logrotate.service
Fri 2018-03-02 00:00:00 CET 15h left Thu 2018-03-01 07:49:43 CET 56min ago man-db.timer man-db.service
Fri 2018-03-02 00:00:00 CET 15h left Thu 2018-03-01 07:49:43 CET 56min ago savarch.timer savarch.service
Fri 2018-03-02 00:00:00 CET 15h left Thu 2018-03-01 07:49:43 CET 56min ago shadow.timer shadow.service
Fri 2018-03-02 00:00:00 CET 15h left Thu 2018-03-01 07:49:43 CET 56min ago updatedb.timer updatedb.service
Fri 2018-03-02 08:04:45 CET 23h left Thu 2018-03-01 08:04:45 CET 41min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
</code></pre></div></div>
<p>et accéder aux logs de vos “timers” :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>journalctl -u savarch.service
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:15 Départ sauvegarde
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:15 synchronisation source partagée /home/yannick/media/Musique cible locale /home/yannick/media/savlocal/Musique
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:21 synchronisation partagée /home/yannick/media/devel cible locale /home/yannick/media/savlocal/devel
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:33 synchronisation partagée /home/yannick/media/BiblioCalibre cible locale /home/yannick/media/savlocal/BiblioCalibre
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:41 SAUVEGARDE /home/yannick -&gt; /home/yannick/media/savlocal/yannick-pc/yannick
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:42 SAUVEGARDE /home/yannick/media/dplus -&gt; /home/yannick/media/savlocal/dplus
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:47 SAUVEGARDE /home/yannick/media/yanspm-md -&gt; /home/yannick/media/savlocal/yanspm-md
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:47 FIN sauvegarde
mars 01 08:43:47 yannick-pc systemd[1]: Started Sauvegarde jour.
</code></pre></div></div>
<p>En cas de modification du <strong>.timer</strong> ou du <strong>.service</strong>, ne pas oublier de faire un <strong>systemctl daemon-reload</strong> pour que la version actualisée de vos fichiers soit prise en compte par systemd.</p>
<h3 id="archlinux-nettoyage-du-cache-systemd-timer">Archlinux Nettoyage du cache (systemd-timer)</h3>
<p><strong>pkgcacheclean</strong> est un petit programme qui nettoie le cache pacman mais conserve n versions du paquet dans le cache.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yaourt -S pkgcacheclean
</code></pre></div></div>
<p><strong>Manuellement</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkgcacheclean -nv 3
</code></pre></div></div>
<p>Cette commande va afficher (et seulement afficher, aucun fichier nest modifié) ce quil ferait si vous vouliez conserver 3 versions pour chaque paquet installé.<br />
Pour effectuer les changements sur le disque :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkgcacheclean -v 3
</code></pre></div></div>
<p><strong>Automatiquement</strong><br />
Nettoyage du cache pacman une fois par mois avec conservation de 3 versions (2 précédentes + 1 en cours)</p>
<p>Fichier <strong>/etc/systemd/system/pkgcacheclean.timer</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Minuterie mensuelle pour pkgcacheclean (nettoyage cache pacman)
[Timer]
OnCalendar=monthly
AccuracySec=5d
Persistent=true
Unit=pkgcacheclean.service
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<p>Fichier <strong>/etc/systemd/system/pkgcacheclean.service</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Nettoyage cache pacman avec conservation de 3 versions
[Service]
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
Type=oneshot
ExecStart=/usr/bin/pkgcacheclean -v 3
</code></pre></div></div>
<p>Ensuite, pour quil soit actif, il faut prévenir systemd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable pkgcacheclean.timer
sudo systemctl start pkgcacheclean.timer
</code></pre></div></div>
<blockquote>
<p>Ne pas oublier un <code class="language-plaintext highlighter-rouge">sudo systemctl daemon-reload</code> après avoir modifier un fichier, sinon ce nest pas pris en compte.</p>
</blockquote>
<h2 id="tutoriel-systemd-timer-avec-un-exemple-denvoi-automatique-de-mails">Tutoriel Systemd Timer avec un exemple denvoi automatique de-mails</h2>
<h3 id="unité-de-service-service-unit">Unité de service (Service Unit)</h3>
<p>Comme mentionné précédemment, lunité de service est la tâche à effectuer, comme lenvoi dun e-mail.<br />
La création dun nouveau Service est très simple, qui consiste à créer un nouveau fichier dans le répertoire /usr/lib/systemd/system, tel que le fichier mytimer.service. Par exemple, vous pouvez taper le code suivant.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=MyTimer
[Service]
ExecStart=/bin/bash /path/to/mail.sh
</code></pre></div></div>
<p>Comme vous pouvez le voir, le fichier de lunité de service est divisé en deux parties.</p>
<p>La partie[Unit] décrit les informations de base de lunité (cest-à-dire les métadonnées) et le champ Description donne une brève introduction de lunité (cest-à-dire MyTimer).</p>
<p>La partie[Service] sert à personnaliser les actions et Systemd fournit de nombreux champs.</p>
<ul>
<li>ExecStart : commandes à exécuter par systemctl start</li>
<li>ExecStop : commandes à exécuter par systemctl stop</li>
<li>ExecReload : commandes à exécuter par rechargement systemctl</li>
<li>ExecStartPre : commandes à exécuter automatiquement avant ExecStartPre</li>
<li>ExecStartPost : commandes à exécuter automatiquement après ExecStartPost</li>
<li>ExecStopPost : commandes à exécuter après ExecStop automatiquement</li>
</ul>
<blockquote>
<p>Notez que lors de la définition, tous les chemins doivent être écrits en tant que chemins absolus. Par exemple, bash doit être écrit comme /bin/bash, sinon Systemd ne le trouvera pas.</p>
</blockquote>
<p>Démarrez maintenant le Service.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl démarrer mytimer.service
</code></pre></div></div>
<p>Si tout va bien, vous recevrez un email.</p>
<h3 id="unité-de-temporisarion-timer-unit">Unité de temporisarion (Timer Unit)</h3>
<p>Lunité de service (Service Unit) définit simplement comment exécuter la tâche.<br />
Vous devez définir lunité Minuterie (Timer Unit) afin dexécuter le Service périodiquement.<br />
Créez un nouveau fichier mytimer.timer dans le répertoire /usr/lib/systemd/system, et tapez le code suivant.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Runs mytimer every hour
[Timer]
OnUnitActiveSec=1h
Unit=mytimer.service
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<p>Le fichier de lunité de temporisation (Timer) est divisé en plusieurs parties.</p>
<p>La partie[Unit] définit les métadonnées.</p>
<p>La partie[Timer] personnalise la minuterie. <br />
Et Systemd fournit les champs suivants.</p>
<ul>
<li>OnActiveSec : Combien de temps faut-il pour démarrer la tâche après que la minuterie ait pris effet ?</li>
<li>OnBootSec : Combien de temps faut-il pour lancer la tâche après le démarrage du système ?</li>
<li>OnStartupSec : Combien de temps faut-il pour démarrer la tâche après le démarrage du processus Systemd ?</li>
<li>OnUnitActiveSec : Combien de temps faut-il pour que lunité exécute à nouveau après la dernière exécution de lunité ?</li>
<li>OnUnitInactiveSec : Combien de temps faut-il pour exécuter une nouvelle fois depuis que la minuterie a été désactivée la dernière fois ?</li>
<li>OnCalendar : Exécuter en fonction du temps absolu et non du temps relatif.</li>
<li>AccuracySec : Si la tâche doit être reportée pour une raison quelconque, le nombre maximum de secondes à reporter est de 60 secondes par défaut.</li>
<li>Unit : Lintervention à exécuter. Lunité par défaut est lunité avec le suffixe .service du même nom.</li>
<li>Persistent : Si le champ est défini, lunité correspondante sera automatiquement exécutée même si la minuterie ne démarre pas.</li>
<li>WakeSystem : Sil faut réveiller le système automatiquement si le système se met en veille.</li>
</ul>
<p>Dans le script ci-dessus, OnUnitActiveSec=1h indique que la tâche sera effectuée par heure.<br />
Dautres façons décrire sont : OnUnitActiveSec=<em>-</em>-<em>-</em> 02:00:00:00 signifie être exécuté à deux heures du matin, et OnUnitActiveSec=Mon <em>-</em>-* 02:00:00:00 signifie être exécuté à deux heures du matin chaque lundi. Pour plus de détails, veuillez vous référer à la documentation officielle.</p>
<h3 id="installer--et-cible-install-and-target">Installer et cible ([Install] and target)</h3>
<p>Dans le fichier mytimer.timer, il y a une partie [Install] qui définit les commandes à exécuter lors de lactivation ou de la désactivation de systemctl de lappareil.<br />
Dans le script ci-dessus, la partie [Install] ne définit quun seul champ, qui est WantedBy=multi-user.target.<br />
Cela signifie que si systemctl enable mytimer.timer est exécuté (tant que Systemd est démarré, le timer prend automatiquement effet), alors le timer appartient à multi-user.target.</p>
<p>La cible se réfère à un groupe de processus apparentés, un peu comme le niveau de démarrage sous le mode de processus init. Lorsquune Cible est démarrée, tous les processus appartenant à cette Cible seront également démarrés.<br />
multi-user.target est la cible la plus couramment utilisée. Lorsquun système est démarré en mode multi-utilisateur, mytimer.timer est démarré avec. La façon de travailler sous le capot est assez simple. Lors de lexécution de la commande systemctl enable mytimer.timer, un lien symbolique sera créé dans le répertoire multi-user.target.wants, pointant vers mytimer.timer.</p>
<h3 id="commandes-relatives-à-la-minuterie">Commandes relatives à la minuterie</h3>
<p>démarrez la minuterie nouvellement créée.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl start mytimer.timer
</code></pre></div></div>
<p>Vous recevrez le courriel immédiatement et recevrez le même courriel toutes les heures.</p>
<p>Vérifiez létat de la minuterie.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl status mytimer.timer
</code></pre></div></div>
<p>Voir tous les chronomètres (rimers) en cours dexécution.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl list-timers
</code></pre></div></div>
<p>Éteignez la minuterie.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl stop myscript.timer
</code></pre></div></div>
<p>Systemctl active automatiquement la minuterie.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable myscript.timer
</code></pre></div></div>
<p>Systemctl désactive la minuterie.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl disable myscript.timer
</code></pre></div></div>
<h3 id="commandes-relatives-au-journal">Commandes relatives au journal</h3>
<p>Si une exception survient, vous devez vérifier le protocole. Voici les commandes que Systemd fournit.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># view the entire log
$ sudo journalctl
# view the log for mytimer.timer
$ sudo journalctl -u mytimer.timer
# view the logs for mytimer.timer and mytimer.service
$ sudo journalctl -u mytimer
# view the latest log from the end
$ sudo journalctl -f
# view the log for mytimer.timer from the end
$ journalctl -f -u timer.timer
</code></pre></div></div>
<h2 id="liens">Liens</h2>
<ul>
<li><a href="https://jason.the-graham.com/2013/03/06/how-to-use-systemd-timers/">How to Use Systemd Timers</a>, by Jason Graham</li>
<li><a href="https://medium.com/horrible-hacks/using-systemd-as-a-better-cron-a4023eea996d">Using systemd as a better cron</a>, by luqmaan</li>
<li><a href="https://coreos.com/os/docs/latest/getting-started-with-systemd.html">Getting started with systemd</a>, by CoreOS</li>
<li><a href="https://wiki.archlinux.org/index.php/Systemd/Timers">systemd/Timers</a>, by ArchWiki</li>
<li>
<p><a href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files">Understanding Systemd Units and Unit Files</a>, by Justin Ellingwood</p>
</li>
<li><a href="https://wiki.archlinux.fr/Systemd/cron">systemd/cron (archlinux fr)</a></li>
<li><a href="https://www.cerenit.fr/blog/systemd-timer-pour-les-crooners/">Systemd timer pour les Cro(o)ners</a></li>
<li><a href="https://ungeek.fr/systemd-timer/">À la découverte de systemd-timer</a></li>
<li><a href="https://fedoramagazine.org/systemd-timers-for-scheduling-tasks/">Systemd Timers for Scheduling Tasks</a></li>
<li><a href="https://www.lisenet.com/2014/create-a-systemd-service-to-send-automatic-emails-when-arch-linux-restarts/">Create a Systemd Service to Send Automatic Emails When Arch Linux Restarts</a></li>
<li><a href="https://www.maketecheasier.com/use-systemd-timers-as-cron-replacement/">How to Use Systemd Timers as a Cron Replacement</a></li>
</ul>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2019-02-11T00:00:00+01:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2019/02/11/Gestion-des-bases-Mysql-MariaDB-avec-PhpMyAdmin.html">Gestion des bases Mysql/MariaDB avec PhpMyAdmin</a></div><div class="next"><span>SUIVANT</span><a href="/2019/02/13/Couper-assembler-des-videos-avec-ffmpeg-et-mencoder.html">Couper et assembler des vidéos avec ffmpeg et mencoder</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>