Lors d’une synchronisation avec rsync over SSH, il peut arriver de tomber sur cette erreur :
TERM environment variable not set.
protocol version mismatch -- is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2)Code language: JavaScript (javascript)
Le message est un peu cryptique, mais la cause est souvent simple : le shell distant affiche du texte au moment où rsync s’attend à parler uniquement avec le processus rsync distant.
Autrement dit, la connexion SSH fonctionne, mais quelque chose pollue le flux : un echo, un message de bienvenue, une commande interactive, un script dans .bashrc, un fortune, un neofetch, un tput, une bannière, ou un message lancé depuis /etc/ssh/sshrc.
Et rsync, lui, n’a aucun humour dans ce cas. Il lit du texte inattendu au lieu du protocole attendu, puis il répond : “is your shell clean?”. Traduction libre : “ton shell bavarde trop”.
Pourquoi rsync affiche “is your shell clean?”
Quand vous lancez une commande comme celle-ci :
rsync -avz ./site/ user@example.com:/var/www/site/Code language: JavaScript (javascript)
rsync ouvre une connexion distante, souvent via SSH, puis démarre un autre processus rsync sur le serveur distant. Les deux processus échangent ensuite des données selon le protocole rsync.
Si le shell distant imprime quelque chose avant le démarrage du rsync distant, le protocole est contaminé. Quelques caractères suffisent à casser l’échange.
La documentation rsync explique que l’erreur “protocol version mismatch — is your shell clean?” vient généralement de scripts de démarrage ou de la couche shell distante qui produisent une sortie parasite sur le flux utilisé par rsync. La méthode de diagnostic recommandée consiste à lancer une commande distante silencieuse et à vérifier qu’elle ne produit aucune sortie.
Tester si le shell distant est propre
Le test le plus simple consiste à exécuter /bin/true sur le serveur distant via SSH. Cette commande ne doit rien afficher.
ssh user@example.com /bin/trueCode language: JavaScript (javascript)
Si tout est propre, vous ne devez obtenir absolument aucune sortie.
Pour capturer toute sortie parasite dans un fichier local :
ssh user@example.com /bin/true > out.datCode language: JavaScript (javascript)
Puis vérifiez le fichier :
cat -A out.datCode language: CSS (css)
Si out.dat contient du texte, des caractères invisibles, des codes couleur ANSI, une bannière, ou un message d’erreur, le shell distant n’est pas propre pour rsync.
Vous pouvez aussi tester la présence de sortie sur stderr :
Vos sauvegardes sont-elles vraiment fiables ?
Une sauvegarde que vous n'avez jamais testée n'est pas une sauvegarde — c'est une illusion de sécurité. Je mets en place des backups automatisés, hors-site, vérifiés, avec procédure de restauration documentée.
Mettons en place une vraie stratégie de backup →ssh user@example.com /bin/true > out.dat 2> err.dat
cat -A out.dat
cat -A err.datCode language: JavaScript (javascript)
Le but est simple : pour une commande non interactive, le shell distant doit rester silencieux.
Cause fréquente : du texte dans .bashrc
La cause la plus classique se trouve dans ~/.bashrc, ~/.profile, ~/.bash_profile ou un fichier chargé depuis ces scripts.
Exemples de lignes qui peuvent casser rsync :
echo "Bienvenue sur le serveur"
date
uptime
df -h
neofetch
screenfetch
fortune
tput setaf 2
printf "Serveur prêt\n"Code language: PHP (php)
Ces commandes sont agréables lors d’une connexion SSH interactive. En revanche, elles sont toxiques pour les commandes automatisées comme rsync, scp, sftp, certains hooks Git ou des scripts de déploiement.
Le manuel Bash précise que ~/.bashrc est lu lorsqu’un shell interactif non-login démarre. Il précise aussi le comportement des fichiers comme ~/.bash_profile, ~/.bash_login et ~/.profile pour les shells de login.
Solution : ne rien afficher dans un shell non interactif
La bonne pratique consiste à arrêter immédiatement .bashrc lorsque le shell n’est pas interactif.
Ajoutez ce bloc tout en haut de ~/.bashrc sur le serveur distant :
# Stop here if the shell is not interactive.
case $- in
*i*) ;;
*) return ;;
esacCode language: PHP (php)
Cette condition vérifie si le shell est interactif. Si ce n’est pas le cas, le fichier s’arrête immédiatement. Les aliases, couleurs, messages de bienvenue, prompts enrichis et commandes décoratives ne seront donc pas exécutés pour rsync.
Sur beaucoup de distributions Linux, ce bloc existe déjà dans le .bashrc par défaut, parfois sous cette forme :
# If not running interactively, don't do anything.
[ -z "$PS1" ] && returnCode language: PHP (php)
La version avec $- est généralement plus explicite, car Bash inclut i dans $- lorsque le shell est interactif.
Déplacer les messages dans le bon fichier
Si vous voulez afficher un message lors d’une connexion SSH interactive, ne le faites pas n’importe où.
Gardez cette règle simple :
.bashrc: configuration interactive du shell, aliases, prompt, complétion, couleurs..profileou.bash_profile: configuration de session login, variables d’environnement.- Messages décoratifs : uniquement après un test confirmant que le shell est interactif.
Exemple propre dans ~/.bashrc :
# Stop here if the shell is not interactive.
case $- in
*i*) ;;
*) return ;;
esac
echo "Bienvenue sur $(hostname)"
uptimeCode language: PHP (php)
Avec cette structure, le message s’affiche quand vous ouvrez un vrai shell interactif, mais il ne pollue pas rsync.
Rediriger les sorties ne suffit pas toujours
On voit parfois cette solution :
commande_bavarde >/dev/null 2>&1Code language: JavaScript (javascript)
Elle peut dépanner, mais ce n’est pas la meilleure architecture. Un fichier de démarrage ne devrait pas lancer des commandes inutiles pour les sessions non interactives, même si leur sortie est masquée.
La meilleure solution reste donc de ne pas exécuter ces commandes du tout lorsque le shell n’est pas interactif.
Vérifier aussi /etc/bash.bashrc, /etc/profile et /etc/ssh/sshrc
Si votre ~/.bashrc semble propre mais que l’erreur persiste, vérifiez les fichiers système.
sudo grep -R "echo\|printf\|neofetch\|screenfetch\|fortune\|tput\|date\|uptime\|df -h" \
/etc/bash.bashrc \
/etc/profile \
/etc/profile.d \
/etc/ssh/sshrc \
2>/dev/nullCode language: JavaScript (javascript)
Le fichier /etc/ssh/sshrc est un piège classique. Il peut lancer des commandes à chaque connexion SSH, y compris pour des commandes non interactives. Si ce fichier affiche du texte, déclenche une notification mal redirigée, ou écrit sur stdout, rsync peut échouer.
Si vous devez garder une notification SSH, envoyez-la vers syslog, un fichier, ou un processus silencieux. Ne l’écrivez pas sur stdout.
Cas particulier : “TERM environment variable not set”
Le message suivant est un indice utile :
TERM environment variable not set.Code language: JavaScript (javascript)
Il apparaît souvent lorsqu’une commande pensée pour un terminal interactif est lancée dans un contexte non interactif. Par exemple :
clear
tput colors
tput setaf 2
dialog
whiptail
neofetch
Ces commandes supposent l’existence d’un terminal. Lors d’un rsync over SSH, ce n’est pas le bon contexte.
Si vous tenez vraiment à utiliser tput ou clear, protégez-les :
if [[ -t 1 && -n "$TERM" ]]; then
tput setaf 2
echo "Bienvenue"
tput sgr0
fiCode language: PHP (php)
Mais, encore une fois, le plus propre est de ne lancer ce type de code qu’après le test interactif placé en haut de .bashrc.
Vérifier que rsync est bien installé côté distant
L’erreur “protocol version mismatch” peut aussi apparaître si la commande distante ne lance pas le bon binaire, ou si rsync n’est pas installé côté serveur.
Vérifiez la version locale :
rsync --version
Puis la version distante :
ssh user@example.com 'rsync --version'Code language: CSS (css)
Si le serveur répond command not found, installez rsync côté distant.
Sous Debian ou Ubuntu :
sudo apt update
sudo apt install rsync
Sous Rocky Linux, AlmaLinux, Fedora ou RHEL :
sudo dnf install rsync
Si rsync existe mais n’est pas dans le PATH utilisé par SSH, vous pouvez préciser le chemin distant :
rsync -avz --rsync-path=/usr/bin/rsync ./site/ user@example.com:/var/www/site/Code language: JavaScript (javascript)
Tester avec une commande rsync minimale
Une fois le shell nettoyé, testez avec une synchronisation minimale :
mkdir -p /tmp/rsync-test
echo "test" > /tmp/rsync-test/test.txt
rsync -av /tmp/rsync-test/ user@example.com:/tmp/rsync-test/Code language: JavaScript (javascript)
Si cette commande fonctionne, le transport SSH et le protocole rsync sont à nouveau propres.
Vous pouvez aussi ajouter -e ssh explicitement :
rsync -av -e ssh /tmp/rsync-test/ user@example.com:/tmp/rsync-test/Code language: JavaScript (javascript)
Checklist rapide de correction
Voici la séquence que j’utilise pour diagnostiquer cette erreur :
# 1. Tester si SSH produit une sortie parasite.
ssh user@example.com /bin/true > out.dat 2> err.dat
cat -A out.dat
cat -A err.dat
# 2. Tester si rsync existe côté distant.
ssh user@example.com 'command -v rsync && rsync --version'
# 3. Chercher les commandes bavardes dans les fichiers de démarrage.
ssh user@example.com 'grep -R "echo\|printf\|neofetch\|screenfetch\|fortune\|tput\|clear\|uptime\|df -h" ~/.bashrc ~/.profile ~/.bash_profile 2>/dev/null'
# 4. Corriger ~/.bashrc en ajoutant un garde interactif en haut du fichier.
# Stop here if the shell is not interactive.
case $- in
*i*) ;;
*) return ;;
esac
# 5. Relancer le test silencieux.
ssh user@example.com /bin/true
# 6. Relancer rsync.
rsync -avz ./site/ user@example.com:/var/www/site/Code language: PHP (php)
Si ssh user@example.com /bin/true ne retourne rien, vous avez déjà éliminé la cause la plus fréquente.
Exemple de .bashrc propre
Voici une base saine pour un ~/.bashrc compatible avec rsync, scp, sftp et les scripts de déploiement :
# Stop here if the shell is not interactive.
case $- in
*i*) ;;
*) return ;;
esac
# Interactive aliases.
alias ll='ls -lah'
alias grep='grep --colour=auto'
# Interactive prompt.
PS1='\u@\h:\w\$ '
# Optional interactive-only message.
if [[ -t 1 ]]; then
echo "Connected to $(hostname)"
fiCode language: PHP (php)
Le point essentiel est tout en haut. Toute sortie texte doit arriver après le test interactif. Avant ce test, rien ne doit afficher quoi que ce soit.
À ne pas faire
- Ne mettez pas
echo,date,uptimeoudf -hau début de.bashrc. - Ne lancez pas
neofetchouscreenfetchsans tester si le shell est interactif. - Ne faites pas dépendre
rsyncd’un shell qui affiche une bannière. - Ne supposez pas que le problème vient forcément des versions rsync.
- Ne redirigez pas au hasard si vous pouvez éviter d’exécuter la commande.
Conclusion
L’erreur protocol version mismatch -- is your shell clean? indique presque toujours que le shell distant écrit du texte au mauvais moment. rsync attend un protocole propre ; il reçoit une bannière, un message, une commande interactive ou une erreur TERM. Forcément, il fait la tête.
La correction consiste à rendre le shell distant silencieux pour les sessions non interactives. Le garde à placer en haut de ~/.bashrc est généralement suffisant :
case $- in
*i*) ;;
*) return ;;
esacCode language: JavaScript (javascript)
Ensuite, testez :
ssh user@example.com /bin/trueCode language: JavaScript (javascript)
Si cette commande ne renvoie rien, votre shell est propre. rsync peut alors reprendre son vrai métier : synchroniser des fichiers sans se faire interrompre par un shell trop bavard.
Sources
- Man page officielle rsync
- GNU Bash Manual : fichiers de démarrage
- bash(1) : manuel Linux
- ssh(1) : manuel Linux
- rsync(1) : manuel Linux
Vos sauvegardes sont-elles vraiment fiables ?
Une sauvegarde que vous n'avez jamais testée n'est pas une sauvegarde — c'est une illusion de sécurité. Je mets en place des backups automatisés, hors-site, vérifiés, avec procédure de restauration documentée.
Mettons en place une vraie stratégie de backup →

Bonjour,
Merci pour l’astuce, je rencontre ce problème et je n’arrive pas à le résoudre.
J’ai créé un fichier bash et je ne vois pas ce qu’il y a à nettoyer sur /~.bashrc.
Je continue de chercher
Bonjour Widee,
Tu dois avoir une commande ou un bout de code qui génére du texte dans le bash.
Quel est le contenu de ton fichier
<.bashrc?