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

12

Comment puis-je obtenir en toute sécurité la version de ksh à partir d'un script ksh?

J'ai vu les solutions suivantes :

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

Et étant donné les bonnes circonstances, chacun de ces travaux fonctionne correctement. Cependant, je me soucie du cas non parfait.

Plus précisément, il y a plusieurs machines avec lesquelles je travaille qui ont des versions plus anciennes de ksh qui, pour mes besoins, manquent gravement 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 ksh est l'une des versions les moins capables; et si oui, je veux exécuter une branche avec du code moins impressionnant.

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

  • Si j'essaye ksh --version, il n'imprime rien et ouvre une nouvelle instance de ksh!
  • Si j'essaie echo ${.sh.version}, kshtraite cela comme une erreur de syntaxe qui ne peut pas être éliminée 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Bien sûr, cela echo $KSH_VERSIONsemble fonctionner correctement - je veux dire qu'il ne se bloquera pas - bien que sur ces machines, il soit vide. De plus, j'ai vu quelque part qui KSH_VERSIONn'est défini que par pdksh.

Des questions:

  • Comment puis-je vérifier en toute sécurité la version de par kshprogramme? Pour mes besoins ici, je ne me soucie pas vraiment du numéro de version réel, juste s'il s'agit d'une version obsolète de ksh.
  • Est-ce $KSH_VERSIONassez bon? Je veux dire si elle est vide, alors est kshnécessairement une version obsolète? Est-ce que cet autre forum avait raison de ne pas être défini même pour les nouvelles versions de ksh?
  • N'y a-t-il simplement aucun moyen de vérifier cela?
Sildoreth
la source
1
Pour une raison quelconque, vous voulez deux chemins de code et pas seulement un avec du code moins génial?
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen cela a à voir avec l'invite. Dans mon fichier .kshrc, j'ai une fonction qui simule la fonctionnalité d'abréviation pwd des invites tcsh et zsh, et j'ai configuré PS1pour utiliser cette fonction. Cependant, Old ksh ne prend pas en charge $()dans PS1. Donc, si c'est une version moderne de ksh, je veux PS1utiliser la fonction que j'ai créée; si c'est l'ancienne version, j'utilise juste $PWD.
Sildoreth
Eh bien, vous pourriez avoir deux versions de votre fichier de configuration (peut-être l'une générée à partir de l'autre), puis distribuer la version appropriée à la machine en question?
Thorbjørn Ravn Andersen
Une autre approche pourrait être de simplement dire "Ce n'est que cette machine particulière qui a le problème - je vais trouver un fichier ou une variable d'environnement ou quelque chose d'autre qui n'existe que ici (probablement AIX ou quelque chose de toute façon) et tester à la place".
Thorbjørn Ravn Andersen

Réponses:

7

Je pense que cela .sh.versionexiste depuis la première version d'ATT ksh 93. Il n'est pas disponible en pdksh ou mksh. Puisqu'il ${.sh.version}s'agit d'une erreur de syntaxe dans des shells autres que ksh93, encapsulez 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 a commencé dans le clone ksh du domaine public (pdksh) et a été ajouté au shell Korn actuel relativement récemment, en 2008 avec ksh93t.

Plutôt que de tester un numéro de version, vous devriez tester la fonctionnalité spécifique qui vous cause du chagrin. 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.

Gilles 'SO- arrête d'être méchant'
la 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 rapprochée. Le message que je reçois est bad substitution.
Sildoreth
@sil Le but d'utiliser un sous-shell est d'attraper l'erreur. Redirigez les erreurs /dev/nullet ignorez l'état de sortie.
Gilles 'SO- arrête d'être méchant'
Je comprends ce que vous dites. Ce que je dis, c'est que l'erreur ne redirige pas. Il imprime toujours sur la console. J'ai essayé ceci dans Solaris, AIX et HP-UX; et ksh présente ce comportement dans chacun d'eux.
Sildoreth
@Sildoreth Ah. Je n'avais testé que sur Linux, et je n'ai aucun de ces OS à tester maintenant. Ça eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullmarche mieux?
Gilles 'SO- arrête d'être méchant'
C'est un peu mieux. Il fonctionne parfaitement sous Solaris et HP-UX. Pour AIX, cela fonctionne sur la ligne de commande mais, curieusement, recommence à échouer si j'essaie de le placer dans une fonction shell.
Sildoreth
6

KSH_VERSIONn'a pas été implémenté ksh93avant la version 93t. Il sera mis en mksh, pdksh, lksh. Donc, pour vérifier la version de ksh, nous pouvons essayer ces étapes:

  • Vérification KSH_VERSIONde détecter mksh, pdksh,lksh
  • Si la première étape échoue, essayez une fonctionnalité différente entre ksh93et ksh88/86( laissez David Korn nous le montrer ).

En gardant cela à l'esprit, j'irai avec:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac
cuonglm
la source
Pourquoi cette vérification $KSH_VERSIONn'est-elle pas non vide en premier? Sur ma machine Ubuntu, cela imprime "ksh93", mais KSH_VERSIONest encore défini.
Sildoreth
Cela échouerait si du code précédemment exécuté (par exemple: .kshrc) altérait la variable KSH_VERSION avec une valeur aléatoire.
jlliagre
@jlliagre: Non, car il a été exécuté comme un script, il ne se lit pas .kshrc.
cuonglm
Si la ENVvariable est définie (et elle est généralement définie sur ~/.kshrc), le script lira définitivement le .kshrcfichier. Bien sûr, il serait assez étrange qu'un script définisse un faux 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.
jlliagre
@jlliagre: Même si vous pouvez le changer, vous obtiendrez une erreur de segmentation lorsque vous y faites référence KSH_VERSION. Et mksh, pdksh, lksh, KSH_VERSIONest marqué en lecture seule.
cuonglm
5

Pour les kshversions "réelles" (c'est-à-dire basées sur AT&T), j'utilise cette commande:

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

Voici différentes sorties que j'obtiens:

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 $

Pour les pdksh/ msh kshclones et les kshversions AT&T modernes aussi, voici quelque chose qui fonctionne:

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

Éditer:

J'ai oublié que vous demandiez de le faire depuis l'intérieur d'un script, pas en connaissant le chemin vers le binaire ksh testé.

En supposant que vous vouliez vraiment la version kshutilisée, et non les fonctionnalités qu'il prend en charge, voici une façon de le faire en utilisant uniquement la stringscommande qui devrait fonctionner au moins sur 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 elle /procpourrait ne pas être montée, et il y a certainement d'autres faiblesses. Il n'est pas testé sur les autres OS Unix.

jlliagre
la source
Cela ne fera pas de distinction entre lkshet pdkshdans Debian Jessie.
cuonglm
@cuonglm Je n'ai pas de Jessie à tester. Voulez-vous dire lkshet pdkshne peut pas être trié de leur KSH_VERSION?
jlliagre
Non, je veux dire courir stringsdessus. KSH_VERSIONpeut définitivement.
cuonglm
@cuonglm Désolé si je n'ai pas été clair. Quand j'ai écrit «pour de« vraies » kshsorties» », j'excluais explicitement les clones ksh non AT&T comme pdksh, mkshet lksh.
jlliagre
Exécuter stringssur un binaire ksh est une mauvaise idée car vous ne savez pas si c'est celui qui exécute votre script. Peut-être que votre script est exécuté par /usr/local/bin/kshou ou /home/bob/bin/kshou /bin/shou /usr/posix/bin/shou ...
Gilles 'SO- arrête d'être méchant'
2

Pendant que j'écrivais un script pour ksh, j'ai remarqué que l' -aoption de la whencecommande intégrée de ksh ne semble pas être prise en charge dans les anciennes versions de ksh. Et cela semble être vrai sur tous les systèmes que j'ai vérifiés, y compris 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
Sildoreth
la source
Pourquoi n'utilisez-vous pas ${.sh.version}?
cuonglm
@cuonglm parce que je ne peux pas. Voir les commentaires sur la réponse de Gilles .
Sildoreth
malheureusement, le whencedans Zsh a-a
Greg A. Woods
@ GregA.Woods, cette fonction est spécifiquement pour ksh. La définition de la fonction irait en .kshrc et n'existerait donc même pas pour d'autres shells tels que zsh. zsh a sa propre whencecommande intégrée qui n'est en aucun cas liée à ksh ou à sa version. Je ne sais même pas pourquoi vous voudriez vérifier si ksh est une ancienne version à partir d'une instance de zsh, qui est un shell complètement différent.
Sildoreth
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 .kshrcou non, mais je soupçonne que c'est le cas.
Greg A. Woods
1

CTRL+ ALT+V

ou

ESC, CTRL+V

Ils se sont généralement révélés très fiables en ce qui concerne la détermination interactive de la version de KSH que vous utilisez, mais leur création s'est avérée plus difficile.

Tim Kennedy
la source
1
c'était le seul qui fonctionnait pour une version AIX ksh 88f.
Jeff Schaller
1
J'ai fait fonctionner l'option <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd>, après avoir couru set -o vipour définir les raccourcis clavier comme 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
bgStack15
0

Je pense que le problème fondamental avec 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 fait référence à $ {. Sh.version} dans un sous-shell, puis de tester pour voir si le sous-shell sort non nul et d'avoir du code dans le sous-shell qui fonctionnera sur les versions de le ksh où référencer $ {. sh.version} fonctionne. Envelopper dans une fonction qui est ensuite appelée par une autre fonction qui inverse le code retour, de sorte que l'appel final vérifie vrai.

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"

J'ai exécuté ceci sur AIX et Oracle Enterprise Linux 5 & 6, avec ksh88, ksh93 et ​​pdksh.

Pete

Pete
la source
1
AT&T Ksh moderne fournit toujours .sh.version(en fait KSH_VERSIONest effectivement un alias pour cela). De plus, certains shells, par exemple NetBSD sh, arrêtent simplement la lecture après la rencontre ${.sh.version}et aucune redirection ne peut les faire fonctionner le script.
Greg A. Woods
0

Les éléments suivants semblent fonctionner raisonnablement bien pour tous les shells que j'ai testés, y compris les anciens ksh88e et une gamme presque complète de clones Ksh communs (bien qu'une seule version de chacun), bien que je n'aie pas encore testé un vrai Bourne shell original ( et cela peut nécessiter d'adapter l' testexpression pour les anciennes versions ....

Addenda:

J'ai également testé cela avec succès avec Heirloom Bourne Shell, bien qu'avec un programme externe (et plus moderne) 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"
Greg A. Woods
la source
Pourquoi voudriez-vous exécuter cette fonction pour des shells qui ne sont pas ksh? Si vous exécutez un script en bash ou zsh, alors ksh n'entre jamais en jeu. De plus, il a déjà été établi via les réponses des autres qui ${.sh.version}ne peuvent pas faire partie de la solution car certaines versions de ksh - les versions qui préoccupaient le message d'origine - se trompent fatalement sur cette syntaxe.
Sildoreth
Comme je l'ai dit, la fonction que je montre a été testée avec des versions de ksh qui donnent des erreurs "fatales", ainsi que des versions d'Ash qui font de même.
Greg A. Woods
Les scripts que j'écris sont destinés à être portables et à être exécutés par n'importe quel shell capable. De plus, comme je l'ai dit ailleurs, certaines personnes ne sauront pas nécessairement qu'elles utilisent Zsh comme Ksh car quand elles tapent 'ksh' le binaire Zsh sera invoqué (avec argv [0] comme "ksh").
Greg A. Woods
Cela met en lumière d'où vous venez. Cependant, cela ressemble à une exigence irréaliste. En règle générale, lorsqu'un développeur Unix dit "portable", cela ne signifie pas "ce code s'exécutera dans n'importe quel shell ", il signifiera "cela s'exécutera sur n'importe quel système ". Et si vous avez besoin d'exécuter un script qui a été écrit pour un autre shell, c'est parfaitement légal; il suffit de démarrer 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.
Sildoreth
1
Certes, tout effort visant à faire glisser la compatibilité descendante 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 rafraîchir ma mémoire de la façon dont les choses se passaient (ce qui me surprend généralement, car les choses étaient souvent beaucoup " mieux "que je ne m'en souviens, mais parfois ils étaient aussi bien pires).
Greg A. Woods