Come posso ottenere in sicurezza la versione di ksh?


12

Come posso ottenere in sicurezza la versione di ksh da uno script ksh?

Ho visto le seguenti soluzioni :

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

E date le giuste circostanze, ognuna di queste funziona correttamente. Tuttavia, mi interessa il caso non perfetto.

In particolare, ci sono diverse macchine con cui lavoro che hanno versioni precedenti di ksh che, ai miei scopi, sono gravemente carenti di funzionalità. Comunque, il motivo per cui voglio controllare la versione (a livello di codice) è vedere se la versione ksh è una delle versioni meno capaci; e in tal caso, voglio eseguire un ramo con un codice meno impressionante.

Tuttavia, sulle macchine problematiche, l'inettitudine della shell si estende al controllo della versione ...

  • Se provo ksh --version, non stampa nulla e apre una nuova istanza di ksh!
  • Se ci provo echo ${.sh.version}, lo kshtratta come un errore di sintassi che non può essere scartato 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Ovviamente echo $KSH_VERSIONsembra funzionare bene - intendo che non si bloccherà - sebbene su queste macchine sia vuoto. Inoltre, ho visto da qualche parte che KSH_VERSIONè impostato solo da pdksh.

Domande:

  • Come posso verificare in modo sicuro la versione di a livello di kshcodice? Per i miei scopi qui, non mi interessa davvero quale sia il numero di versione attuale, sia che si tratti di una versione obsoleta di ksh.
  • È $KSH_VERSIONabbastanza buono? Voglio dire se è vuoto, quindi è kshnecessariamente una versione obsoleta? Quell'altro forum aveva ragione sul fatto che potrebbe non essere impostato anche per le versioni più recenti di ksh?
  • Non c'è proprio modo di verificarlo?

1
Qualche motivo per cui vuoi due percorsi di codice e non solo uno con meno codice fantastico?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen ha a che fare con il prompt. Nel mio file .kshrc, ho una funzione che simula la funzionalità di abbreviazione pwd dei prompt tcsh e zsh e ho impostato PS1per utilizzare questa funzione. Tuttavia, Vecchio ksh non supporta $()in PS1. Quindi, se è una versione moderna di ksh, voglio PS1usare la funzione che ho creato; se è la vecchia versione, io uso solo $PWD.
Sildoreth,

Beh, si potrebbe avere due versioni del file di configurazione (forse quello generato dalle altre) e quindi distribuire la versione appropriata per la macchina in questione?
Thorbjørn Ravn Andersen,

Un altro approccio potrebbe essere semplicemente quello di dire "È solo questa macchina particolare che ha il problema: troverò un file o una variabile d'ambiente o qualcos'altro che esiste solo qui (probabilmente AIX o qualcosa del genere) e testerò invece per quello".
Thorbjørn Ravn Andersen,

Risposte:


7

Penso che .sh.versionesista dalla prima versione di ATT ksh 93. Non è disponibile in pdksh o mksh. Poiché si ${.sh.version}tratta di un errore di sintassi in shell diverse da ksh93, avvolgere il test per esso in una subshell e proteggerlo dietro 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 è iniziato nel clone di dominio pubblico ksh (pdksh) e è stato aggiunto alla shell Korn attuale relativamente di recente, nel 2008 con ksh93t.

Invece di testare un numero di versione, dovresti testare la funzionalità specifica che ti sta dando dolore. La maggior parte delle funzionalità può essere testata provando un costrutto in una subshell e vedere se innesca un errore.


Non vedo alcuna differenza quando si utilizza una subshell. Tratta ancora ${.sh.version}come un errore di sintassi che non può essere riconciliato. Il messaggio che ricevo è bad substitution.
Sildoreth,

@sil Il punto di usare una subshell è catturare l'errore. Reindirizzare gli errori /dev/nulle ignorare lo stato di uscita.
Gilles 'SO- smetti di essere malvagio'

Capisco quello che stai dicendo. Quello che sto dicendo è che l'errore non reindirizza. Stampa sempre sulla console. Ho provato questo in Solaris, AIX e HP-UX; e ksh mostra questo comportamento in tutti loro.
Sildoreth,

@Sildoreth Ah. Avevo testato solo su Linux e non ho nessuno di questi SO da testare ora. Funziona eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullmeglio?
Gilles 'SO- smetti di essere malvagio'

Va un po 'meglio. Funziona perfettamente in Solaris e HP-UX. Per AIX, funziona dalla riga di comando ma curiosamente inizia a fallire di nuovo se provo a inserirlo in una funzione shell.
Sildoreth,

6

KSH_VERSIONnon era implementato ksh93prima della versione 93t. Esso sarà ambientato in mksh, pdksh, lksh. Quindi, per controllare la versione di ksh, possiamo provare questi passaggi:

  • Controllo KSH_VERSIONper rilevare mksh, pdksh,lksh
  • Se il primo passo fallisce, prova una funzione diversa tra ksh93e ksh88/86( Lascia che David Korn ci mostri ).

Con questi in mente, andrò con:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac

Perché questo non controlla se $KSH_VERSIONprima non è vuoto? Sulla mia macchina Ubuntu, questo stampa "ksh93", ma KSH_VERSIONè impostato.
Sildoreth,

Ciò fallirebbe se un codice precedentemente eseguito (ad esempio: .kshrc) manomettesse la variabile KSH_VERSION con un valore casuale.
jlliagre,

@jlliagre: No, dato che è stato eseguito come script, non legge .kshrc.
cuonglm,

Se la ENVvariabile è impostata (e in genere è impostata su ~/.kshrc), lo script leggerà sicuramente il .kshrcfile. Certo, sarebbe abbastanza strano per uno script impostare una falsa KSH_VERSION, ma ciò è comunque possibile, proprio come eseguire esplicitamente uno script con un interprete diverso da quello specificato nella sua prima riga è una possibile situazione.
jlliagre,

@jlliagre: anche tu puoi cambiarlo, diventerai segfault quando ti riferirai a KSH_VERSION. E in mksh, pdksh, lksh, KSH_VERSIONè contrassegnato come di sola lettura.
cuonglm,

5

Per le kshversioni "reali" (ovvero basate su AT&T), utilizzo questo comando:

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

Ecco vari output che ottengo:

Ksh originale:

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

dtksh;

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

Ksh93 moderno:

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

Anche per pdksh/ msh kshcloni e le moderne kshversioni di AT&T , ecco qualcosa che funziona:

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

Modificare:

Ho trascurato che stavi chiedendo di farlo dall'interno di uno script, non conoscendo il percorso del binario ksh testato.

Supponendo che tu voglia davvero la versione di kshusato, e non le funzionalità che supporta, ecco un modo per farlo usando solo il stringscomando che dovrebbe funzionare almeno su Linux e 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)

Si noti che questo metodo non è affidabile in quanto /procpotrebbe non essere montato e ci sono sicuramente altri punti deboli. Non è testato su altri sistemi operativi Unix.


Questo non farà distinzione tra lkshe pdkshin Debian Jessie.
cuonglm,

@cuonglm Non ho Jessie da testare. Vuoi dire lkshe pdkshnon può essere risolto dal loro KSH_VERSION?
jlliagre,

No, intendo correre stringssu di loro. KSH_VERSIONdefinitivamente possibile.
cuonglm,

@cuonglm Scusate se non ero chiaro. Quando ho scritto «per kshversioni " reali " , escludevo esplicitamente cloni non AT&T ksh come pdksh, mkshe lksh.
jlliagre,

Eseguire stringssu alcuni binari di ksh è una cattiva idea perché non sai se è quello che esegue il tuo script. Forse la tua sceneggiatura è gestita da /usr/local/bin/ksho /home/bob/bin/ksho /bin/sho /usr/posix/bin/sho ...
Gilles 'SO- smetti di essere malvagio'

2

Mentre stavo scrivendo uno script per ksh, ho notato che l' -aopzione del whencecomando integrato di ksh sembra non essere supportata nelle versioni precedenti di ksh. E questo sembra essere vero su tutti i sistemi che ho controllato, tra cui Solaris, AIX, HP-UX e Linux.

Ecco quindi la soluzione come funzione 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
}

Ed ecco come 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

Perché non usi ${.sh.version}?
cuonglm,

@cuonglm perché non posso. Vedi i commenti sulla risposta di Gilles .
Sildoreth,

sfortunatamente il whencein Zsh ha-a
Greg A. Woods il

@ GregA.Woods, questa funzione è specifica per ksh. La definizione della funzione andrebbe in .kshrc e quindi non esisterebbe nemmeno per altre shell come zsh. zsh ha il suo whencecomando integrato che non è in alcun modo legato a ksh o alla sua versione. Non so nemmeno perché ti dispiacerebbe verificare se ksh è una versione precedente all'interno di un'istanza di zsh, che è una shell completamente diversa.
Sildoreth,

C'è un problema con i tuoi presupposti: Zsh è spesso installato con un collegamento /bin/ksh, ad esempio su Debian Linux. Ora non lo uso lì (e al momento non posso cambiare la mia shell di login per controllare), quindi non so se legge .kshrco no, ma sospetto che lo faccia.
Greg A. Woods,

1

CTRL+ ALT+V

o

ESC, CTRL+V

In genere si sono dimostrati molto affidabili per quanto riguarda la determinazione interattiva della versione di KSH che stai utilizzando, tuttavia lo scripting si è rivelato più difficile.


1
questo è stato l'unico che ha funzionato per una versione AIX ksh 88f.
Jeff Schaller

1
Ho funzionato l'opzione <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd>, dopo che ho corso set -o viper impostare le combinazioni di tasti come vi-like. Prima di questo, o con + o vi o -o emacs, semplicemente non mi avrebbe mostrato. PD KSH v5.2.14 99/07 / 13.2 su openbsd 6.1
bgStack15

0

Penso che il problema fondamentale con l'utilizzo di $ {. Sh.version} sia che ksh88 si ferma, con un codice di uscita diverso da zero.

Quindi la mia soluzione è quella di mettere il codice che fa riferimento a $ {. Sh.version} in una sotto-shell, quindi testare per vedere se la sotto-shell esce da zero e ha un codice nella sotto-shell che funzionerà sulle versioni di il ksh dove fa riferimento a $ {. sh.version}. Avvolgendolo in una funzione che viene quindi chiamata da un'altra funzione che inverte il codice di ritorno, in modo che la chiamata finale stia verificando la verità.

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"

Ho eseguito questo su AIX e Oracle Enterprise Linux 5 e 6, con ksh88, ksh93 e pdksh.

Pete


1
il moderno AT&T Ksh fornisce ancora .sh.version(in effetti KSH_VERSIONè effettivamente un alias per questo). Anche alcune shell, ad esempio NetBSD sh, smettono semplicemente di leggere dopo l'incontro ${.sh.version}e nessuna quantità di reindirizzamento può farle eseguire lo script.
Greg A. Woods,

0

Quanto segue sembra funzionare abbastanza bene per tutte le shell che ho testato, tra cui il vecchio ksh88e e una gamma quasi completa di cloni Ksh comuni (anche se solo una versione di ciascuno), anche se non ho ancora testato una vera shell Bourne originale ( e farlo potrebbe richiedere l'adattamento testdell'espressione per le versioni precedenti ....

Addendum:

Ora ho anche provato con successo questo con Heirloom Bourne Shell, sebbene con un programma esterno (e più 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"

Perché dovresti voler eseguire questa funzione per shell che non sono ksh? Se stai eseguendo uno script in bash o zsh, allora ksh non entra mai in gioco. Inoltre, è già stato stabilito tramite le risposte di altri che ${.sh.version}non possono far parte della soluzione perché alcune versioni di ksh - le versioni di cui il post originale era preoccupato - si sono verificate fatalmente su quella sintassi.
Sildoreth,

Come ho detto, la funzione che mostro è stata testata con versioni di ksh che danno errori "fatali", così come versioni di Ash che fanno lo stesso.
Greg A. Woods,

Gli script che scrivo sono pensati per essere portatili e per essere eseguiti da qualsiasi shell capace. Inoltre, come ho detto altrove, alcune persone non sapranno necessariamente che stanno usando Zsh come Ksh perché quando digitano 'ksh' verrà invocato il binario Zsh (con argv [0] come "ksh").
Greg A. Woods,

Questo fa luce su da dove vieni. Tuttavia, sembra un requisito non realistico. In genere, quando uno sviluppatore Unix dice "portatile", non significa "questo codice verrà eseguito in qualsiasi shell ", significa "questo verrà eseguito su qualsiasi sistema ". E se hai bisogno di eseguire uno script che è stato scritto per un'altra shell, è perfettamente legale; basta avviare un'istanza non interattiva dell'altra shell nello script. Lo sostengo perché voglio promuovere buone pratiche di codifica. Se questa soluzione funziona per te, eccezionale. Ma consiglierei ad altri di adottare un approccio più semplice.
Sildoreth,

1
Certamente tutti gli sforzi per trascinare la compatibilità all'indietro troppo lontano nel passato sono piuttosto sciocchi. Ho solo compilato versioni dell'antica AT&T Ksh e Unix Sh per soddisfare il mio desiderio personale di comprendere meglio la storia e l'evoluzione di alcune caratteristiche e per rinfrescare la mia memoria di come erano le cose (che di solito mi sorprende, poiché le cose erano spesso molto " meglio di quanto mi ricordi, anche se a volte erano anche molto peggio).
Greg A. Woods,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.