Depuis que j’ai déplacé les bases de données SQL sur une autre partition, logrotate semble avoir quelques soucis avec l’archivage des logs MySQL/MariaDB.
Voici le message d’erreur complet reçu depuis cron.daily :
/etc/cron.daily/logrotate: logrotate_script: 3: [: /var/run/mysqld/mysqld.pid: unexpected operatorCode language: JavaScript (javascript)
Ce message est un peu cryptique, mais le problème est simple : dans le script exécuté par logrotate, le test shell [ -f ... ] reçoit une valeur inattendue, souvent parce qu’une commande retourne plusieurs lignes au lieu d’un seul chemin de fichier PID.
Résultat : le shell ne comprend plus l’expression et retourne unexpected operator.
Comprendre l’erreur “unexpected operator”
L’erreur vient généralement d’une ligne de ce type dans le fichier de rotation MySQL/MariaDB :
if [ -f `my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"` ]; thenCode language: CSS (css)
Le but de cette ligne est de récupérer le chemin du fichier PID de MySQL/MariaDB, puis de vérifier s’il existe.
Le problème apparaît si la commande retourne plusieurs chemins ou plusieurs lignes. Le shell se retrouve alors avec une expression de ce type :
if [ -f /var/run/mysqld/mysqld.pid /run/mysqld/mysqld.pid ]; thenCode language: JavaScript (javascript)
Or [ -f ... ] attend un seul chemin. Avec deux valeurs, il ne sait plus évaluer correctement le test. D’où l’erreur unexpected operator.
C’est souvent lié à une configuration MySQL/MariaDB qui contient plusieurs directives pid-file, parfois après migration, installation depuis un dépôt externe, ou déplacement de datadir/logs.
Vérifier ce que retourne my_print_defaults
Avant de modifier quoi que ce soit, vérifiez ce que retourne la commande utilisée par le script :
my_print_defaults --mysqld | grep 'pid-file'Code language: JavaScript (javascript)
Vous pouvez aussi tester la regex exacte :
my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"Code language: JavaScript (javascript)
Si la commande retourne plusieurs lignes, vous avez trouvé la cause.
Exemple problématique :
/var/run/mysqld/mysqld.pid
/run/mysqld/mysqld.pidCode language: JavaScript (javascript)
Le test shell reçoit deux chemins, et logrotate se prend les pieds dans le tapis. Pas violemment, mais assez pour vous envoyer un email à l’aube.
Solution rapide : limiter grep au premier résultat
La correction historique consiste à limiter grep à une seule correspondance avec l’option -m1.
Éditez le fichier de configuration logrotate concerné :
sudo nano /etc/logrotate.d/mysql-server
Selon votre distribution, le fichier peut aussi s’appeler :
Votre base de données ralentit tout ?
Tables wp_options surchargées, autoload incontrôlé, requêtes non indexées — une base WordPress mal entretenue finit toujours par plomber les temps de réponse. Je l'audite, je la nettoie, je l'optimise.
Diagnostiquons votre base de données →/etc/logrotate.d/mysql
/etc/logrotate.d/mariadb
/etc/logrotate.d/mysql-server
Recherchez la ligne suivante :
if [ -f `my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"` ]; thenCode language: CSS (css)
Et remplacez-la par :
if [ -f `my_print_defaults --mysqld | grep -oPm1 "pid-file=\K[^$]+"` ]; thenCode language: CSS (css)
Le changement est minuscule :
grep -oP
grep -oPm1
L’option -m1 demande à grep de s’arrêter après la première correspondance. Le test [ -f ... ] reçoit donc un seul chemin et ne déclenche plus l’erreur.
Correction plus lisible : stocker le PID file dans une variable
Si vous modifiez un script vous-même, une version plus lisible consiste à éviter les backticks et à stocker le résultat dans une variable.
pid_file="$(my_print_defaults --mysqld | grep -oPm1 'pid-file=\K[^$]+')"
if [ -n "$pid_file" ] && [ -f "$pid_file" ]; then
# actions post-rotation
fiCode language: PHP (php)
C’est plus robuste, plus lisible, et cela évite les surprises si le chemin contient des caractères inattendus.
Cela dit, si le fichier appartient à un paquet Debian/Ubuntu/MariaDB, je préfère faire une sauvegarde avant modification, car une mise à jour du paquet peut écraser ou remplacer le fichier.
sudo cp -a /etc/logrotate.d/mysql-server /etc/logrotate.d/mysql-server.bak.$(date +%Y%m%d-%H%M%S)Code language: JavaScript (javascript)
Tester logrotate sans attendre cron.daily
Inutile d’attendre le prochain passage de cron pour savoir si la correction fonctionne. Testez la configuration avec logrotate en mode debug.
sudo logrotate -d /etc/logrotate.conf
Le mode debug affiche ce que logrotate ferait, sans modifier les logs.
Pour tester uniquement un fichier :
sudo logrotate -d /etc/logrotate.d/mysql-server
Ou selon votre fichier :
sudo logrotate -d /etc/logrotate.d/mariadb
La page de manuel de logrotate documente l’option -d comme un mode debug qui n’applique aucune modification aux logs ni au fichier d’état.
Pour forcer une rotation réelle, uniquement si vous savez ce que vous faites :
sudo logrotate -f /etc/logrotate.d/mysql-server
ou :
sudo logrotate -f /etc/logrotate.d/mariadb
La même page de manuel documente -f comme l’option permettant de forcer une rotation, même si logrotate estime qu’elle n’est pas nécessaire.
Vérifier les directives pid-file en double
La correction grep -m1 règle le symptôme. Mais si plusieurs pid-file sont définis, cela vaut le coup de nettoyer la configuration MySQL/MariaDB.
Recherchez toutes les occurrences :
grep -R "^[[:space:]]*pid-file" /etc/mysql /etc/my.cnf 2>/dev/nullCode language: JavaScript (javascript)
Vous pouvez aussi inspecter toutes les options lues par MariaDB/MySQL :
my_print_defaults --mysqld
Si vous trouvez plusieurs valeurs contradictoires, gardez une seule directive cohérente, par exemple :
pid-file = /run/mysqld/mysqld.pidCode language: JavaScript (javascript)
Sur les systèmes modernes, /run est le chemin réel. /var/run est souvent un lien symbolique vers /run. Évitez d’avoir les deux si cela crée des doublons dans les scripts.
Attention aux fichiers de paquet
Le fichier /etc/logrotate.d/mysql-server ou /etc/logrotate.d/mariadb est souvent fourni par un paquet système.
Avant modification, identifiez le paquet propriétaire :
dpkg -S /etc/logrotate.d/mysql-server 2>/dev/null
dpkg -S /etc/logrotate.d/mariadb 2>/dev/null
dpkg -S /etc/logrotate.d/mysql 2>/dev/nullCode language: JavaScript (javascript)
Si une mise à jour remplace le fichier, votre modification peut disparaître ou générer un conflit de configuration.
Quand c’est possible, je préfère corriger la cause dans la configuration MySQL/MariaDB — par exemple les directives pid-file en double — plutôt que patcher durablement le script logrotate fourni par le paquet.
Cela dit, si le script du paquet est réellement fautif, la correction grep -oPm1 reste pragmatique.
Vérifier la configuration logrotate de MariaDB
Une configuration logrotate MariaDB contient souvent un bloc de ce type :
/var/log/mysql/*.log {
daily
rotate 7
missingok
create 640 mysql adm
compress
sharedscripts
postrotate
test -x /usr/bin/mysqladmin || exit 0
/usr/bin/mysqladmin flush-logs
endscript
}Code language: JavaScript (javascript)
Les directives peuvent varier selon les distributions et versions.
Quelques directives utiles :
daily: rotation quotidienne ;rotate 7: conservation de sept archives ;missingok: ne pas échouer si un fichier manque ;compress: compresser les anciens logs ;create: recréer le fichier avec les bons droits ;sharedscripts: exécuter le script postrotate une seule fois pour plusieurs logs ;postrotate: commandes exécutées après rotation.
Logrotate permet justement de définir des règles par fichier ou groupe de fichiers, puis d’exécuter des scripts avant ou après rotation.
S’assurer que MariaDB réouvre ses fichiers de logs
Après rotation, MariaDB doit réouvrir ses fichiers de logs. Sinon, il peut continuer à écrire dans l’ancien fichier renommé, ou ne plus écrire là où vous l’attendez.
La commande habituelle est :
mysqladmin flush-logs
ou avec MariaDB :
mariadb-admin flush-logs
Si votre script utilise mysqladmin, vérifiez que la commande existe :
command -v mysqladmin
command -v mariadb-admin
Et vérifiez que l’utilisateur utilisé par le script a les droits nécessaires pour exécuter flush-logs.
MariaDB moderne : mysqladmin ou mariadb-admin ?
Sur les installations MariaDB modernes, l’outil peut s’appeler mariadb-admin, même si mysqladmin reste parfois disponible comme alias ou compatibilité.
Vérifiez :
ls -l /usr/bin/mysqladmin /usr/bin/mariadb-admin 2>/dev/nullCode language: JavaScript (javascript)
Si le script logrotate fourni par le paquet appelle un binaire qui n’existe plus, vous pouvez obtenir d’autres erreurs. Dans ce cas, il faut adapter le script ou installer le paquet client adéquat.
On a déjà vu des cas où la transition MySQL/MariaDB changeait les noms ou options utilisés par les scripts, notamment autour de mysqld et mariadbd. Les migrations MySQL/MariaDB aiment beaucoup les petits détails qui cassent à 6 h 25, c’est leur sens du théâtre.
Cron ou systemd timer ?
Historiquement, logrotate tourne via /etc/cron.daily/logrotate. Sur certaines distributions modernes, il peut aussi être lancé via un timer systemd.
Vérifiez cron :
ls -l /etc/cron.daily/logrotate
Vérifiez systemd :
systemctl list-timers | grep -i logrotate
systemctl status logrotate.timer --no-pager
systemctl status logrotate.service --no-pagerCode language: PHP (php)
Le message d’erreur peut donc arriver par email cron, dans les logs systemd, ou les deux selon votre distribution.
Diagnostic rapide complet
Voici la séquence que j’utiliserais aujourd’hui :
# Identifier le fichier logrotate concerné.
ls -l /etc/logrotate.d/ | grep -E 'mysql|maria'
# Voir son contenu.
sudo sed -n '1,200p' /etc/logrotate.d/mysql-server 2>/dev/null
sudo sed -n '1,200p' /etc/logrotate.d/mariadb 2>/dev/null
# Tester ce que retourne my_print_defaults.
my_print_defaults --mysqld | grep 'pid-file'
my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"
# Chercher les directives pid-file en double.
grep -R "^[[:space:]]*pid-file" /etc/mysql /etc/my.cnf 2>/dev/null
# Tester logrotate en debug.
sudo logrotate -d /etc/logrotate.conf
# Tester uniquement le fichier MySQL/MariaDB.
sudo logrotate -d /etc/logrotate.d/mysql-server 2>/dev/null
sudo logrotate -d /etc/logrotate.d/mariadb 2>/dev/null
# Vérifier les commandes admin SQL.
command -v mysqladmin
command -v mariadb-admin
# Voir les timers systemd.
systemctl list-timers | grep -i logrotateCode language: PHP (php)
Correction rapide
Si la cause est bien une commande grep qui retourne plusieurs chemins de PID, corrigez la ligne :
if [ -f `my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"` ]; thenCode language: CSS (css)
en :
if [ -f `my_print_defaults --mysqld | grep -oPm1 "pid-file=\K[^$]+"` ]; thenCode language: CSS (css)
Puis testez :
sudo logrotate -d /etc/logrotate.d/mysql-server
ou :
sudo logrotate -d /etc/logrotate.d/mariadb
Si le mode debug ne remonte plus l’erreur, la prochaine exécution cron ne devrait plus envoyer d’email.
Mémo rapide
# Erreur.
logrotate_script: 3: [: /var/run/mysqld/mysqld.pid: unexpected operator
# Fichier à vérifier.
sudo nano /etc/logrotate.d/mysql-server
sudo nano /etc/logrotate.d/mariadb
# Ligne problématique.
if [ -f `my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"` ]; then
# Correction.
if [ -f `my_print_defaults --mysqld | grep -oPm1 "pid-file=\K[^$]+"` ]; then
# Vérifier ce que retourne la commande.
my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"
# Chercher les pid-file multiples.
grep -R "^[[:space:]]*pid-file" /etc/mysql /etc/my.cnf 2>/dev/null
# Tester logrotate sans rien modifier.
sudo logrotate -d /etc/logrotate.conf
sudo logrotate -d /etc/logrotate.d/mysql-server
sudo logrotate -d /etc/logrotate.d/mariadb
# Forcer une rotation si nécessaire.
sudo logrotate -f /etc/logrotate.d/mysql-server
sudo logrotate -f /etc/logrotate.d/mariadbCode language: PHP (php)
Conclusion
L’erreur logrotate_script: 3: [: /var/run/mysqld/mysqld.pid: unexpected operator vient généralement d’un test shell qui reçoit trop d’arguments, souvent parce que my_print_defaults retourne plusieurs valeurs pid-file.
La correction rapide consiste à limiter grep à la première correspondance :
grep -oPm1 "pid-file=\K[^$]+"Code language: JavaScript (javascript)
Mais le bon réflexe consiste aussi à vérifier s’il existe plusieurs directives pid-file dans la configuration MySQL/MariaDB, surtout après une migration ou un déplacement du datadir.
Enfin, testez toujours logrotate en mode debug avec logrotate -d avant d’attendre le prochain cron. Cela évite de découvrir le lendemain matin que le serveur vous a encore écrit une lettre d’amour passive-agressive.
Votre base de données ralentit tout ?
Tables wp_options surchargées, autoload incontrôlé, requêtes non indexées — une base WordPress mal entretenue finit toujours par plomber les temps de réponse. Je l'audite, je la nettoie, je l'optimise.
Diagnostiquons votre base de données →
