Jak mogę bezpiecznie uzyskać wersję ksh?


12

Jak mogę bezpiecznie pobrać wersję ksh ze skryptu ksh?

Ja widziałem następujące rozwiązania :

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

Biorąc pod uwagę odpowiednie okoliczności, każda z nich działa poprawnie. Dbam jednak o niedoskonały przypadek.

W szczególności istnieje kilka maszyn, z którymi współpracuję, które mają starsze wersje ksh, które, moim zdaniem, są bardzo pozbawione funkcjonalności. W każdym razie powodem, dla którego chcę sprawdzić wersję (programowo), jest sprawdzenie, czy wersja ksh jest jedną z mniej zdolnych wersji; a jeśli tak, chcę wykonać gałąź z mniej niesamowitym kodem.

Jednak na problematycznych maszynach nieudolność powłoki rozciąga się na sprawdzenie wersji ...

  • Jeśli spróbuję ksh --version, nic nie drukuje i otwiera nową instancję ksh!
  • Jeśli spróbuję echo ${.sh.version}, kshpotraktuje to jako błąd składniowy, którego nie można usunąć 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Oczywiście echo $KSH_VERSIONwydaje się, że działa dobrze - mam na myśli to, że się nie zawiesi - choć na tych komputerach jest pusty. Również widziałem gdzieś , że KSH_VERSIONjest ustawiony tylko pdksh.

Pytania:

  • Jak mogę bezpiecznie sprawdzić wersję kshprogramowo? Dla moich celów nie obchodzi mnie, jaki jest rzeczywisty numer wersji, tylko czy jest to przestarzała wersja ksh.
  • Czy $KSH_VERSIONwystarczy? Mam na myśli to, że jeśli jest puste, to kshkoniecznie jest nieaktualna wersja? Czy to drugie forum było prawidłowe, że może nie być ustawione nawet dla nowszych wersji ksh?
  • Czy po prostu w ogóle nie można tego sprawdzić?

1
Czy jest jakiś powód, dla którego chcesz mieć dwie ścieżki kodu, a nie tylko jedną z mniej niesamowitym kodem?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen ma to związek z pytaniem. W moim pliku .kshrc mam funkcję, która symuluje funkcjonalność skracania pwd podpowiedzi tcsh i zsh, i skonfigurowałem się PS1do korzystania z tej funkcji. Jednak Old ksh nie obsługuje $()w PS1. Więc jeśli jest to nowoczesna wersja ksh, chcę PS1użyć funkcji, którą stworzyłem; jeśli to stara wersja, używam tylko $PWD.
Sildoreth

Cóż, mógł mieć dwie wersje pliku konfiguracyjnego (chyba jeden generowane z drugiej), a następnie rozprowadzić na odpowiednią wersję do maszyny w pytaniu?
Thorbjørn Ravn Andersen

Innym podejściem może być po prostu powiedzenie „Problemem jest tylko ta konkretna maszyna - znajdę plik lub zmienną środowiskową albo coś, co istnieje tylko tutaj (prawdopodobnie AIX lub coś takiego) i przetestuję to”.
Thorbjørn Ravn Andersen

Odpowiedzi:


7

Myślę, że .sh.versionistnieje od pierwszej wersji ATT ksh 93. Nie jest dostępny w pdksh ani mksh. Ponieważ ${.sh.version}jest to błąd składniowy w powłokach innych niż ksh93, zawiń test w podpowłoce i chroń go 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 zaczął w klonie ksh (pdksh) w domenie publicznej i został dodany do rzeczywistej powłoki Korna stosunkowo niedawno, w 2008 roku z ksh93t.

Zamiast testować numer wersji, powinieneś przetestować konkretną funkcję, która wywołuje u Ciebie żal. Większość funkcji można przetestować, wypróbowując jakąś konstrukcję w podpowłoce i sprawdzając, czy powoduje to błąd.


Nie widzę żadnej różnicy podczas korzystania z podpowłoki. Nadal jest traktowany ${.sh.version}jako błąd składniowy, którego nie można pogodzić. Otrzymałem wiadomość bad substitution.
Sildoreth

@sil Celem użycia podpowłoki jest złapanie błędu. Przekieruj błędy /dev/nulli zignoruj ​​status wyjścia.
Gilles „SO- przestań być zły”

Rozumiem co mówisz. Mówię tylko, że błąd nie przekierowuje. To zawsze drukuje do konsoli. Próbowałem tego w systemach Solaris, AIX i HP-UX; i ksh wykazuje to zachowanie we wszystkich z nich.
Sildoreth,

@Sildoreth Ah. Testowałem tylko na Linuksie i nie mam teraz żadnego z tych systemów operacyjnych do przetestowania. Czy eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nulldziała lepiej?
Gilles „SO- przestań być zły”

To trochę lepiej. Działa doskonale w systemach Solaris i HP-UX. W systemie AIX działa w wierszu poleceń, ale z ciekawością zaczyna się ponownie, jeśli spróbuję umieścić go w funkcji powłoki.
Sildoreth,

6

KSH_VERSIONnie został zaimplementowany ksh93przed wersją 93t. Będzie on być ustawiony w mksh, pdksh, lksh. Aby sprawdzić wersję ksh, możemy wypróbować następujące kroki:

  • Sprawdzanie KSH_VERSIONwykryć mksh, pdksh,lksh
  • Jeśli pierwszy krok nie powiedzie się, wypróbuj funkcję, która różni się między ksh93i ksh88/86( Pokaż nam David Korn ).

Mając to na uwadze, pójdę z:

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

Dlaczego ta opcja nie sprawdza, czy pole $KSH_VERSIONnie jest puste? Na moim komputerze Ubuntu wyświetla „ksh93”, ale KSH_VERSIONjest ustawione.
Sildoreth

Nie udałoby się to, gdyby jakiś wcześniej wykonany kod (np. .Kshrc) sfałszował zmienną KSH_VERSION przy pomocy jakiejś losowej wartości.
jlliagre

@jlliagre: Nie, ponieważ został uruchomiony jako skrypt, nie czyta się .kshrc.
cuonglm

Jeśli ENVzmienna jest ustawiona (i zwykle jest ustawiona na ~/.kshrc), skrypt na pewno odczyta .kshrcplik. Oczywiście, byłoby dość dziwne, gdyby skrypt ustawiał fałszywy KSH_VERSION, ale jest to jednak możliwe, podobnie jak jawne wykonanie skryptu przy użyciu innego interpretera niż ten określony w pierwszym wierszu jest możliwą sytuacją.
jlliagre

@jlliagre: Nawet jeśli możesz to zmienić, dostaniesz segfault, gdy będziesz się do niego odnosił KSH_VERSION. I mksh, pdksh, lksh, KSH_VERSIONjest oznaczony jako tylko do odczytu.
cuonglm

5

W przypadku „prawdziwych” kshwersji (tj. Opartych na AT&T) używam tego polecenia:

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

Oto różne wyniki, które otrzymuję:

Oryginalny ksh:

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

dtksh;

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

Współczesny ksh93:

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

Dla pdksh/ msh kshklonów i nowoczesnych wersji AT&T ksh, oto coś, co działa:

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

Edytować:

Przeoczyłem, że pytasz o zrobienie tego ze skryptu, nie poprzez znajomość ścieżki do testowanego pliku binarnego ksh.

Zakładając, że naprawdę chcesz wersji kshużywanej, a nie obsługiwanych przez nią funkcji, oto jeden ze sposobów, aby to zrobić, używając tylko stringspolecenia, które powinno działać przynajmniej w systemie Linux i 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)

Zauważ, że ta metoda jest zawodna, ponieważ /procmoże nie zostać zamontowana, a na pewno istnieją inne słabości. Nie jest testowany na innych systemach uniksowych.


Nie będzie to rozróżnienie pomiędzy lkshi pdkshw Debian Jessie.
cuonglm

@cuonglm Nie mam Jessie do przetestowania. Masz na myśli lkshi pdkshnie można ich oddzielić KSH_VERSION?
jlliagre

Nie, mam na myśli bieganie stringspo nich. KSH_VERSIONzdecydowanie może.
cuonglm

@cuonglm Przepraszam, gdybym był niejasny. Kiedy pisałem «dla«prawdziwych» kshwydań», byłem wyraźnie wyłączając klonów niż AT & T KSH, jak pdksh, mkshi lksh.
jlliagre

Uruchamianie stringsna niektórych plikach binarnych ksh to zły pomysł, ponieważ nie wiesz, czy to ten, który uruchamia twój skrypt. Może twój skrypt jest uruchamiany przez /usr/local/bin/kshlub /home/bob/bin/kshlub /bin/shlub /usr/posix/bin/shalbo…
Gilles „SO- przestań być zły”

2

Podczas pisania skryptu kshzauważyłem, że -aopcja wbudowanego whencepolecenia ksh wydaje się nie być obsługiwana w starszych wersjach ksh. I wydaje się, że tak jest we wszystkich sprawdzonych systemach, w tym w systemach Solaris, AIX, HP-UX i Linux.

Oto rozwiązanie jako funkcja 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
}

A oto jak go użyć:

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

Dlaczego nie używasz ${.sh.version}?
cuonglm

@cuonglm, bo nie mogę. Zobacz komentarze do odpowiedzi Gillesa .
Sildoreth

niestety whencew Zsh ma-a
Greg A. Woods

@ GregA.Woods, ta funkcja jest specjalnie dla ksh. Definicja funkcji byłaby zapisana w .kshrc, a więc nie istniałaby nawet dla innych powłok, takich jak zsh. zsh ma swoje wbudowane whencepolecenie, które nie jest w żaden sposób powiązane z ksh lub jego wersją. Nie wiem nawet, dlaczego chciałbyś sprawdzić, czy ksh jest starą wersją z poziomu instancji zsh, która jest zupełnie inną powłoką.
Sildoreth,

Istnieje problem z twoimi założeniami: Zsh jest często instalowany z linkiem do /bin/ksh, np. W Debian Linux. Teraz go tam nie używam (w tej chwili nie mogę zmienić powłoki logowania, żeby sprawdzić), więc nie wiem, czy to czyta .kshrc, ale podejrzewam, że tak.
Greg A. Woods

1

CTRL+ ALT+V

lub

ESC, CTRL+V

Zazwyczaj okazały się bardzo niezawodne, jeśli chodzi o interaktywne określanie używanej wersji KSH, jednak ich skryptowanie okazało się trudniejsze.


1
był to jedyny, który działał dla wersji AIX ksh 88f.
Jeff Schaller

1
Mam opcję <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd> do pracy, po tym jak pobiegłem, set -o viaby ustawić skróty klawiszowe na vi. Wcześniej, lub z + o vi lub -o emacs, po prostu mi się nie pokazywał. PD KSH v5.2.14 99/07 / 13.2 na openbsd 6.1
bgStack15

0

Myślę, że podstawowym problemem z użyciem $ {. Sh.version} jest to, że ksh88 po prostu zatrzymuje się, z niezerowym kodem wyjścia.

Więc moim rozwiązaniem jest umieszczenie kodu, który odwołuje się do $ {. Sh.version} w podpowłoce, a następnie przetestowanie, aby sprawdzić, czy podpowłoka wychodzi z zera i czy ma kod w podpowłoce, który będzie działał na wersjach ksh, do którego odwołuje się $ {. sh.version}. Zawijanie go w funkcję, która jest następnie wywoływana przez inną funkcję, która odwraca kod powrotu, tak aby końcowe wywołanie sprawdzało, czy jest prawdziwe.

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"

Uruchomiłem to na systemach AIX i Oracle Enterprise Linux 5 i 6 z ksh88, ksh93 i pdksh.

Pete


1
nowoczesne AT&T Ksh wciąż dostarcza .sh.version(faktycznie KSH_VERSIONjest to alias). Również niektóre powłoki, np. NetBSD sh, po prostu przestają czytać po napotkaniu ${.sh.version}i żadne przekierowanie nie może utrzymać ich działania skryptu.
Greg A. Woods,

0

Poniższe wydaje się działać całkiem nieźle dla wszystkich przetestowanych przeze mnie powłok, w tym starego ksh88e i prawie pełnej gamy popularnych klonów Ksh (choć tylko jedna wersja każdego z nich), chociaż nie przetestowałem jeszcze prawdziwej oryginalnej powłoki Bourne'a ( i może to wymagać dostosowania testwyrażenia do starszych wersji ...

Uzupełnienie:

Teraz z powodzeniem przetestowałem to również w Heirloom Bourne Shell, chociaż z zewnętrznym (i bardziej nowoczesnym) testprogramem.

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"

Dlaczego chcesz uruchomić tę funkcję dla powłok, które nie są ksh? Jeśli uruchamiasz skrypt w bash lub zsh, wtedy ksh nigdy nie wchodzi w grę. Co więcej, zostało to już ustalone na podstawie odpowiedzi innych, które ${.sh.version}nie mogą być częścią rozwiązania, ponieważ niektóre wersje ksh - wersje, których dotyczył oryginalny post - błąd krytycznie przy tej składni.
Sildoreth,

Jak powiedziałem, funkcja, którą pokazuję, została przetestowana z wersjami ksh, które powodują błędy krytyczne, a także z wersjami Ash, które robią to samo.
Greg A. Woods,

Skrypty, które piszę, mają być przenośne i mogą być uruchamiane przez dowolną zdolną powłokę. Ponadto, jak powiedziałem gdzie indziej, niektórzy ludzie niekoniecznie będą wiedzieć, że używają Zsh jako Ksh, ponieważ po wpisaniu „ksh” zostanie wywołany plik binarny Zsh (z argv [0] jako „ksh”).
Greg A. Woods,

To rzuca światło na to, skąd pochodzisz. Brzmi to jednak nierealnie. Zazwyczaj, gdy programista Unix mówi „przenośny”, nie oznacza to, że ten kod będzie działał w dowolnej powłoce , oznacza to, że „będzie działał w dowolnym systemie ”. A jeśli musisz wykonać skrypt napisany dla innej powłoki, jest to całkowicie legalne; po prostu uruchom nieinteraktywną instancję drugiej powłoki w skrypcie. Mówię o tym, ponieważ chcę promować dobre praktyki kodowania. Jeśli to rozwiązanie Ci odpowiada, to świetnie. Ale radziłbym innym, aby przyjęli prostsze podejście.
Sildoreth,

1
Trzeba przyznać, że wszelkie wysiłki, by zbyt daleko sięgnąć wstecz w przeszłość, są głupie. Skompilowałem tylko wersje starożytnych AT&T Ksh i Unix Sh, aby zaspokoić moje osobiste pragnienie lepszego zrozumienia historii i ewolucji niektórych funkcji oraz odświeżenia mojej pamięci o tym, jak było (co zwykle mnie zaskakuje, ponieważ rzeczy często były dużo „ lepsze ”niż pamiętam, choć czasem były też znacznie gorsze).
Greg A. Woods,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.