¿Cómo puedo obtener la versión de ksh de forma segura?

12

¿Cómo puedo obtener la versión de ksh de forma segura desde un script ksh?

He visto las siguientes soluciones :

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

Y dadas las circunstancias correctas, cada uno de estos funciona correctamente. Sin embargo, me importa el caso no perfecto.

Específicamente, hay varias máquinas con las que trabajo que tienen versiones anteriores de ksh que, para mis propósitos, carecen de funcionalidad. De todos modos, la razón por la que quiero verificar la versión (programáticamente) es para ver si la versión ksh es una de las versiones menos capaces; y si es así, quiero ejecutar una rama con un código menos sorprendente.

Sin embargo, en las máquinas problemáticas, la ineptitud del shell se extiende hasta verificar la versión ...

  • Si lo intento ksh --version, no imprime nada y abre una nueva instancia de ksh!
  • Si lo intento echo ${.sh.version}, kshtrata esto como un error de sintaxis con el que no se puede descartar 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Por supuesto, echo $KSH_VERSIONparece funcionar bien, quiero decir que no se bloqueará, aunque en estas máquinas está en blanco. Además, vi en algún lugar que KSH_VERSIONse establece solo por pdksh.

Preguntas:

  • ¿Cómo puedo verificar la versión de forma segura mediante kshprogramación? Para mis propósitos aquí, realmente no me importa cuál es el número de versión real, solo si es una versión desactualizada ksh.
  • Es $KSH_VERSIONlo suficientemente bueno? Quiero decir, si está en blanco, ¿es kshnecesariamente una versión desactualizada? ¿Era correcto ese otro foro en el que podría no estar configurado incluso para versiones más recientes ksh?
  • ¿No hay forma de verificar esto?
Sildoreth
fuente
1
¿Alguna razón por la que quieres dos rutas de código y no solo una con un código menos sorprendente?
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen tiene que ver con el aviso. En mi archivo .kshrc, tengo una función que simula la funcionalidad abreviada pwd de los avisos tcsh y zsh, y configuré PS1para usar esta función. Sin embargo, Old ksh no es compatible $()con PS1. Entonces, si es una versión moderna de ksh, quiero PS1usar la función que creé; si es la versión anterior, yo solo uso $PWD.
Sildoreth
Bueno, ¿ podría tener dos versiones de su archivo de configuración (quizás una generada a partir de la otra) y luego distribuir la versión adecuada a la máquina en cuestión?
Thorbjørn Ravn Andersen
Otro enfoque podría ser simplemente decir "Es solo esta máquina en particular la que tiene el problema: encontraré una variable de archivo o entorno u otra cosa que solo exista aquí (probablemente AIX o algo así) y probaré eso en su lugar".
Thorbjørn Ravn Andersen

Respuestas:

7

Creo que .sh.versionha existido desde la primera versión de ATT ksh 93. No está disponible en pdksh o mksh. Como se ${.sh.version}trata de un error de sintaxis en shells distintos de ksh93, envuelva la prueba en un subshell y protéjalo detrás 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 comenzó en el dominio público ksh clone (pdksh), y se agregó al shell Korn real hace relativamente poco, en 2008 con ksh93t.

En lugar de probar un número de versión, debe probar la función específica que le causa dolor. La mayoría de las características se pueden probar probando alguna construcción en una subshell y ver si desencadena un error.

Gilles 'SO- deja de ser malvado'
fuente
No veo ninguna diferencia cuando uso una subshell. Todavía se trata ${.sh.version}como un error de sintaxis que no se puede conciliar. El mensaje que recibo es bad substitution.
Sildoreth
@sil El objetivo de utilizar una subshell es detectar el error. Redirigir errores /dev/nulle ignorar el estado de salida.
Gilles 'SO- deja de ser malvado'
Entiendo lo que dices. Lo que digo es que el error no redirige. Es siempre imprime en la consola. Intenté esto en Solaris, AIX y HP-UX; y ksh exhibe este comportamiento en todos ellos.
Sildoreth,
@Sildoreth Ah. Solo había probado en Linux, y no tengo ninguno de estos sistemas operativos para probar ahora. ¿ eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullFunciona mejor?
Gilles 'SO- deja de ser malvado'
Eso está un poco mejor. Funciona de manera perfecta en Solaris y HP-UX. Para AIX, funciona en la línea de comando pero curiosamente comienza a fallar nuevamente si trato de colocarlo en una función de shell.
Sildoreth
6

KSH_VERSIONno se implementó ksh93antes de la versión 93t. Se encuentra en mksh, pdksh, lksh. Entonces, para verificar la versión de ksh, podemos probar estos pasos:

  • Comprobación KSH_VERSIONde detectar mksh, pdksh,lksh
  • Si el primer paso falla, pruebe una función que sea diferente entre ksh93y ksh88/86( deje que David Korn nos lo muestre ).

Con esto en mente, iré con:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac
Cuonglm
fuente
¿Por qué no verifica esto si $KSH_VERSIONno está en blanco primero? En mi máquina Ubuntu, esto imprime "ksh93", pero KSH_VERSIONestá configurado.
Sildoreth
Esto fallaría si algún código ejecutado previamente (por ejemplo: .kshrc) manipulara la variable KSH_VERSION con algún valor aleatorio.
jlliagre
@jlliagre: No, como se ejecutó como un script, no se lee .kshrc.
Cuonglm
Si la ENVvariable está configurada (y normalmente está configurada en ~/.kshrc), el script definitivamente leerá el .kshrcarchivo. Por supuesto, sería bastante extraño que un script establezca una KSH_VERSION falsa, pero sin embargo esto es posible, al igual que ejecutar explícitamente un script con un intérprete diferente al especificado en su primera línea es una situación posible.
jlliagre
@jlliagre: Incluso si puede cambiarlo, obtendrá segfault cuando haga referencia KSH_VERSION. Y en mksh, pdksh, lksh, KSH_VERSIONse marca como de sólo lectura.
Cuonglm
5

Para las kshversiones "reales" (es decir, basadas en AT&T), utilizo este comando:

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

Aquí hay varios resultados que obtengo:

Ksh original:

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

dtksh;

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

Ksh93 moderno:

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

Para pdksh/ msh kshclones y kshversiones modernas de AT&T también, aquí hay algo que funciona:

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

Editar:

Pasé por alto que estabas preguntando acerca de hacerlo desde dentro de un script, no sabiendo la ruta al binario ksh probado.

Suponiendo que realmente desea la versión de kshutilizado, y no las características que admite, aquí hay una forma de hacerlo utilizando solo el stringscomando que debería funcionar al menos en Linux y 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)

Tenga en cuenta que este método no es confiable ya que /procpodría no estar montado, y ciertamente hay otras debilidades. No se ha probado en otros sistemas operativos Unix.

jlliagre
fuente
Esto no se distinguirá entre lkshy pdkshen Debian Jessie.
Cuonglm
@cuonglm No tengo a Jessie para probar. ¿Quieres decir lkshy pdkshno se puede separar de ellos KSH_VERSION?
jlliagre
No, me refiero a correr stringssobre ellos. KSH_VERSIONdefinitivamente puede.
Cuonglm
@cuonglm Lo siento si no estaba claro. Cuando escribí «para kshlanzamientos " reales " , estaba excluyendo explícitamente los clones ksh que no son de AT&T como pdksh, mkshy lksh.
jlliagre
Ejecutar stringsen algún binario de ksh es una mala idea porque no sabes si ese es el que ejecuta tu script. Tal vez tu guión esté siendo ejecutado por /usr/local/bin/ksho /home/bob/bin/ksho /bin/sho /usr/posix/bin/sho ...
Gilles 'SO- deja de ser malvado'
2

Mientras escribía un script para ksh, noté que la -aopción del whencecomando incorporado de ksh parece no ser compatible con versiones anteriores de ksh. Y esto parece ser cierto en todos los sistemas que verifiqué, incluidos Solaris, AIX, HP-UX y Linux.

Así que aquí está la solución como una función 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
}

Y aquí está cómo usarlo:

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
fuente
¿Por qué no lo usas ${.sh.version}?
Cuonglm
@cuonglm porque no puedo. Ver los comentarios sobre la respuesta de Gilles .
Sildoreth
desafortunadamente el whenceen Zsh tiene-a
Greg A. Woods
@ GregA.Woods, esta función es específicamente para ksh. La definición de la función iría en .kshrc y, por lo tanto, ni siquiera existiría para otros shells como zsh. zsh tiene su propio whencecomando incorporado que de ninguna manera está vinculado a ksh o la versión del mismo. Ni siquiera sé por qué querrías comprobar si ksh es una versión antigua desde una instancia de zsh, que es un shell completamente diferente.
Sildoreth
Hay un problema con sus suposiciones: Zsh a menudo se instala con un enlace a /bin/ksh, por ejemplo, en Debian Linux. Ahora no lo uso allí (y por el momento no puedo cambiar mi shell de inicio de sesión para verificar), así que no sé si se lee .kshrco no, pero sospecharía que sí.
Greg A. Woods, el
1

CTRL+ ALT+V

o

ESC, CTRL+V

Por lo general, han demostrado ser muy confiables en lo que respecta a la determinación interactiva de la versión de KSH que está utilizando, sin embargo, crear scripts es más difícil.

Tim Kennedy
fuente
1
este fue el único que funcionó para una versión AIX ksh 88f.
Jeff Schaller
1
set -o viObtuve la opción <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd> para trabajar, después de ejecutar para configurar las combinaciones de teclas para que sean como vi. Antes de eso, o con + o vi o -o emacs, simplemente no me mostraría. PD KSH v5.2.14 99/07 / 13.2 en openbsd 6.1
bgStack15
0

Creo que el problema fundamental con el uso de $ {. Sh.version} es que ksh88 simplemente se detiene, con un código de salida distinto de cero.

Por lo tanto, mi solución es poner el código que hace referencia a $ {. Sh.version} en un sub-shell, luego probar para ver si el sub-shell sale de cero y tiene un código en el sub-shell que funcionará en versiones de el ksh donde hacer referencia a $ {. sh.version} funciona. Envolviéndolo en una función que luego es llamada por otra función que invierte el código de retorno, de modo que la última llamada verifica que sea verdadera.

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"

He ejecutado esto en AIX y Oracle Enterprise Linux 5 y 6, con ksh88, ksh93 y pdksh.

Pete

Pete
fuente
1
AT&T Ksh moderno todavía suministra .sh.version(de hecho KSH_VERSIONes un alias para ello). También algunos shells, por ejemplo, NetBSD sh, simplemente dejan de leer después de encontrarse ${.sh.version}y ninguna cantidad de redirección puede mantenerlos ejecutando el script.
Greg A. Woods, el
0

Lo siguiente parece funcionar razonablemente bien para todos los shells que he probado, incluido el viejo ksh88e y una gama casi completa de clones Ksh comunes (aunque solo una versión de cada uno), aunque todavía no he probado un shell Bourne original real ( y hacerlo puede requerir adaptar la testexpresión para versiones anteriores ...

Apéndice:

Ahora también he probado con éxito esto con Heirloom Bourne Shell, aunque con un programa externo (y más moderno) 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
fuente
¿Por qué querrías ejecutar esta función para shells que no son ksh? Si está ejecutando un script en bash o zsh, entonces ksh nunca entra en juego. Además, ya se ha establecido a través de las respuestas de otros que ${.sh.version}no pueden ser parte de la solución porque ciertas versiones de ksh, las versiones que preocupaban a la publicación original, tienen un error fatal en esa sintaxis.
Sildoreth
Como dije, la función que muestro ha sido probada con versiones de ksh que dan errores "fatales", así como con versiones de Ash que hacen lo mismo.
Greg A. Woods, el
Los scripts que escribo están destinados a ser portátiles y a ser ejecutados por cualquier shell capaz. Además, como dije en otra parte, algunas personas no necesariamente sabrán que están usando Zsh como Ksh porque cuando escriben 'ksh' se invocará el binario de Zsh (con argv [0] como "ksh").
Greg A. Woods, el
Eso arroja luz sobre de dónde vienes. Sin embargo, eso suena como un requisito poco realista. Por lo general, cuando un desarrollador de Unix dice "portable", no quiere decir "este código se ejecutará en ningún shell ", sino "esto se ejecutará en cualquier sistema ". Y si necesita ejecutar un script que fue escrito para otro shell, eso es perfectamente legal; simplemente inicie una instancia no interactiva del otro shell en su secuencia de comandos. Menciono esto porque quiero promover buenas prácticas de codificación. Si esta solución te funciona, genial. Pero recomendaría a otros que adopten un enfoque más simple.
Sildoreth
1
Es cierto que cualquier esfuerzo por arrastrar la compatibilidad hacia atrás demasiado lejos en el pasado es bastante tonto. Solo he compilado versiones de los antiguos AT&T Ksh y Unix Sh para satisfacer mi deseo personal de comprender mejor la historia y la evolución de algunas características y para refrescar mi memoria de cómo eran las cosas (lo que generalmente me sorprende, ya que las cosas a menudo eran mucho " mejor "de lo que recuerdo, aunque en algún momento también fueron mucho peores).
Greg A. Woods, el