Connexions SSH et « locales » manquantes
Vincent Bernat
Sur mon système, j’initialise les variables LANG
et LC_MESSAGES
à,
respectivement, fr_FR.utf8
et en_US.utf8
. Ainsi, les différents
programmes appliquent les paramètres régionaux français à l’exception
des messages qui sont affichés en anglais. Cela implique de bien
inclure ces deux « locales » dans le fichier
/etc/locale.gen
. Toutefois, celles-ci peuvent être indisponibles sur
certains systèmes distants. La plupart des applications se rabattent
sur la locale C
sans broncher. Une exception notable est Perl qui se
plaint très bruyamment :
$ perl -e 'print "Hello\n";' perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: LANGUAGE = (unset), LC_ALL = (unset), LC_MESSAGES = "en_US.utf8", LANG = "fr_FR.utf8" are supported and installed on your system. perl: warning: Falling back to the standard locale ("C"). Hello
Ce message est une véritable plaie. Je ne comprends pas qu’il soit
encore présent actuellement. La
documentation de Perl explique comment se débarasser de
ce message. La manière la plus expéditive est d’utiliser la variable
d’environnement PERL_BADLANG
:
$ PERL_BADLANG=0 perl -e 'print "Hello\n";' Hello
Toutefois, cela ne résout en rien notre problème. Quand on se connecte
sur un système distant avec ssh
, les variables d’environnement sont
pour la plupart mises à la poubelle. La directive AcceptEnv
du
fichier /etc/ssh/sshd_config
du système distant indique quelles sont
les variables d’environnement autorisées à survivre. Sur un système
Debian, la valeur de cette directive est LANG LC_*
.
Comme je ne peux pas passer sur chaque système installer les locales
manquantes et qu’il ne m’est pas non plus possible d’aller
tripatouiller chaque shell distant pour corriger l’environnement,
j’utilise quelque chose comme ça dans mon .zshrc
:
ssh() { [ -t 1 ] && echo -ne "\033]0;$@\007" LANG=C LC_MESSAGES=C =ssh "$@" }
Le défaut majeur de cette approche est que je me retrouve avec la
locale C
sur tous les systèmes, y compris ceux qui disposent de mes
locales préférées. Si je supprimais les variables d’environnement
LANG
et LC_MESSAGES
, j’utiliserais la locale par défaut du système
distant qui peut tout à fait être composée d’une langue qui m’est
inconnue. De plus, le comportement de la locale C
n’est pas défini
quand il y a utilisation de caractères non-ASCII. Voici ce que dit
IEEE Std 1003.1 :
Conforming systems shall provide a POSIX locale, also known as the C locale. The behavior of standard utilities and functions in the POSIX locale shall be as if the locale was defined via the
localedef
utility with input data from the POSIX locale tables in Locale Definition.The tables in Locale Definition describe the characteristics and behavior of the POSIX locale for data consisting entirely of characters from the portable character set and the control character set. For other characters, the behavior is unspecified. For C-language programs, the POSIX locale shall be the default locale when the
setlocale()
function is not called.The POSIX locale can be specified by assigning to the appropriate environment variables the values
C
orPOSIX
.
Il est impossible de savoir à l’avance les locales installées sur un
système distant et initier une connexion juste pour vérifier la locale
est beaucoup trop coûteux. Voici un autre extrait de mon .zshrc
(que
je place sur certains systèmes) pour rétablir une locale proche de mes
préférences si possible :
export LANG=C export LC_MESSAGES=C (( $+commands[locale] )) && function { local available local locales local locale locales=( "LANG fr_FR.utf8 en_US.utf8 C.UTF-8 C" \ "LC_MESSAGES en_US.utf8 fr_FR.utf8 C.UTF-8 C" ) available=("${(f)$(locale -a)}") for locale in $locales; do for l in $=locale[(w)2,-1]; do if (( ${available[(i)$l]} <= ${#available} )); then export $locale[(w)1]=$l break fi done done unset LC_ALL } 2> /dev/null
Il y a quelques techniques intéressantes mais propres à Zsh.
- L’utilisation de
(( $+commands[locale] ))
permet de vérifier l’existence de la commandelocale
. C’est plus joli queif $(which locale >& /dev/null)
. - Une fonction anonyme permet d’éviter de polluer l’environnement du shell avec des variables inutiles.
$locale[(w)1]
convertit$locale
en tableau et extrait le premier élément.$=locale[(w)2,-1]
convertit$locale
en tableau, conserve tous les éléments sauf le premier et applique le word splitting (par défaut, Zsh ne le fait pas tout seul).
Si vous connaissez une meilleure méthode pour gérer ce problème, n’hésitez pas à me l’indiquer !