Comment puis-je obtenir la version de ksh en toute sécurité?

Comment puis-je obtenir en toute sécurité la version de ksh depuis un script ksh?

J'ai vu les solutions suivantes :

  1. ksh --version
  2. echo $ {. sh.version}
  3. echo $ KSH_VERSION

Et dans les bonnes circonstances, chacune d’elles fonctionne correctement. Cependant, je me soucie du cas non parfait.

En particulier, plusieurs machines sur lesquelles je travaille ont des versions plus anciennes de ksh qui, à mon sens, manquent cruellement de fonctionnalités. Quoi qu'il en soit, la raison pour laquelle je veux vérifier la version (par programme) est de voir si la version de ksh est l’une des versions les moins capables; et si oui, je veux exécuter une branche avec un code moins impressionnant.

Cependant, sur les machines problématiques, l’inaptitude du shell s’étend à la vérification de la version ...

  • If I try ksh --version, it prints nothing and opens a new instance of ksh!
  • If I try echo ${.sh.version}, ksh treats this as a syntax error that cannot be discarded with 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
    
  • Of course echo $KSH_VERSION appears to work fine – I mean it won't crash – though on these machines it's blank. Also, I saw somewhere that KSH_VERSION is set only by pdksh.

Des questions:

  • Comment vérifier en toute sécurité la version de ksh par programme? Pour les besoins de mon propos, peu importe le numéro de version, peu importe qu'il s'agisse d'une version obsolète de ksh .
  • $ KSH_VERSION est-il suffisant? Je veux dire que si elle est vide, alors ksh est-il nécessairement une version obsolète? Cet autre forum a-t-il eu raison de ne pas le définir même pour les versions les plus récentes de ksh ?
  • N'y a-t-il aucun moyen de vérifier cela?
12
@ ThorbjørnRavnAndersen cela a à voir avec l'invite. Dans mon fichier .kshrc, j'ai une fonction qui simule la fonctionnalité d'abréviation de pwd des invites tcsh et zsh, et j'ai configuré PS1 pour utiliser cette fonction. Cependant, Old ksh ne prend pas en charge $() dans PS1 . Donc, si c'est une version moderne de ksh, je veux que PS1 utilise la fonction que j'ai créée; si c'est l'ancienne version, j'utilise seulement $ PWD .
ajouté l'auteur Jimmie R. Houts, source
Une autre approche pourrait consister simplement à dire "Seule la machine en question pose problème - je vais trouver un fichier ou une variable d'environnement ou quelque chose d'autre qui existe seulement ici (probablement AIX ou quelque chose de toute façon) et le tester à la place".
ajouté l'auteur Swaroop C H, source
Eh bien, vous pourriez avoir deux versions de votre fichier de configuration (peut-être l’une générée à partir de l’autre) et distribuer ensuite la version appropriée à la machine en question?
ajouté l'auteur Swaroop C H, source
Une raison pour laquelle vous voulez deux chemins de code et pas un seul avec un code moins génial?
ajouté l'auteur Swaroop C H, source

7 Réponses

Je pense que .sh.version existe depuis la première version de ATT ksh 93. Il n'est pas disponible dans pdksh ou mksh. Puisque $ {. Sh.version} est une erreur de syntaxe dans les shells autres que ksh93, enveloppez le test dans un sous-shell et protégez-le derrière eval .

_sh_version=$(eval 'echo "${.sh.version}"' 2>/dev/null) 2>/dev/null
case $_sh_version in
  '') echo "This isn't ATT ksh93";;
  …
esac

KSH_VERSION started out in the public domain ksh clone (pdksh), and was added to the actual Korn shell relatively recently, in 2008 with ksh93t.

Plutôt que de rechercher un numéro de version, vous devez rechercher la fonctionnalité spécifique qui vous pose problème. La plupart des fonctionnalités peuvent être testées en essayant une construction dans un sous-shell et voir si cela déclenche une erreur.

7
ajouté
Pour les futurs lecteurs, les systèmes d'exploitation que je référence sont Solaris 5.10, HP-UX 11 et AIX 7.
ajouté l'auteur Jimmie R. Houts, source
C'est un peu mieux. Cela fonctionne parfaitement dans Solaris et HP-UX. Pour AIX, cela fonctionne sur la ligne de commande, mais curieusement, il échoue à nouveau si j'essaie de le placer dans une fonction shell.
ajouté l'auteur Jimmie R. Houts, source
Je comprends ce que vous dites. Ce que je dis, c'est que l'erreur ne redirige pas. Il toujours imprime sur la console. J'ai essayé cela sous Solaris, AIX et HP-UX; et ksh présente ce comportement dans chacun d'eux.
ajouté l'auteur Jimmie R. Houts, source
Je ne vois aucune différence lors de l'utilisation d'un sous-shell. Il traite toujours $ {. Sh.version} comme une erreur de syntaxe qui ne peut pas être réconciliée. Le message que je reçois est substitution incorrecte .
ajouté l'auteur Jimmie R. Houts, source
@ GregA.Woods Aïe. Pouvez-vous essayer _sh_version = $ (echo "$ {. Sh.version}"; true) 2>/dev/null ou _sh_version = $ (eval 'echo "$ {. Sh. version} "') 2>/dev/null ?
ajouté l'auteur Valters Vingolds, source
@ Sildoreth Ah. Je n'avais testé que sur Linux et je n'ai aucun de ces systèmes à tester pour le moment. eval '_sh_version = $ (echo "$ {. Sh.version}")' 2>/dev/null fonctionne-t-il mieux?
ajouté l'auteur Valters Vingolds, source
@sil L'intérêt d'utiliser un sous-shell est de détecter l'erreur. Rediriger les erreurs vers /dev/null et ignorer le statut de sortie.
ajouté l'auteur Valters Vingolds, source
Pourquoi le second 2>/dev/null ?
ajouté l'auteur Stéphane Chazelas, source
Il vaut mieux utiliser la commande eval '_sh_version = $ {. Sh.version}' 2>/dev/null ou {_sh_version = ...; } 2>/dev/null
ajouté l'auteur Stéphane Chazelas, source
Malheureusement, sh sh NetBSD (bien que pas dash-0.5.7-3 sur linux) arrêtera carrément de lire le script quand il verra $ {. Sh.version} avec la réclamation Erreur de syntaxe: substitution incorrecte .
ajouté l'auteur armadillotoe, source
eval à nouveau à la rescousse! La redirection doit cependant être déplacée dans le développement de la commande: _sh_version = $ (eval 'echo "$ {. Sh.version}"' 2>/dev/null) Merci beaucoup!
ajouté l'auteur armadillotoe, source
Pour info, ma version eval fonctionne même avec ksh88e (que je viens de créer et d’exécuter sur NetBSD/amd64, bien qu’avec quelques piratages grossiers et corrections mineures). C'est à dire. cela "fonctionne" en ce sens qu'il laisse _sh_version vide et n'écrit rien à l'erreur standard; car comme vous le savez, cette version et les précédentes ne disposaient d'aucun autre moyen qu'une commande de l'éditeur pour montrer leur identification de version.
ajouté l'auteur armadillotoe, source

KSH_VERSION was not implemented in ksh93 before version 93t. It will be set in mksh, pdksh, lksh. So for checking the version of ksh, we can try these steps:

  • Checking KSH_VERSION to detect mksh, pdksh, lksh
  • If first step fails, try a feature that's different between ksh93 and ksh88/86 (Let David Korn show us).

Dans cet esprit, je vais aller avec:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac
6
ajouté
Pourquoi ne vérifie-t-il pas si $ KSH_VERSION n'est pas vide en premier? Sur ma machine Ubuntu, ceci affiche "ksh93", mais KSH_VERSION est défini.
ajouté l'auteur Jimmie R. Houts, source
Si la variable ENV est définie (et qu'elle est généralement définie sur ~/.kshrc ), le script lira définitivement le fichier .kshrc . Bien sûr, il serait assez étrange pour un script de définir une fausse KSH_VERSION, mais cela est néanmoins possible, tout comme l’exécution explicite d’un script avec un interpréteur différent de celui spécifié dans sa première ligne est une situation possible.
ajouté l'auteur Eric Scrivner, source
Cela échouerait si un code précédemment exécuté (par exemple: .kshrc) altérait la variable KSH_VERSION avec une valeur aléatoire.
ajouté l'auteur Eric Scrivner, source
pdksh et les shells dérivés sont bien protégés en ayant la variable KSH_VERSION en lecture seule, mais pas celle de AT & T ksh . Les versions plus anciennes que ksh93t (2008) ne le définissent pas et l'acceptent comme un nom de variable sans restriction. Tout script basé sur KSH_VERSION peut facilement être trompé si cette variable a déjà été exportée ou est définie dans un fichier .kshrc . ksh93t et les versions plus récentes ne se différencient toujours pas si cette variable est définie par un script d'initialisation. ksh93t à ksh93u uniquement si la variable est définie par défaut, elle est déjà définie dans l'environnement de départ. Segfaulting était un bogue corrigé dans ksh93v .
ajouté l'auteur Eric Scrivner, source
@jlliagre: Non, car il a été exécuté en tant que script, il ne lit pas .kshrc .
ajouté l'auteur cuonglm, source
@jlliagre: Même si vous pouvez le changer, vous obtiendrez un segfault lorsque vous vous référerez à KSH_VERSION . Et dans mksh , pdksh , lksh , KSH_VERSION est marqué en lecture seule.
ajouté l'auteur cuonglm, source

For "real" ksh releases (i.e. AT&T based), I use this command:

strings /bin/ksh | grep Version | tail -2 

Voici différentes sorties que je reçois:

Ksh d'origine:

@(#)Version M-11/16/88i

Dtksh;

@(#)Version 12/28/93
Version not defined

Ksh93 moderne:

@(#)$Id: Version AJM 93u+ 2012-08-01 $

For pdksh/msh ksh clones and modern AT&T ksh versions too, here is something that works:

$ mksh -c 'echo $KSH_VERSION'
@(#)MIRBSD KSH R50 2015/04/19

Modifier:

J'ignorais que vous demandiez de le faire depuis un script, pas en connaissant le chemin d'accès au binaire ksh testé.

En supposant que vous souhaitiez réellement utiliser la version de ksh , et non les fonctionnalités qu’elle prend en charge, voici une façon de le faire en utilisant uniquement la commande strings qui devrait au moins fonctionner avec Linux et Solaris:

echo $(for i in $(find /proc/$$ ! -type d ! -name "pagemap" | 
  grep -v "/path/" | grep -v "/fd/" ) ; do
  strings $i | egrep "([V]ersion|[K]SH_VERSION).*[0-9]" | sort -u
done 2>/dev/null)

Notez que cette méthode n’est pas fiable car /proc peut ne pas être monté et il existe certainement d’autres faiblesses. Il n'a pas été testé sur d'autres systèmes d'exploitation Unix.

5
ajouté
@ Gilles Oh. Je pensais .kshrc. Mais je vois ce que tu veux dire.
ajouté l'auteur Jimmie R. Houts, source
@Gilles Que faire si cela a été effectué sur la valeur renvoyée par la commande -v $ 0 ?
ajouté l'auteur Jimmie R. Houts, source
@cuonglm Désolé si je n'étais pas clair. Quand j’écrivais «pour de« véritables » ksh versions», j’excluais explicitement les clones ksh non AT & T tels que pdksh , mksh et lksh .
ajouté l'auteur Eric Scrivner, source
@cuonglm J'ai mis à jour ma réponse avec une méthode qui fonctionne avec la commande strings avec lksh et pdksh .
ajouté l'auteur Eric Scrivner, source
@cuonglm Accordé, en effet. On ne peut pas utiliser de manière fiable la ligne d'interprétation, réponse mise à jour.
ajouté l'auteur Eric Scrivner, source
@cuonglm Je n'ai pas de Jessie à tester. Voulez-vous dire que lksh et pdksh ne peuvent pas être triés de leur KSH_VERSION ?
ajouté l'auteur Eric Scrivner, source
Exécuter strings sur un binaire ksh est une mauvaise idée car vous ne savez pas s'il s'agit de celui qui exécute votre script. Peut-être que votre script est exécuté par /usr/local/bin/ksh ou /home/bob/bin/ksh ou /bin/sh ou /usr/posix/bin/sh ou…
ajouté l'auteur Valters Vingolds, source
@Sildoreth En quoi $ 0 est-il utile ici? Il désigne le script, pas à l'interprète.
ajouté l'auteur Valters Vingolds, source
Je n'ai pas vu les chaînes utilisés depuis un moment. C'est un bon programme. Merci de me l'avoir rappelé!
ajouté l'auteur SailorCire, source
Cela ne fera pas la distinction entre lksh et pdksh dans Debian Jessie.
ajouté l'auteur cuonglm, source
@jlliagre: si un script a été appelé avec script ksh ou script lksh ou <script> script pdksh , votre solution a échoué.
ajouté l'auteur cuonglm, source
Non, je veux dire courir chaînes sur eux. KSH_VERSION le peut définitivement.
ajouté l'auteur cuonglm, source

Pendant que j'écrivais un script pour ksh , j'ai remarqué que l'option -a de la commande intégrée de ksh, d'où la commande ne semble pas être prise en charge dans anciennes versions de ksh . Et cela semble être vrai sur tous les systèmes que j'ai vérifiés, notamment Solaris, AIX, HP-UX et Linux.

Voici donc la solution en tant que fonction ksh:

is_modern_ksh() {
  if whence -a whence > /dev/null 2>&1 ; then
    return 0 #success -> true
  fi
  #Else the call to `whence` failed because `-a` is not supported
  return 1 #failure -> false
}

Et voici comment l'utiliser:

if is_modern_ksh ; then
  echo "You're using a MODERN version of ksh. :)"
else
  echo "You're using an OLD version of ksh. :("
fi
2
ajouté
@MichaelBrux Le point essentiel est qu'une erreur soit générée pour les versions antérieures, que la redirection d'erreur utilise ensuite. C'est ainsi que cette fonction identifie la version ksh comme "ancienne". Voulez-vous dire que la redirection d'erreur ne fonctionne pas?
ajouté l'auteur Jimmie R. Houts, source
@Greg Ou voulez-vous dire que zsh appelle ksh pour certaines de ses fonctionnalités? Si c'est le cas, je doute que vous ayez même besoin de cette fonction. Ma raison principale pour avoir besoin d'une telle fonction était le besoin de savoir quand remédier à un manque de fonctionnalités spécifiques dans les anciennes versions de ksh. Existe-t-il une lacune spécifique des anciennes versions de ksh qui saigne dans zsh pour vous? Si tel est le cas, je serais surpris puisque le principe fondateur de zsh est qu’il combine le meilleur de tous les autres obus tout en éliminant toutes les lacunes.
ajouté l'auteur Jimmie R. Houts, source
@Greg voulez-vous dire que "zsh" est défini comme un lien symbolique pour exécuter ksh? Si c'est le cas, vous n'utilisez pas zsh du tout. Et si cette version de ksh supporte -a , cela signifie nécessairement que c'est une version moderne de ksh . et la fonction is_modern_ksh ne fait que son travail.
ajouté l'auteur Jimmie R. Houts, source
@ GregA.Woods, cette fonction est spécifiquement pour ksh. La définition de la fonction irait en .kshrc et n’existerait donc pas pour d’autres shells tels que zsh. zsh possède sa propre commande d'origine intégrée, qui n'est aucunement liée à ksh ou à sa version. Je ne sais même pas pourquoi vous voudriez vérifier si ksh est une ancienne version d'une instance de zsh, qui est un shell complètement différent.
ajouté l'auteur Jimmie R. Houts, source
@cuonglm parce que je ne peux pas. Voir les commentaires sur la réponse de Gilles .
ajouté l'auteur Jimmie R. Houts, source
Ne fonctionne pas avec mksh: mksh: whence: -a: option inconnue . KSH_VERSION est: @ (#) MIRBSD KSH R54 2016/11/11 (sous Debian 9/stretch)
ajouté l'auteur drxzcl, source
@Sildoreth: "mksh est un successeur de pdksh" - cela signifie, à ma connaissance, qu'il est moderne. Votre code retourne "OLD version", ce qui est une erreur, de mon point de vue. La raison de cette erreur est la suivante: mksha pourquoi l’intégration n’a pas d’option -a. Est-ce plus clair/compréhensible/correct?
ajouté l'auteur drxzcl, source
Non, Zsh prétend être Ksh, bien que dans certaines situations, il l'ait plutôt mal fait.
ajouté l'auteur armadillotoe, source
Il y a un problème avec vos hypothèses: Zsh est souvent installé avec un lien vers /bin/ksh , par exemple. sur Debian Linux. Maintenant, je ne l'utilise pas là-bas (et pour le moment je ne peux pas changer mon shell de connexion pour vérifier), donc je ne sais pas s'il lit .kshrc ou pas, mais je soupçonnerais Cela fait.
ajouté l'auteur armadillotoe, source
Malheureusement, le whence dans Zsh a -a
ajouté l'auteur armadillotoe, source
Pourquoi n'utilisez-vous pas $ {. Sh.version} ?
ajouté l'auteur cuonglm, source

CTRL+ALT+V

ou

ESC , CTRL+V

Ils se sont généralement révélés très fiables pour déterminer de manière interactive la version de KSH que vous utilisez, mais leur script s'est avéré plus difficile.

1
ajouté
c'était le seul qui fonctionnait pour une version AIX ksh 88f.
ajouté l'auteur javanix, source
J'ai obtenu l'option ESC , CTRL + V , après avoir exécuté set -o vi pour définir la les raccourcis clavier à ressembler à vi. Avant cela, ou avec + o vi ou -o emacs, cela ne me montrait tout simplement pas. PD KSH v5.2.14 99/07/13.2 sur openbsd 6.1
ajouté l'auteur Meitar, source

Ce qui suit semble fonctionner assez bien pour tous les shells que j'ai testés, y compris l'ancien ksh88e et une gamme presque complète de clones Ksh courants (bien qu'une seule version de chacun), bien que je n'ai pas encore testé le véritable shell Bourne original ( Pour cela, vous devrez peut-être adapter l’expression test pour les versions antérieures ....

Addenda:

Je l’ai également testé avec succès avec Heirloom Bourne Shell, bien qu’avec un programme externe test .

is_attksh()
{
    # ksh93
    _sh_version=$(eval 'echo "${.sh.version}"' 2>/dev/null)
    # pdksh only
    _opt_login=$(set -o | grep login)

    test -n "${_sh_version}" -o \( -z "${_opt_login}" -a -n "${_}" -a -n "${ERRNO}" -a -n "${FCEDIT}" -a -n "${PS3}" \)
}
is_attksh && echo "AT&T Ksh${_sh_version:+: }${_sh_version:- (probably ksh88 or ksh86)}" || echo "not real ksh"

is_zsh()
{
    test -n "${ZSH_VERSION}"
}
is_zsh && echo "Zsh: ${ZSH_VERSION}" || echo "not zsh"
0
ajouté
Cela nous éclaire sur votre origine. Cependant, cela semble être une exigence irréaliste. Généralement, lorsqu'un développeur Unix dit "portable", il ne veut pas dire "ce code fonctionnera dans n'importe quel shell ", mais "cela fonctionnera sur n'importe quel système ". . Et si vous avez besoin d'exécuter un script écrit pour un autre shell, c'est parfaitement légal. démarrez simplement une instance non interactive de l'autre shell dans votre script. J'en parle parce que je veux promouvoir de bonnes pratiques de codage. Si cette solution fonctionne pour vous, tant mieux. Mais je conseillerais aux autres d’adopter une approche plus simple.
ajouté l'auteur Jimmie R. Houts, source
Pourquoi voudriez-vous exécuter cette fonction pour les shells qui ne sont pas ksh? Si vous exécutez un script en bash ou en zsh, alors ksh n'entre jamais en jeu. En outre, il a déjà été établi, d’après les réponses d’autres réponses, que $ {. Sh.version} ne peut pas faire partie de la solution car certaines versions de ksh - les versions qui intéressaient la publication originale - erreur irrécupérable cette syntaxe.
ajouté l'auteur Jimmie R. Houts, source
Certes, tout effort visant à faire glisser la compatibilité en amont trop loin dans le passé est plutôt stupide. Je n'ai compilé que des versions des anciens AT & T Ksh et Unix Sh pour satisfaire mon désir personnel de mieux comprendre l'histoire et l'évolution de certaines fonctionnalités et de me rafraîchir la mémoire de la façon dont les choses se passaient (ce qui me surprend généralement, car les choses étaient souvent beaucoup plus importantes ». mieux "que je me souviens, même si parfois ils étaient aussi bien pires).
ajouté l'auteur armadillotoe, source
J'écris depuis 28 ans des scripts shell portables destinés à fonctionner avec tous les compatibles /bin/sh Bourne (et plus tard POSIX). :-)
ajouté l'auteur armadillotoe, source
Les scripts que j'écris sont destinés à être portables et à être exécutés par tout shell capable. En outre, comme je l'ai dit ailleurs, certaines personnes ne sauront pas nécessairement qu'elles utilisent Zsh en tant que Ksh, car lorsqu'elles tapent 'ksh', le fichier binaire Zsh sera invoqué (avec argv [0] sous la forme "ksh").
ajouté l'auteur armadillotoe, source
Comme je le disais, la fonction que je montre a été testée avec des versions de ksh qui donnent des erreurs "fatales", ainsi que des versions de Ash qui font la même chose.
ajouté l'auteur armadillotoe, source

Je pense que le problème fondamental de l’utilisation de $ {. Sh.version} est que ksh88 s’arrête juste, avec un code de sortie non nul.

Donc, ma solution est de mettre le code qui référence $ {. Sh.version} dans un sous-shell, puis de vérifier si le sous-shell quitte différent de zéro et a du code dans le sous-shell qui fonctionnera sur les versions de le ksh où référencer $ {. sh.version} fonctionne. En le enveloppant dans une fonction qui est ensuite appelée par une autre fonction qui inverse le code de retour, de sorte que le dernier appel recherche la valeur true.

function is_oldksh
{
    (test -n ${.sh.version}) 2>/dev/null
}

function oldkshtest
{

    is_oldksh || return 0 && return 1
}

oldkshtest && echo "old ksh" || echo "new ksh"

I have run this on AIX and Oracle Enterprise Linux 5 & 6, with ksh88, ksh93 and pdksh.

Pete

0
ajouté
AT & T Ksh moderne fournit toujours .sh.version (en effet, KSH_VERSION est en réalité un alias pour cela). Aussi, quelques coquilles, par exemple NetBSD sh, arrêtez simplement de lire après avoir rencontré $ {. Sh.version} et aucune quantité de redirection ne peut les empêcher d’exécuter le script.
ajouté l'auteur armadillotoe, source