Comment puis-je empêcher les options «shopt» non prises en charge de provoquer des erreurs dans mon .bashrc?

9

Je travaille dans un environnement relativement hétérogène où je peux exécuter différentes versions de Bash sur différents nœuds HPC, machines virtuelles ou mon poste de travail personnel. Parce que je mets mes scripts de connexion dans un référentiel Git, je voudrais utiliser le même (ish) à .bashrctous les niveaux, sans beaucoup de "si cet hôte, alors ..." - type désordre.

Je comme le comportement par défaut de Bash ≤ 4.1 qui se développe cd $SOMEPATHen cd /the/actual/pathlorsque vous appuyez sur la Tabtouche. Dans Bash 4.2 et supérieur, vous devrez shopt -s direxpandréactiver ce comportement, et ce n'est devenu disponible que le 4.2.29 . Ce n'est qu'un exemple, cependant; une autre shoptoption , éventuellement liée , complete_fullquote(bien que je ne sache pas exactement ce qu'elle fait) peut également avoir changé le comportement par défaut de la v4.2.

Cependant, direxpandn'est pas reconnu par les versions antérieures de Bash, et si je tente de shopt -s direxpandmon .bashrc, qui se traduit par un message d'erreur en cours d' impression à la console à chaque fois que je me connecte à un nœud avec un Bash plus:

-bash: shopt: direxpand: invalid shell option name

Ce que j'aimerais faire, c'est encapsuler un conditionnel shop -s direxpandpour activer cette option sur Bash> 4.1 de manière robuste, sans frotter les anciennes versions de Bash ( c'est -à- dire , pas seulement rediriger la sortie d'erreur vers /dev/null).

TheDudeAbides
la source
Comment ma réponse n'a pas aidé?
Luciano Andress Martini
@LucianoAndressMartini Oui, et c'est la solution avec laquelle j'ai fini par aller de moi-même .bashrc. Je voulais toujours un enregistrement de la façon d'utiliser $BASH_VERSINFOpour interroger la version majeure / mineure du shell en cours d'exécution, pour ma propre édification, c'est pourquoi j'ai fini de poster ma propre réponse. :)
TheDudeAbides
Regardez dans ma réponse j'ai quelque chose sur la comparaison de la version des programmes avec le script shell.
Luciano Andress Martini

Réponses:

14

Vérifiez s'il direxpandest présent dans la sortie de shoptet activez-le s'il est:

shopt | grep -q '^direxpand\b' && shopt -s direxpand
Luciano Andress Martini
la source
4
Mieux vaut le faire grep -q '^direxpand\b'dans le cas où une future version ou fork de bash a une option qui la contient comme sous - chaîne et la supprime direxpand. Peu probable dans ce cas spécifique, mais il ne coûte pas cher d'être robuste.
Gilles 'SO- arrête d'être méchant'
Merci Luciano. J'avais l'intention de répondre à ma propre question, mais j'accepterai votre réponse après que mes modifications
auront été
4
Bash permet d'interroger des options de shell spécifiques, donc on peut les utiliser [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Plus de problèmes d'expression régulière! :-)
David Foerster
@DavidFoerster Je renverserais la logique: [ -n "blah" ] && shopt blahla façon dont vous l'exprimez, vous dites "si direxpand n'est pas pris en charge, alors ne faites pas cette chose".
Rich
1
@Rich: La plupart de mes scripts shell incluent set -een haut, j'ai donc tendance à utiliser une logique de raccourci de cette façon.
David Foerster
16

Je ne vois pas ce qui ne va pas avec la redirection des erreurs /dev/null. Si vous voulez que votre code soit robuste set -e, utilisez l'idiome commun … || true:

shopt -s direxpand 2>/dev/null || true

Si vous souhaitez exécuter du code de secours si l'option n'existe pas, utilisez l'état de retour de shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Mais si vous n'aimez pas vraiment rediriger l'erreur, vous pouvez utiliser le mécanisme de complétion pour effectuer une introspection. Cela suppose que vous n'avez pas de machines obsolètes avec bash ≤ 2.03 qui n'avaient pas de fin programmable.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Cette méthode évite le bifurcation, qui est lent sur certains environnements tels que Cygwin. Il en va de même pour le simple 2>/dev/null, je ne pense pas que vous pouvez battre cela sur la performance.

Gilles 'SO- arrête d'être méchant'
la source
Ce n'est pas où mon cerveau serait allé, mais j'aime la compgenproposition. C'est des trucs de niveau universitaire juste là! Éviter la redirection vers /dev/nulln'est qu'une préférence personnelle. J'aime demander la permission au lieu du pardon, si cela a du sens? :)
TheDudeAbides
+1 pour une scolarité totalement imprévue en achèvement programmable Bash, ce qui m'a obligé à aller dans le manuel pour déchiffrer ce que compgen -A shopt -X ...cela signifiait.
TheDudeAbides
4
@TheDudeAbides J'ai lu comment utiliser compgencette méthode sur Unix et Linux , je ne sais pas qui l'a proposé en premier. (J'ai cessé d'utiliser bash comme shell principal avant qu'il ne soit programmé.) En programmation, c'est généralement une mauvaise idée de demander la permission car il y a un risque que la vérification des permissions ne corresponde pas à ce que vous faites réellement, soit à cause d'un codage erreur (où vous ne vérifiez pas vraiment ce que vous pensez vérifier) ​​ou parce que ce que vous avez vérifié a changé avant de l'utiliser .
Gilles 'SO- arrête d'être méchant'
5

Lorsque vous savez avec certitude qu'une shoptoption spécifique est disponible dans une certaine version majeure / mineure / de patch de Bash, vous pouvez inspecter la $BASH_VERSIONvariable ou les éléments du $BASH_VERSINFO[]tableau afin de l'activer conditionnellement.

Voici un test pour Bash 4.2.29 ou supérieur, la version où a direxpand été introduite pour la première fois dans la série 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

Edit: Pour être clair, il s'agit d'une solution ridiculement surdimensionnée pour ignorer simplement un message d'erreur provenant de vos scripts de connexion, mais je voulais le documenter malgré tout, pour ma propre édification.

Notez les accolades autour , qui sont obligatoires, et l'utilisation de et , qui font des comparaisons lexicales entières plutôt que (dépendantes des paramètres régionaux). S'il n'est pas cité, le RHS de l' opérateur est traité comme des motifs "extglob" dans Bash / conditionals, comme indiqué ici , ce qui rend une comparaison "commence par" plus esthétique qu'un regex, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

Le $BASH_VERSINFOtableau contient toutes les informations que vous verriez dans la sortie de bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Lorsqu'il n'est pas clair dans la documentation pour shoptlaquelle les versions de Bash ont été prises en charge ou ont changé leur comportement, la méthode proposée par Luciano est très bien:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... tout comme la solution proposée par Gilles d'ignorer simplement l'erreur ( shopt -s direxpand 2>/dev/null), et peut-être de vérifier $?si c'est absolument nécessaire.

Références: 1 , 2 , 3
Lecture connexe: Set and Shopt - Why Two?

TheDudeAbides
la source
Vous pourriez également être en mesure d'utiliser quelque chose comme if [[ $BASH_VERSION > 4.3 ]];(qui correspond 4.3.0, 5.0etc., mais aussi 4.3.0-alpha. Je ne sais pas si le fait ultérieur compte.)
ilkkachu
Salut @ilkkachu. Merci pour votre montage pour couvrir Bash v5.x. L' direxpandoption est en effet disponible pour Bash 4.2, cependant; J'ai vérifié cela avec une image Docker en v4.2.53 en exécutant docker run --rm bash:4.2 bash -c shopt | grep direxpand(et, pour faire bonne mesure, qu'elle n'est en effet pas disponible en v4.1.17 en exécutant docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides
ah ok, j'ai testé 4.2.0et suis tombé sur le fait que cela ne fonctionnait pas là-bas. Le journal des modifications mentionne également son ajout bash-4.3-alpha. Je suppose alors qu'il faudrait vérifier ${BASH_VERSINFO[2]}pour être exact à ce sujet, mais je ne sais pas quel point de sortie l'a ajouté ...
ilkkachu
Je pense que nous avons essentiellement prouvé le point soulevé par Gilles ci-dessus; qu'il est, en fait, préférable d' essayer simplement d'activer l'option shell, puis de traiter l'erreur (ou de la supprimer) si elle n'est pas prise en charge.
TheDudeAbides