Article écrit par François Vantomme
Comme promis dans l’article portant sur les classes de caractères des regex, me revoilà avec, cette fois, un exemple concret que je vais exécuter froidement devant vous !
En passant d’un macOS à une Debian GNU/Linux, je me suis aperçu d’un comportement inattendu sur un script ZSH de ma confection en ce qui concerne les regex, et plus particulièrement les classes de caractères.
Le contexte
L’idée était de détecter la présence d’un mot dans une chaîne de caractères. Prenons la chaîne suivante en exemple :
bat exa fd fzf git htop ncdu neovim ripgrep tig tldr tmux tree watch z zplug
Mettons maintenant que nous recherchions la présence du mot « tldr » dans cette liste. D’après ce que nous savons des classes de caractères, nous pouvons par exemple écrire la regex suivante :
/[[:<:]]tldr[[:>:]]/
Les classes de caractères [[:<:]]
et [[:>:]]
représentent respectivement le début et la fin d’un mot. Cela nous permet de nous assurer de ne pas tomber sur une suite de caractères au milieu d’un mot. On peut ainsi rechercher « z » sans tomber sur « fzf » ou « zplug », pour reprendre notre exemple.
Lost in the Shell
Voyons à présent ce que cela donne quand on utilise notre petite regex dans le contexte de ZSH.
❯ uname Darwin ❯ [[ "bat tldr zplug" =~ [[:<:]]man[[:>:]] ]] && echo "true" || echo "false" false ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" true
Tout semble se passer pour le mieux ! Essayons sous GNU/Linux :
❯ uname Linux ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" zsh: failed to compile regex: Nom de classe de caractères invalide false
Outch ! Mais que se passe-t-il ?
Let’s Read The Famous Manual !
Un petit tour dans la documentation de ZSH devrait nous aiguiller… voyons voir.
REMATCH_PCRE If set, regular expression matching with the =~ operator will use Perl-Compatible Regular Expressions from the PCRE library. (The zsh/pcre module must be available.) If not set, regular expressions will use the extended regexp syntax provided by the system libraries.
Il semblerait qu’une option nous permettrait d’imposer une bibliothèque compatible Perl (PCRE). Si cette option n’est pas définie, nous sommes dépendants de la bibliothèque système. Allons-y !
❯ uname Darwin ❯ setopt rematch_pcre ❯ [[ "bat tldr zplug" =~ [[:<:]]man[[:>:]] ]] && echo "true" || echo "false" false ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" true ❯ unsetopt rematch_pcre ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" true
❯ uname Linux ❯ setopt rematch_pcre ❯ [[ "bat tldr zplug" =~ [[:<:]]man[[:>:]] ]] && echo "true" || echo "false" false ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" true ❯ unsetopt rematch_pcre ❯ [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] && echo "true" || echo "false" zsh: failed to compile regex: Nom de classe de caractères invalide false
Parfait, ça semble faire le boulot ! Cela dit, si nous nous rappelons bien du tableau présenté dans l’article précédent, il existe d’autres manières de délimiter un mot :
❯ uname Linux ❯ unsetopt rematch_pcre ❯ [[ "bat tldr zplug" =~ "<tldr>" ]] && echo "true" || echo "false" true ❯ [[ "bat tldr zplug" =~ "btldrb" ]] && echo "true" || echo "false" true
Mais là, manque de chance, c’est macOS qui flanche :
❯ uname Darwin ❯ unsetopt rematch_pcre ❯ [[ "bat tldr zplug" =~ "btldrb" ]] && echo "true" || echo "false" false ❯ [[ "bat tldr zplug" =~ "<tldr>" ]] && echo "true" || echo "false" false
La solution
À la vue de ces comportements bigarrés, la meilleure option qui s’offre à nous est de nous assurer qu’un moteur PCRE sera utilisé, ou d’utiliser une regex de repli dans le cas contraire. Ce qui pourrait donner ceci :
if [[ -o rematchpcre || "$OSTYPE" == darwin* ]]; then [[ "bat tldr zplug" =~ [[:<:]]tldr[[:>:]] ]] else [[ "bat tldr zplug" =~ "<tldr>" ]] fi
On considère ici que si l’option ZSH rematchpcre
est activée ou si le système d’exploitation est macOS (darwin
de son petit nom), alors on pourra utiliser notre regex compatible Perl.
En espérant que ce petit retour d’expérience vous aura appris une ou deux choses et donné l’envie de lire le fameux manuel quand une question vous taraude !