Як я можу безпечно отримати версію ksh?


12

Як я можу безпечно отримати версію ksh із сценарію ksh?

Я бачив такі рішення :

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

І з огляду на правильні обставини, кожен із них працює правильно. Однак я дбаю про не досконалий випадок.

Зокрема, є кілька машин, з якими я працюю, і мають більш старі версії ksh, які для моїх цілей вкрай не мають функціональних можливостей. У будь-якому разі, причина, яку я хочу перевірити на версію (програмно), полягає в тому, щоб побачити, чи версія ksh є однією з менш спроможних версій; і якщо так, я хочу виконати гілку з менш дивовижним кодом.

Однак на проблемних машинах невміння оболонки поширюється на перевірку версії ...

  • Якщо я спробую ksh --version, він нічого не надрукує і відкриє новий екземпляр ksh!
  • Якщо я спробую echo ${.sh.version}, kshтрактує це як помилку синтаксису, яку неможливо відмовити 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Звичайно, echo $KSH_VERSIONздається, він працює добре - я маю на увазі, що він не вийде з ладу - хоча на цих машинах він порожній. Також я десь побачив, що KSH_VERSIONвстановлено лише pdksh.

Запитання:

  • Як я можу безпечно перевірити версію kshпрограмно? Для моїх цілей тут мені зовсім не байдуже, що таке фактичний номер версії, лише чи це застаріла версія ksh.
  • Чи $KSH_VERSIONдостатньо добре? Я маю на увазі, якщо він порожній, то це kshобов'язково застаріла версія? Чи був цей правильний форум правильним, що він не може бути встановлений навіть для новіших версій ksh?
  • Чи просто немає способу це перевірити?

1
З якої причини ви хочете два кодові шляхи, а не один єдиний з менш дивовижним кодом?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen це стосується підказки. У моєму файлі .kshrc у мене є функція, яка імітує функцію скорочення pwd tcsh та zsh-підказок, і я налаштований PS1використовувати цю функцію. Однак, Старий КШ не підтримує $()в PS1. Отже, якщо це сучасна версія ksh, я хочу PS1використовувати функцію, яку я створив; якщо це стара версія, я використовую просто $PWD.
Сілдорет

Ну, ви могли б мати дві версії вашого конфігураційного файлу (можливо, одну, згенеровану з іншої), а потім поширити відповідну версію на відповідну машину?
Thorbjørn Ravn Andersen

Іншим підходом може бути просто сказати: "У цій проблемі є лише ця конкретна машина - я знайду змінну файлу чи середовища або щось інше, що існує лише тут (можливо, AIX або щось все одно) і тестую на це замість цього".
Thorbjørn Ravn Andersen

Відповіді:


7

Я думаю, що .sh.versionце існує ще з першої версії ATT ksh 93. Він не доступний в pdksh або mksh. Оскільки ${.sh.version}помилка синтаксису в оболонках, відмінних від ksh93, оберніть тест для цього в нижній частині і захистіть його позаду 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 розпочався у загальнодоступному клоні ksh (pdksh), і був доданий до фактичної оболонки Korn порівняно недавно, у 2008 році з ksh93t.

Замість того, щоб перевірити номер версії, слід перевірити ту особливість, яка доставляє вам горе. Більшість функцій можна перевірити, спробувавши деяку конструкцію в підкашрі і побачити, чи не викликає помилку.


Я не бачу різниці при використанні підшару. Він все ще трактується ${.sh.version}як синтаксична помилка, яку неможливо виправити. Я отримую повідомлення bad substitution.
Сілдорет

@sil Сенс використання підшару полягає в тому, щоб знайти помилку. Перенаправлення помилок на /dev/nullстан виходу та їх ігнорування.
Жил "ТАК - перестань бути злим"

Я розумію, що ти кажеш. Що я кажу, що помилка не перенаправляється. Він завжди друкує на консоль. Я спробував це в Solaris, AIX та HP-UX; і ksh проявляє таку поведінку у всіх.
Сілдорет

@Sildoreth Ah. Я тестувався лише на Linux, і зараз я не маю жодної з цих ОС. Чи eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullпрацює краще?
Жил "ТАК - перестань бути злим"

Це трохи краще. Це найкраще працює в Solaris та HP-UX. Для AIX він працює в командному рядку, але з цікавістю знову починає виходити з ладу, якщо я спробую розмістити його у функції оболонки.
Сілдорет

6

KSH_VERSIONне було реалізовано ksh93до версії 93t. Він буде встановлений в mksh, pdksh, lksh. Тому для перевірки версії ksh, ми можемо спробувати наступні кроки:

  • Перевірка KSH_VERSIONвиявити mksh, pdksh,lksh
  • Якщо перший крок не вдається, спробуйте функцію, яка відрізняється між ksh93і ksh88/86( Нехай Девід Корн покаже нам ).

З огляду на це, я піду:

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

Чому це не перевіряється, якщо $KSH_VERSIONспочатку це не порожнє? На моїй машині Ubuntu цей принт "ksh93" ще KSH_VERSIONвстановлений.
Sildoreth

Це не вдасться, якщо якийсь раніше виконаний код (наприклад: .kshrc) підробив змінну KSH_VERSION деяким випадковим значенням.
jlliagre

@jlliagre: Ні, оскільки він був запущений як сценарій, він не читається .kshrc.
cuonglm

Якщо ENVзмінна встановлена ​​(і зазвичай вона встановлена ~/.kshrc), скрипт обов'язково прочитає .kshrcфайл. Звичайно, було б досить дивним, щоб сценарій встановив фіктивну KSH_VERSION, але це все-таки можливо, подібно до того, як явно виконати сценарій з іншим інтерпретатором, ніж той, який вказаний у його першому рядку, можлива ситуація.
jlliagre

@jlliagre: Навіть якщо ви можете змінити це, ви отримаєте segfault, коли ви посилаєтесь на KSH_VERSION. І mksh, pdksh, lksh, KSH_VERSIONпозначається як незмінні.
cuonglm

5

Для "реальних" kshвипусків (тобто на основі AT&T) я використовую цю команду:

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

Ось різні результати, які я отримую:

Оригінальний ksh:

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

дткш;

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

Сучасний ksh93:

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

Для pdksh/ msh kshклонів та сучасних kshверсій AT&T також працює щось таке:

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

Редагувати:

Я не помітив, що ви запитували про те, щоб це зробити з сценарію, а не знаючи шлях до тестованого бінарного файлу ksh.

Якщо припустити, що ви дійсно хочете використовувати версію, яка kshвикористовується, а не функції, які вона підтримує, ось один із способів зробити це лише за допомогою stringsкоманди, яка повинна працювати принаймні на Linux та 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)

Зауважте, що цей метод ненадійний, оскільки його /procнеможливо встановити, і, безумовно, є й інші недоліки. Це не перевірено на інших ОС Unix.


Це не буде розрізняти lkshі pdkshв Debian Jessie.
cuonglm

@cuonglm У мене немає Джессі для тестування. Ви маєте на увазі lkshі pdkshне можете їх розібратися KSH_VERSION?
jlliagre

Ні, я маю stringsна увазі біг на них. KSH_VERSIONостаточно може.
cuonglm

@cuonglm Вибачте, якщо я не зрозумів. Коли я писав «для« справжніх » kshрелізів, я чітко виключав не AT & T ksh-клони, як pdksh, mkshі lksh.
jlliagre

Запуск stringsна деяких ksh бінарних файлах - погана ідея, оскільки ви не знаєте, чи це той, що працює з вашим сценарієм. Можливо, ваш сценарій працює /usr/local/bin/kshабо /home/bob/bin/kshабо /bin/shабо /usr/posix/bin/shабо…
Жил "ТАК - перестань бути злим"

2

Поки я писав сценарій для ksh, я помітив, що -aпараметр вбудованої whenceкоманди ksh, здається, не підтримується в старих версіях ksh. І це, мабуть, вірно у всіх перевірених системах, включаючи Solaris, AIX, HP-UX та Linux.

Отже, ось рішення як функція 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
}

А ось як його використовувати:

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

Чому ви не використовуєте ${.sh.version}?
cuonglm

@cuonglm, тому що я не можу. Дивіться коментарі до відповіді Жиля .
Сілдорет

на жаль, whenceу Zsh є-a
Грег А. Вудс

@ GregA.Woods, ця функція спеціально для ksh. Визначення функції входило б у .kshrc і тому не існувало б навіть для інших оболонок, таких як zsh. zsh має власну вбудовану whenceкоманду, яка жодним чином не прив’язана до ksh або його версії. Я навіть не знаю, чому ви б хотіли перевірити, чи ksh - це стара версія з екземпляру zsh, яка зовсім інша оболонка.
Sildoreth

Існує проблема з вашими припущеннями: Zsh часто встановлюється з посиланням на /bin/ksh, наприклад, на Debian Linux. Зараз я не використовую його там (і на даний момент я не можу змінити свою оболонку входу, щоб перевірити), тому не знаю, чи читається вона .kshrcчи ні, але я б підозрював, що це робить.
Грег А. Вудс

1

CTRL+ ALT+V

або

ESC, CTRL+V

Як правило, вони виявились дуже надійними щодо інтерактивного визначення версії KSH, яку ви використовуєте, однак їх створення сценаріїв виявилося складніше.


1
це єдиний, хто працював для версії AIX ksh 88f.
Джефф Шаллер

1
У мене з'явився варіант <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd> після того, як я побіг set -o viвстановити прив'язки клавіш до типу vi-like. До цього, або з + o vi або -o emacs, це мені просто не показало б. PD KSH v5.2.14 99/07 / 13.2 у openbsd 6.1
bgStack15

0

Я думаю, що основна проблема використання $ {. Sh.version} полягає в тому, що ksh88 просто зупиняється з ненульовим кодом виходу.

Тож моє рішення полягає в тому, щоб помістити код, на який посилається $ {. Sh.version}, у підзагін, а потім перевірити, чи немає в під оболонці коду та чи буде він у під-оболонці, який буде працювати над версіями ksh, де посилання на $ {. sh.version} працює. Обгортання його у функцію, яку потім викликає інша функція, яка повертає код повернення, так що підсумковий виклик перевіряє справжність.

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"

Я запустив це на AIX та Oracle Enterprise Linux 5 & 6, з ksh88, ksh93 та pdksh.

Піт


1
сучасний AT&T Ksh все ще постачається .sh.version(справді KSH_VERSIONце фактично псевдонім для нього). Також деякі оболонки, наприклад NetBSD sh, просто припиняють читати після зустрічі, ${.sh.version}і жодна кількість переспрямувань не може змусити їх виконувати сценарій.
Грег А. Вудс

0

Далі, здається, працює досить добре для всіх протестованих оболонок, включаючи старий ksh88e і майже повний спектр звичайних Ksh-клонів (хоча лише одна версія кожного), хоча я ще не перевіряв фактичну оригінальну оболонку Bourne ( і це може зажадати адаптації testвиразу для старих версій ....

Додаток:

Зараз я також успішно протестував це з Heirloom Bourne Shell, хоча із зовнішньою (і більш сучасною) 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"

Чому ви хочете запустити цю функцію для оболонок, які не є ksh? Якщо ви використовуєте скрипт в bash або zsh, ksh ніколи не вступає в гру. Крім того, це вже було встановлено за допомогою відповідей інших, які ${.sh.version}не можуть бути частиною рішення, оскільки певні версії ksh - версії, про які стосувалася початкова публікація - помилково помиляються в цьому синтаксисі.
Сілдорет

Як я вже сказав, функція, яку я показую, була протестована з версіями ksh, які дають "фатальні" помилки, а також версіями Ash, які роблять те саме.
Грег А. Вудс

Сценарії, які я пишу, призначені для переносу та керування будь-якою здатною оболонкою. Крім того, як я вже казав десь, деякі люди не обов'язково знають, що вони використовують Zsh як Ksh, оскільки коли вони вводять 'ksh', бінарний файл Zsh буде викликаний (аргумент [0] як "ksh").
Грег А. Вудс

Це проливає світло на те, звідки ви родом. Однак це звучить як нереальна вимога. Як правило, коли розробник Unix каже "портативний", вони не означають, що "цей код буде працювати в будь-якій оболонці ", вони означають "це буде працювати в будь-якій системі ". І якщо вам потрібно виконати сценарій, написаний для іншої оболонки, це цілком законно; просто запустіть інтерактивний екземпляр іншої оболонки у вашому сценарії. Я піднімаю це, тому що хочу просувати хороші методи кодування. Якщо це рішення працює для вас, чудово. Але я б порадив іншим скористатися більш простим підходом.
Сілдорет

1
Справді, будь-які зусилля, спрямовані на те, щоб перенести сумісність занадто далеко в минуле, досить нерозумні. Я лише склав версії стародавніх AT&T Ksh та Unix Sh, щоб задовольнити моє особисте бажання краще зрозуміти історію та еволюцію деяких особливостей та оновити пам’ять про те, як все було (що зазвичай мене дивує, оскільки речі часто бувають багато " краще ", ніж я пам'ятаю, хоча колись вони теж були набагато гірші).
Грег А. Вудс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.