Bagaimana saya bisa mendapatkan versi ksh dengan aman?

12

Bagaimana saya bisa dengan aman mendapatkan versi ksh dari dalam script ksh?

Saya telah melihat solusi berikut :

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

Dan mengingat keadaan yang tepat, masing-masing berfungsi dengan benar. Namun, saya peduli dengan kasus yang tidak sempurna.

Secara khusus, ada beberapa mesin yang bekerja dengan saya yang memiliki versi ksh yang lebih lama, yang untuk tujuan saya, sangat kurang fungsionalitasnya. Bagaimanapun, alasan saya ingin memeriksa versi (secara terprogram) adalah untuk melihat apakah versi ksh adalah salah satu versi yang kurang mampu; dan jika demikian, saya ingin menjalankan cabang dengan kode yang kurang bagus.

Namun, pada mesin yang bermasalah, ketidakmampuan shell meluas ke memeriksa versi ...

  • Jika saya mencoba ksh --version , itu tidak mencetak apa-apa dan membuka contoh baru ksh!
  • Jika saya mencoba echo ${.sh.version}, kshmemperlakukan ini sebagai kesalahan sintaks yang tidak dapat diabaikan 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Tentu saja echo $KSH_VERSIONtampaknya berfungsi dengan baik - maksud saya tidak akan crash - meskipun pada mesin ini kosong. Juga, saya melihat suatu tempat yang KSH_VERSIONhanya ditetapkan oleh pdksh.

Pertanyaan:

  • Bagaimana saya bisa memeriksa versi ksh program dengan aman? Untuk tujuan saya di sini, saya tidak terlalu peduli apa nomor versi yang sebenarnya, hanya apakah itu versi lama ksh.
  • Apakah $KSH_VERSIONcukup baik? Maksud saya jika itu kosong, maka ituksh versi yang sudah ketinggalan zaman? Apakah forum lain itu benar sehingga mungkin tidak disetel untuk versi yang lebih baru ksh?
  • Apakah tidak ada cara untuk memeriksa ini sama sekali?
Sildoreth
sumber
1
Adakah alasan Anda menginginkan dua jalur kode dan bukan hanya satu jalur dengan kode yang kurang keren?
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen itu ada hubungannya dengan prompt. Dalam file .kshrc saya, saya memiliki fungsi yang mensimulasikan fungsi singkatan pwd dari tcsh dan zsh, dan saya mengatur PS1untuk menggunakan fungsi ini. Namun, ksh Old tidak mendukung $()dalam PS1. Jadi jika ini adalah versi modern ksh, saya ingin PS1menggunakan fungsi yang saya buat; kalau itu versi lama, saya pakai saja $PWD.
Sildoreth
Nah, Anda bisa memiliki dua versi file konfigurasi Anda (mungkin satu dihasilkan dari yang lain) dan kemudian mendistribusikan versi yang sesuai ke mesin yang dimaksud?
Thorbjørn Ravn Andersen
Pendekatan lain dapat dengan sederhana mengatakan "Hanya mesin khusus ini yang memiliki masalah - saya akan menemukan variabel file atau lingkungan atau sesuatu yang lain yang hanya ada di sini (mungkin AIX atau sesuatu) dan mengujinya sebagai gantinya".
Thorbjørn Ravn Andersen

Jawaban:

7

Saya pikir itu .sh.versionsudah ada sejak versi pertama ATT ksh 93. Tidak tersedia dalam pdksh atau mksh. Karena ${.sh.version}ada kesalahan sintaksis dalam shell selain ksh93, bungkus tes untuk itu dalam subkulit dan lindungi di belakang 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 dimulai pada klon ksh domain publik (pdksh), dan ditambahkan ke shell Korn yang sebenarnya baru-baru ini, pada tahun 2008 dengan ksh93t.

Alih-alih menguji nomor versi, Anda harus menguji fitur tertentu yang membuat Anda sedih. Sebagian besar fitur dapat diuji dengan mencoba beberapa konstruk dalam sebuah subkulit dan melihat apakah itu memicu kesalahan.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Saya tidak melihat perbedaan ketika menggunakan subkulit. Itu masih memperlakukan ${.sh.version}sebagai kesalahan sintaks yang tidak dapat direkonsiliasi. Pesan yang saya dapatkan adalah bad substitution.
Sildoreth
@sil Poin menggunakan subkulit adalah untuk menangkap kesalahan. Arahkan ulang kesalahan ke /dev/nulldan abaikan status keluar.
Gilles 'SO- berhenti menjadi jahat'
Saya mengerti apa yang Anda katakan. Yang saya katakan adalah bahwa kesalahan tidak mengarahkan. Itu selalu dicetak ke konsol. Saya mencoba ini di Solaris, AIX, dan HP-UX; dan ksh menunjukkan perilaku ini pada mereka semua.
Sildoreth
@ Sildoreth Ah. Saya hanya menguji di Linux, dan saya tidak punya OS ini untuk diuji sekarang. Apakah eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullbekerja lebih baik?
Gilles 'SANGAT berhenti menjadi jahat'
Itu sedikit lebih baik. Ini bekerja secara sempurna di Solaris dan HP-UX. Untuk AIX, ia bekerja di baris perintah tetapi anehnya mulai gagal lagi jika saya mencoba menempatkannya dalam fungsi shell.
Sildoreth
6

KSH_VERSIONtidak diimplementasikan ksh93sebelum versi 93t. Ini akan diatur dalam mksh, pdksh, lksh. Jadi untuk memeriksa versi ksh, kita dapat mencoba langkah-langkah ini:

Dengan ini dalam pikiran, saya akan pergi dengan:

case "$KSH_VERSION" in
  (*MIRBSD*|*PD*|*LEGACY*) printf '%s\n' "$KSH_VERSION" ;;
  (*) [ -z "$ERRNO" ] && printf '%s\n' "${.sh.version}" || echo ksh88/86 ;;
esac
cuonglm
sumber
Mengapa ini tidak memeriksa jika $KSH_VERSIONtidak kosong dulu? Di mesin Ubuntu saya, ini mencetak "ksh93", belum KSH_VERSIONdiatur.
Sildoreth
Ini akan gagal jika beberapa kode yang dieksekusi sebelumnya (misalnya: .kshrc) merusak variabel KSH_VERSION dengan beberapa nilai acak.
jlliagre
@ jlliagre: Tidak, karena dijalankan sebagai skrip, tidak dibaca .kshrc.
cuonglm
Jika ENVvariabel disetel (dan biasanya disetel ke ~/.kshrc), skrip pasti akan membaca .kshrcfile. Tentu saja, itu akan sangat aneh untuk sebuah skrip untuk menetapkan KSH_VERSION palsu tetapi ini tetap mungkin, seperti mengeksekusi skrip secara eksplisit dengan penerjemah yang berbeda dari yang ditentukan pada baris pertama adalah situasi yang memungkinkan.
jlliagre
@ jlliagre: Bahkan Anda bisa mengubahnya, Anda akan mendapatkan segfault ketika Anda merujuk KSH_VERSION. Dan di mksh, pdksh, lksh, KSH_VERSIONditandai sebagai dibaca.
cuonglm
5

Untuk kshrilis "nyata" (yaitu berbasis AT&T), saya menggunakan perintah ini:

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

Berikut adalah berbagai hasil yang saya dapatkan:

Ksh asli:

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

dtksh;

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

Ksh93 modern:

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

Untuk pdksh/ msh kshklon dan kshversi AT&T modern juga, ini adalah sesuatu yang berfungsi:

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

Edit:

Saya mengabaikan Anda bertanya tentang melakukannya dari dalam skrip, bukan dengan mengetahui jalan ke biner ksh diuji.

Dengan asumsi Anda benar-benar menginginkan versi yang kshdigunakan, dan bukan fitur yang didukungnya, berikut adalah salah satu cara untuk melakukannya hanya dengan menggunakan stringsperintah yang seharusnya berfungsi setidaknya di Linux dan 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)

Perhatikan bahwa metode ini tidak dapat diandalkan karena /procmungkin tidak dipasang, dan tentu saja ada kelemahan lainnya. Ini belum teruji pada OS Unix lainnya.

Jlliagre
sumber
Ini tidak akan membedakan antara lkshdan pdkshdi Debian Jessie.
cuonglm
@cuonglm Saya tidak punya Jessie untuk diuji. Apakah maksud Anda lkshdan pdkshtidak dapat disortir dari mereka KSH_VERSION?
jlliagre
Tidak, maksud saya berlari stringspada mereka. KSH_VERSIONpasti bisa.
cuonglm
@cuonglm Maaf jika saya tidak jelas. Ketika saya menulis «untuk kshrilis " nyata " », saya secara eksplisit mengecualikan klon ksh non AT&T seperti pdksh, mkshdan lksh.
jlliagre
Menjalankan stringsbeberapa binary ksh adalah ide yang buruk karena Anda tidak tahu apakah itu yang menjalankan skrip Anda. Mungkin naskah Anda sedang dijalankan oleh /usr/local/bin/kshatau /home/bob/bin/kshatau /bin/shatau /usr/posix/bin/shatau ...
Gilles 'SO berhenti menjadi jahat'
2

Ketika saya sedang menulis skrip ksh, saya perhatikan bahwa -aopsi whenceperintah built-in ksh tampaknya tidak didukung di versi yang lebih lama ksh. Dan ini tampaknya benar pada semua sistem yang saya periksa, termasuk Solaris, AIX, HP-UX, dan Linux.

Jadi, inilah solusinya sebagai fungsi 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
}

Dan inilah cara menggunakannya:

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
sumber
Kenapa tidak kamu gunakan ${.sh.version}?
cuonglm
@cuonglm karena saya tidak bisa. Lihat komentar pada jawaban Gilles .
Sildoreth
sayangnya whencedi Zsh memiliki-a
Greg A. Woods
@ GregA.Woods, fungsi ini khusus untuk ksh. Definisi fungsi akan masuk .kshrc dan bahkan tidak akan ada untuk shell lain seperti zsh. zsh memiliki perintah bawaannya sendiri whenceyang tidak terikat dengan ksh atau versi daripadanya. Saya bahkan tidak tahu mengapa Anda ingin memeriksa apakah ksh adalah versi lama dari instance zsh, yang merupakan shell yang sama sekali berbeda.
Sildoreth
Ada masalah dengan asumsi Anda: Zsh sering diinstal dengan tautan ke /bin/ksh, mis. Di Debian Linux. Sekarang saya tidak menggunakannya di sana (dan saat ini saya tidak dapat mengganti shell login saya untuk memeriksa), jadi saya tidak tahu apakah itu membaca .kshrcatau tidak, tetapi saya menduga itu akan terjadi.
Greg A. Woods
1

CTRL+ ALT+V

atau

ESC, CTRL+V

Biasanya terbukti sangat andal sejauh menentukan secara interaktif versi KSH yang Anda gunakan, namun menulisnya terbukti lebih sulit.

Tim Kennedy
sumber
1
ini adalah satu-satunya yang bekerja untuk versi AIX ksh 88f.
Jeff Schaller
1
Saya mendapatkan opsi <kbd> ESC </kbd>, <kbd> CTRL </kbd> + <kbd> V </kbd> untuk bekerja, setelah saya berlari set -o viuntuk mengatur keybindings menjadi vi-like. Sebelum itu, atau dengan + o vi atau -o emacs, itu tidak akan menunjukkan kepada saya. PD KSH v5.2.14 99/07 / 13.2 di openbsd 6.1
bgStack15
0

Saya pikir masalah mendasar dengan menggunakan $ {. Sh.version} adalah bahwa ksh88 berhenti, dengan kode keluar yang tidak nol.

Jadi solusi saya adalah dengan meletakkan kode yang mereferensikan $ {. Sh.version} dalam sub-shell, kemudian tes untuk melihat apakah sub-shell keluar non-nol dan memiliki kode dalam sub-shell yang akan bekerja pada versi ksh tempat referensi $ {. sh.version} berfungsi. Membungkusnya menjadi fungsi yang kemudian dipanggil oleh fungsi lain yang membalikkan kode kembali, sehingga panggilan terakhir memeriksa true.

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"

Saya telah menjalankan ini pada AIX dan Oracle Enterprise Linux 5 & 6, dengan ksh88, ksh93 dan pdksh.

Pete

Pete
sumber
1
AT&T Ksh modern masih memasok .sh.version(memang KSH_VERSIONefektif alias untuk itu). Juga beberapa shell, misalnya NetBSD sh, cukup berhenti membaca setelah bertemu ${.sh.version}dan tidak ada jumlah pengalihan yang dapat membuat mereka menjalankan skrip.
Greg A. Woods
0

Berikut ini tampaknya bekerja dengan cukup baik untuk semua cangkang yang telah saya uji, termasuk ksh88e lama dan berbagai klon Ksh umum yang hampir lengkap (meskipun masing-masing hanya satu versi), meskipun saya belum menguji cangkang Bourne asli yang sebenarnya ( dan melakukannya mungkin memerlukan mengadaptasi testekspresi untuk versi yang lebih lama ....

Tambahan:

Saya sekarang juga berhasil menguji ini dengan Heirloom Bourne Shell, meskipun dengan program eksternal (dan lebih modern) 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
sumber
Mengapa Anda ingin menjalankan fungsi ini untuk kerang yang bukan ksh? Jika Anda menjalankan skrip dalam bash atau zsh, maka ksh tidak pernah ikut bermain. Lebih jauh lagi, ini sudah dibuat melalui jawaban orang lain yang ${.sh.version}tidak dapat menjadi bagian dari solusi karena versi tertentu dari ksh - versi yang menjadi perhatian postingan asli - mengalami kesalahan fatal pada sintaksis itu.
Sildoreth
Seperti yang saya katakan, fungsi yang saya tunjukkan telah diuji dengan versi ksh yang memberikan kesalahan "fatal", serta versi Ash yang melakukan hal yang sama.
Greg A. Woods
Skrip yang saya tulis dimaksudkan untuk portabel dan dijalankan oleh shell yang mampu. Juga, seperti yang saya katakan di tempat lain, beberapa orang tidak akan selalu tahu mereka menggunakan Zsh sebagai Ksh karena ketika mereka mengetik 'ksh' biner Zsh akan dipanggil (dengan argv [0] sebagai "ksh").
Greg A. Woods
Itu menjelaskan dari mana Anda berasal. Namun, itu terdengar seperti persyaratan yang tidak realistis. Biasanya, ketika pengembang Unix mengatakan "portable", mereka tidak berarti "kode ini akan berjalan di shell apa pun ", mereka berarti "ini akan berjalan pada sistem apa pun ". Dan jika Anda perlu menjalankan skrip yang ditulis untuk shell lain, itu legal; mulai saja instance non-interaktif dari shell lain di skrip Anda. Saya membawa ini karena saya ingin mempromosikan praktik pengkodean yang baik. Jika solusi ini bekerja untuk Anda, bagus. Tetapi saya akan menyarankan orang lain untuk mengambil pendekatan yang lebih sederhana.
Sildoreth
1
Memang segala upaya untuk menyeret mundur kompatibilitas terlalu jauh ke masa lalu agak konyol. Saya hanya mengkompilasi versi AT&T Ksh dan Unix Sh kuno untuk memuaskan keinginan pribadi saya untuk lebih memahami sejarah dan evolusi beberapa fitur dan untuk menyegarkan ingatan saya tentang bagaimana hal-hal itu (yang biasanya mengejutkan saya, karena hal-hal yang sering jauh " lebih baik "daripada yang saya ingat, meskipun kadang-kadang mereka juga jauh lebih buruk).
Greg A. Woods