Làm thế nào tôi có thể có được phiên bản của ksh một cách an toàn?


12

Làm cách nào tôi có thể lấy phiên bản ksh một cách an toàn từ trong tập lệnh ksh?

Tôi đã thấy các giải pháp sau đây :

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

Và đưa ra các trường hợp phù hợp, mỗi trong số này hoạt động chính xác. Tuy nhiên, tôi quan tâm đến trường hợp không hoàn hảo.

Cụ thể, có một số máy tôi làm việc với các phiên bản cũ hơn của ksh, với mục đích của tôi, thiếu nghiêm trọng về chức năng. Dù sao, lý do tôi muốn kiểm tra phiên bản (lập trình) là để xem phiên bản ksh có phải là một trong những phiên bản ít khả năng hơn không; và nếu vậy, tôi muốn thực thi một nhánh với mã ít tuyệt vời hơn.

Tuy nhiên, trên các máy có vấn đề, sự bất lực của vỏ mở rộng đến việc kiểm tra phiên bản ...

  • Nếu tôi thử ksh --version, nó không in gì cả và mở một ví dụ mới ksh!
  • Nếu tôi thử echo ${.sh.version}, kshcoi đây là lỗi cú pháp không thể loại bỏ 2> /dev/null.

    $ echo ${.sh.version} 2> /dev/null  
    ksh: ${.sh.version}: bad substitution
  • Tất nhiên có echo $KSH_VERSIONvẻ hoạt động tốt - ý tôi là nó sẽ không gặp sự cố - mặc dù trên các máy này nó trống. Ngoài ra, tôi thấy một nơi nào đó chỉ KSH_VERSIONđược thiết lập bởi pdksh.

Câu hỏi:

  • Làm thế nào tôi có thể kiểm tra phiên bản kshlập trình một cách an toàn ? Đối với mục đích của tôi ở đây, tôi không thực sự quan tâm số phiên bản thực tế là gì, chỉ là liệu đó có phải là phiên bản lỗi thời hay không ksh.
  • $KSH_VERSIONđủ tốt? Ý tôi là nếu nó trống, thì kshnhất thiết phải là một phiên bản lỗi thời? Có phải diễn đàn khác đã đúng rằng nó có thể không được đặt ngay cả đối với các phiên bản mới hơn ksh?
  • Có cách nào để kiểm tra điều này không?

1
Bất kỳ lý do nào bạn muốn có hai đường dẫn mã và không chỉ một đường dẫn với mã ít tuyệt vời hơn?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen nó phải làm với lời nhắc. Trong tệp .kshrc của tôi, tôi có một chức năng mô phỏng chức năng viết tắt pwd của lời nhắc tcsh và zsh và tôi thiết lập PS1để sử dụng chức năng này. Tuy nhiên, Old ksh không hỗ trợ $()trong PS1. Vì vậy, nếu đó là phiên bản hiện đại của ksh, tôi muốn PS1sử dụng chức năng tôi đã tạo; Nếu đó là phiên bản cũ, tôi chỉ sử dụng $PWD.
Sildoreth

Chà, bạn có thể có hai phiên bản của tệp cấu hình của mình (có thể là một phiên bản được tạo từ cái kia) và sau đó phân phối phiên bản phù hợp cho máy đang đề cập?
Thorbjørn Ravn Andersen

Một cách tiếp cận khác có thể chỉ đơn giản là nói "Chỉ có máy cụ thể này có vấn đề - tôi sẽ tìm thấy một biến tệp hoặc môi trường hoặc một cái gì đó chỉ tồn tại ở đây (có thể là AIX hoặc một cái gì đó) và thử nghiệm thay thế".
Thorbjørn Ravn Andersen

Câu trả lời:


7

Tôi nghĩ rằng nó .sh.versionđã tồn tại kể từ phiên bản đầu tiên của ATT ksh 93. Nó không có sẵn trong pdksh hoặc mksh. Vì ${.sh.version}là một lỗi cú pháp trong các shell khác với ksh93, hãy bọc thử nghiệm cho nó trong một lớp con và bảo vệ nó phía sau 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 bắt đầu trong bản sao công khai ksh clone (pdksh), và đã được thêm vào vỏ Korn thực tế tương đối gần đây, vào năm 2008 với ksh93t.

Thay vì kiểm tra số phiên bản, bạn nên kiểm tra tính năng cụ thể khiến bạn đau buồn. Hầu hết các tính năng có thể được kiểm tra bằng cách thử một số cấu trúc trong một lớp con và xem nếu nó gây ra lỗi.


Tôi không thấy bất kỳ sự khác biệt khi sử dụng một subshell. Nó vẫn xử lý ${.sh.version}như một lỗi cú pháp không thể khắc phục được. Tin nhắn tôi nhận được là bad substitution.
Sildoreth

@sil Điểm của việc sử dụng một subshell là để bắt lỗi. Chuyển hướng lỗi đến /dev/nullvà bỏ qua trạng thái thoát.
Gilles 'SO- ngừng trở thành ác quỷ'

Tôi hiểu những gì bạn đang nói. Điều tôi đang nói là lỗi không chuyển hướng. Nó luôn in ra bàn điều khiển. Tôi đã thử điều này trong Solaris, AIX và HP-UX; và ksh thể hiện hành vi này trong tất cả chúng.
Sildoreth

@Sildoreth Ah. Tôi chỉ thử nghiệm trên Linux và hiện tại tôi chưa có hệ điều hành nào để thử nghiệm. Có eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullhoạt động tốt hơn không?
Gilles 'SO- ngừng trở nên xấu xa'

Điều đó tốt hơn một chút. Nó hoạt động hoàn hảo trong Solaris và HP-UX. Đối với AIX, nó hoạt động ở dòng lệnh nhưng tò mò bắt đầu thất bại một lần nữa nếu tôi cố gắng đặt nó trong một hàm shell.
Sildoreth

6

KSH_VERSIONđã không được thực hiện ksh93trước phiên bản 93t. Nó sẽ được thiết lập trong mksh, pdksh, lksh. Vì vậy, để kiểm tra phiên bản của ksh, chúng ta có thể thử các bước sau:

  • Kiểm tra KSH_VERSIONđể phát hiện mksh, pdksh,lksh
  • Nếu bước đầu tiên thất bại, hãy thử một tính năng khác biệt giữa ksh93ksh88/86( Hãy để David Korn chỉ cho chúng tôi ).

Với những điều này trong tâm trí, tôi sẽ đi với:

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

Tại sao không kiểm tra này nếu $KSH_VERSIONkhông trống đầu tiên? Trên máy Ubuntu của tôi, bản in này "ksh93", vẫn KSH_VERSIONđược đặt.
Sildoreth

Điều này sẽ thất bại nếu một số mã được thực thi trước đó (ví dụ: .kshrc) giả mạo biến KSH_VERSION với một số giá trị ngẫu nhiên.
jlliagre

@jlliagre: Không, vì nó được chạy như một kịch bản, nó không đọc .kshrc.
cuonglm

Nếu ENVbiến được đặt (và thông thường nó được đặt thành ~/.kshrc), tập lệnh chắc chắn sẽ đọc .kshrctệp. Tất nhiên, sẽ khá kỳ lạ khi một tập lệnh đặt một KSH_VERSION không có thật nhưng điều này vẫn có thể xảy ra, giống như việc thực thi một cách rõ ràng một tập lệnh với một trình thông dịch khác với tập lệnh được chỉ định trong dòng đầu tiên là một tình huống có thể xảy ra.
jlliagre

@jlliagre: Ngay cả bạn có thể thay đổi nó, bạn sẽ nhận được segfault khi bạn tham khảo KSH_VERSION. Và trong mksh, pdksh, lksh, KSH_VERSIONđược đánh dấu là chỉ đọc.
cuonglm

5

Đối với các kshbản phát hành "thực" (tức là dựa trên AT & T), tôi sử dụng lệnh này:

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

Đây là đầu ra khác nhau tôi nhận được:

Bản gốc ksh:

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

dtksh;

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

Ksh93 hiện đại:

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

Đối với pdksh/ msh kshbản sao và các kshphiên bản AT & T hiện đại cũng vậy, đây là một cái gì đó hoạt động:

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

Biên tập:

Tôi bỏ qua bạn đang hỏi về việc thực hiện nó từ bên trong một tập lệnh, không phải bằng cách biết đường dẫn đến nhị phân ksh được thử nghiệm.

Giả sử bạn thực sự muốn phiên bản đã kshsử dụng chứ không phải các tính năng mà nó hỗ trợ, đây là một cách để làm điều đó bằng cách chỉ sử dụng stringslệnh nên hoạt động ít nhất trên Linux và 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)

Lưu ý rằng phương pháp này không đáng tin cậy vì /proccó thể không được gắn kết, và chắc chắn có những điểm yếu khác. Nó chưa được kiểm tra trên các hệ điều hành Unix khác.


Điều này sẽ không được phân biệt giữa lkshpdkshtrong Debian Jessie.
cuonglm

@cuonglm Mình không có bài kiểm tra. Bạn có nghĩa là lkshpdkshkhông thể được sắp xếp từ họ KSH_VERSION?
jlliagre

Không, ý tôi là chạy stringstrên chúng. KSH_VERSIONdứt khoát có thể.
cuonglm

@cuonglm Xin lỗi nếu tôi không rõ ràng. Khi tôi viết «cho các kshbản phát hành " thực " », tôi đã loại trừ rõ ràng các bản sao không phải AT & T ksh như pdksh, mkshlksh.
jlliagre

Chạy stringstrên một số nhị phân ksh là một ý tưởng tồi bởi vì bạn không biết liệu đó có phải là chạy tập lệnh của bạn không. Có lẽ kịch bản của bạn đang được chạy bởi /usr/local/bin/kshhoặc /home/bob/bin/kshhoặc /bin/shhoặc /usr/posix/bin/shhoặc ...
Gilles 'Somali dừng vốn là xấu'

2

Trong khi tôi đang viết một kịch bản cho ksh, tôi nhận thấy rằng -atùy chọn lệnh tích hợp của ksh whencedường như không được hỗ trợ trong các phiên bản cũ hơn ksh. Và điều này dường như đúng trên tất cả các hệ thống tôi đã kiểm tra, bao gồm Solaris, AIX, HP-UX và Linux.

Vì vậy, đây là giải pháp như một hàm 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
}

Và đây là cách sử dụng nó:

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

Tại sao bạn không sử dụng ${.sh.version}?
cuonglm

@cuonglm vì tôi không thể. Xem các bình luận về câu trả lời của Gilles .
Sildoreth

Thật không may, whencetrong Zsh có-a
Greg A. Woods

@ GregA.Woods, chức năng này dành riêng cho ksh. Định nghĩa hàm sẽ đi vào .kshrc và do đó thậm chí sẽ không tồn tại cho các shell khác như zsh. zsh có whencelệnh tích hợp riêng của nó mà không có cách nào gắn với ksh hoặc phiên bản của nó. Tôi thậm chí không biết tại sao bạn quan tâm kiểm tra xem ksh có phải là phiên bản cũ từ trong phiên bản zsh không, đây là một vỏ hoàn toàn khác.
Sildoreth

Có một vấn đề với các giả định của bạn: Zsh thường được cài đặt với một liên kết đến /bin/ksh, ví dụ như trên Debian Linux. Bây giờ tôi không sử dụng nó ở đó (và hiện tại tôi không thể thay đổi vỏ đăng nhập của mình để kiểm tra), vì vậy tôi không biết nó có đọc .kshrchay không, nhưng tôi sẽ nghi ngờ điều đó.
Greg A. Woods

1

CTRL+ ALT+V

hoặc là

ESC, CTRL+V

Thông thường đã được chứng minh là rất đáng tin cậy khi xác định tương tác phiên bản của KSH bạn đang sử dụng, tuy nhiên việc tạo kịch bản cho chúng đã được chứng minh là khó khăn hơn.


1
đây là chiếc duy nhất hoạt động cho phiên bản AIX ksh 88f.
Jeff Schaller

1
Tôi đã có tùy chọn <kbd> ESC </ kbd>, <kbd> CTRL </ kbd> + <kbd> V </ kbd> để hoạt động, sau khi tôi chạy set -o viđể đặt các phím bấm giống như vi. Trước đó, hoặc với + o vi hoặc -o emacs, đơn giản là nó sẽ không hiển thị cho tôi. PD KSH v5.2.14 99/07 / 13.2 trên openbsd 6.1
bgStack15

0

Tôi nghĩ vấn đề cơ bản khi sử dụng $ {. Sh.version} là ksh88 chỉ dừng lại, với mã thoát khác không.

Vì vậy, giải pháp của tôi là đặt mã tham chiếu $ {. Shversion. ksh nơi tham chiếu $ {. sh.version} không hoạt động. Gói nó vào một hàm sau đó được gọi bởi một hàm khác đảo ngược mã trả về, để cuộc gọi cuối cùng đang kiểm tra đúng.

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"

Tôi đã chạy nó trên AIX và Oracle Enterprise Linux 5 & 6, với ksh88, ksh93 và pdksh.

Pete


1
AT & T Ksh hiện đại vẫn cung cấp .sh.version(thực sự KSH_VERSIONlà một bí danh cho nó). Ngoài ra, một số shell, ví dụ như NetBSD sh, chỉ cần dừng đọc sau khi gặp phải ${.sh.version}và không có lượng chuyển hướng nào có thể giữ cho chúng chạy tập lệnh.
Greg A. Woods

0

Các tính năng sau đây có vẻ hoạt động khá tốt đối với tất cả các vỏ mà tôi đã thử nghiệm, bao gồm cả ksh88e cũ và một loạt các bản sao Ksh phổ biến gần như hoàn chỉnh (mặc dù chỉ có một phiên bản của mỗi loại), mặc dù tôi chưa thử nghiệm vỏ Bourne ban đầu thực sự ( và làm như vậy có thể yêu cầu điều chỉnh testbiểu thức cho các phiên bản cũ hơn ....

Phụ lục:

Bây giờ tôi cũng đã thử nghiệm thành công điều này với Heirloom Bourne Shell, mặc dù với chương trình bên ngoài (và hiện đại hơn) 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"

Tại sao bạn muốn chạy chức năng này cho các shell không phải là ksh? Nếu bạn đang chạy một tập lệnh trong bash hoặc zsh, thì ksh không bao giờ phát huy tác dụng. Hơn nữa, nó đã được thiết lập thông qua các câu trả lời của người khác ${.sh.version}không thể là một phần của giải pháp vì các phiên bản nhất định của ksh - phiên bản mà bài đăng gốc quan tâm - lỗi nghiêm trọng về cú pháp đó.
Sildoreth

Như tôi đã nói, chức năng tôi hiển thị đã được thử nghiệm với các phiên bản ksh có lỗi "nghiêm trọng", cũng như các phiên bản của Ash cũng làm như vậy.
Greg A. Woods

Các kịch bản tôi viết được dự định là di động và được chạy bởi bất kỳ trình bao có khả năng nào. Ngoài ra, như tôi đã nói ở nơi khác, một số người không nhất thiết phải biết họ đang sử dụng Zsh là Ksh vì khi họ gõ 'ksh', nhị phân Zsh sẽ được gọi (với argv [0] là "ksh").
Greg A. Woods

Điều đó làm sáng tỏ nơi bạn đến. Tuy nhiên, điều đó nghe có vẻ như là một yêu cầu không thực tế. Thông thường, khi một nhà phát triển Unix nói "di động", họ không có nghĩa là "mã này sẽ chạy trong bất kỳ hệ vỏ nào ", họ có nghĩa là "mã này sẽ chạy trên bất kỳ hệ thống nào ". Và nếu bạn cần thực thi một tập lệnh được viết cho shell khác, điều đó là hoàn toàn hợp pháp; chỉ bắt đầu một phiên bản không tương tác của shell khác trong tập lệnh của bạn. Tôi đưa ra điều này bởi vì tôi muốn thúc đẩy thực hành mã hóa tốt. Nếu giải pháp này làm việc cho bạn, tuyệt vời. Nhưng tôi sẽ khuyên những người khác nên có một cách tiếp cận đơn giản hơn.
Sildoreth

1
Phải thừa nhận rằng bất kỳ nỗ lực nào để kéo khả năng tương thích ngược quá xa vào quá khứ là khá ngớ ngẩn. Tôi chỉ biên soạn các phiên bản AT & T Ksh và Unix Sh cổ đại để thỏa mãn mong muốn cá nhân của riêng tôi để hiểu rõ hơn về lịch sử và sự phát triển của một số tính năng và để làm mới trí nhớ của tôi về mọi thứ (thường làm tôi ngạc nhiên, vì mọi thứ thường rất nhiều " tốt hơn "tôi nhớ, mặc dù đôi khi họ cũng tệ hơn nhiều).
Greg A. Woods
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.