Как мне безопасно получить версию 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?
  • Нет ли способа проверить это вообще?
Sildoreth
источник
1
По какой-то причине вам нужны два пути кода, а не просто один с менее крутым кодом?
Турбьёрн Равн Андерсен
@ ThorbjørnRavnAndersen это связано с подсказкой. В моем файле .kshrc у меня есть функция, которая имитирует функциональные возможности pwd-abbreviating приглашений tcsh и zsh, и я настроен PS1на использование этой функции. Однако Old ksh не поддерживает $()в PS1. Так что, если это современная версия ksh, я хочу PS1использовать созданную мной функцию; если это старая версия, я использую просто $PWD.
Сильдорет
Что ж, у вас может быть две версии вашего файла конфигурации (возможно, одна сгенерированная из другой), а затем распространять соответствующую версию для рассматриваемой машины?
Турбьерн Равн Андерсен
Другой подход может заключаться в том, чтобы просто сказать: «Проблема связана только с этой конкретной машиной - я найду файл или переменную окружения или что-то еще, что существует только здесь (вероятно, AIX или что-то еще), и протестирую вместо этого».
Турбьёрн Равн Андерсен

Ответы:

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 Ах. Я тестировал только на 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
cuonglm
источник
Почему эта проверка не проверяется, если $KSH_VERSIONона не пуста? На моей машине с Ubuntu, это печатает "ksh93", пока KSH_VERSIONне установлено.
Сильдорет
Это не получится, если какой-то ранее выполненный код (например, .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 

Вот различные выходные данные, которые я получаю:

Оригинал кш:

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

dtksh;

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

Современный кш93:

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

Для pdksh/ msh kshclones и современных 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.

jlliagre
источник
Это не будет различать lkshи pdkshв Debian Джесси.
cuonglm
@cuonglm У меня нет Джесси, чтобы проверить. Ты имеешь ввиду lkshи pdkshне можешь разобраться со своими KSH_VERSION?
Jlliagre
Нет, я имею в виду бегать stringsна них. KSH_VERSIONокончательно может.
Cuonglm
@cuonglm Извините, если мне неясно. Когда я писал «для« настоящих » kshвыпусков», я явно исключал не клоны AT & T, такие как pdksh, mkshи lksh.
июля
Работать stringsна некотором двоичном файле ksh - плохая идея, потому что вы не знаете, запускает ли он ваш скрипт. Может быть, ваш сценарий исполняется /usr/local/bin/kshили /home/bob/bin/kshили /bin/shили /usr/posix/bin/shили ...
Жиль "ТАК, перестань быть злым"
2

Когда я писал скрипт для ksh, я заметил, что -aопция встроенной команды ksh, whenceпохоже, не поддерживается в более старых версиях 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
Sildoreth
источник
Почему ты не используешь ${.sh.version}?
cuonglm
@cuonglm, потому что я не могу. Смотрите комментарии к ответу Жиля .
Сильдорет
к сожалению, whenceв Zsh есть-a
Грег А. Вудс
@ GregA.Woods, эта функция специально для ksh. Определение функции будет идти в .kshrc и даже не будет существовать для других оболочек, таких как zsh. У zsh есть собственная встроенная whenceкоманда, которая никоим образом не связана с ksh или ее версией. Я даже не знаю, почему вы захотите проверить, является ли ksh старой версией из экземпляра zsh, который представляет собой совершенно другую оболочку.
Сильдорет
Есть проблема с вашими предположениями: 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. До этого, или с + 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выражения для более старых версий ....

Приложение:

Теперь я также успешно проверил это с помощью семейной реликвии 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 (с argv [0] как "ksh").
Грег А. Вудс
Это проливает свет на то, откуда вы пришли. Однако это звучит как нереальное требование. Как правило, когда разработчик Unix говорит «переносимый», они не означают «этот код будет работать в любой оболочке », они подразумевают «он будет работать в любой системе ». И если вам нужно выполнить скрипт, написанный для другой оболочки, это совершенно законно; просто запустите неинтерактивный экземпляр другой оболочки в вашем скрипте. Я поднял это, потому что я хочу продвинуть хорошие методы кодирования. Если это решение работает для вас, отлично. Но я бы посоветовал другим воспользоваться более простым подходом.
Сильдорет
1
По общему признанию, любые попытки перенести обратную совместимость слишком далеко в прошлое довольно глупы. Я только скомпилировал версии древних AT & T Ksh и Unix Sh, чтобы удовлетворить мое собственное желание лучше понять историю и развитие некоторых функций и освежить мою память о том, как все было (что обычно удивляет меня, поскольку вещей часто было много » лучше ", чем я помню, хотя иногда они тоже были намного хуже).
Грег А. Вудс