ฉันจะรับรุ่น 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_VERSIONpdksh

คำถาม:

  • ฉันจะตรวจสอบเวอร์ชั่นของkshโปรแกรมอย่างปลอดภัยได้อย่างไร kshสำหรับวัตถุประสงค์ของฉันที่นี่ฉันไม่สนใจสิ่งที่หมายเลขรุ่นที่เกิดขึ้นจริงเป็นเพียงแค่ไม่ว่าจะเป็นรุ่นเก่า
  • คือ$KSH_VERSIONพอดีหรือไม่? ฉันหมายถึงถ้ามันว่างเปล่าkshจำเป็นต้องเป็นรุ่นที่ล้าสมัยหรือไม่? ฟอรัมอื่นถูกต้องหรือไม่ที่อาจไม่ได้ตั้งไว้สำหรับรุ่นที่ใหม่กว่าksh?
  • ไม่มีวิธีตรวจสอบเลยใช่ไหม?

1
ด้วยเหตุผลใดก็ตามที่คุณต้องการเส้นทางของรหัสสองเส้นทางและไม่ใช่เพียงเส้นทางเดียวที่มีรหัสที่ยอดเยี่ยมน้อยกว่า
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersenเกี่ยวข้องกับพรอมต์ ในไฟล์. kshrc ของฉันฉันมีฟังก์ชั่นที่จำลองฟังก์ชั่น pwd-abbreviating ของพรอมต์ tcsh และ zsh และฉันตั้งค่าPS1ให้ใช้ฟังก์ชั่นนี้ อย่างไรก็ตาม ksh เก่าไม่สนับสนุนใน$() PS1ดังนั้นหากเป็นรุ่นที่ทันสมัยของ ksh ฉันต้องการPS1ใช้ฟังก์ชันที่ฉันสร้างขึ้น $PWDถ้าหากมันเป็นรุ่นเก่าผมใช้เพียง
Sildoreth

คุณอาจมีไฟล์กำหนดค่าสองเวอร์ชัน (อาจเป็นรุ่นที่สร้างจากรุ่นอื่น) จากนั้นแจกจ่ายเวอร์ชันที่เหมาะสมไปยังเครื่องที่สงสัย
Thorbjørn Ravn Andersen

วิธีอื่นอาจกล่าวได้ง่ายๆว่า "เป็นเพียงเครื่องนี้โดยเฉพาะที่มีปัญหา - ฉันจะหาไฟล์หรือตัวแปรสภาพแวดล้อมหรืออย่างอื่นที่มีอยู่ที่นี่เท่านั้น (อาจเป็น AIX หรือบางอย่างอยู่แล้ว) และทดสอบแทน"
Thorbjørn Ravn Andersen

คำตอบ:


7

ฉันคิดว่า.sh.versionมีอยู่นับตั้งแต่ ATT ksh 93 รุ่นแรกมันไม่สามารถใช้ได้ใน pdksh หรือ mksh เนื่องจาก${.sh.version}เป็นข้อผิดพลาดทางไวยากรณ์ในเปลือกหอยอื่น ๆ กว่า ksh93 ห่อทดสอบสำหรับใน subshell 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 clone (pdksh) และถูกเพิ่มลงใน Korn เชลล์ที่เกิดขึ้นจริงค่อนข้างเร็ว ๆ นี้ในปี 2008 ด้วย ksh93t

แทนที่จะทดสอบหมายเลขรุ่นคุณควรทดสอบคุณลักษณะเฉพาะที่ทำให้คุณเศร้าโศก คุณสมบัติส่วนใหญ่สามารถทดสอบได้โดยการลองสร้างในเชลล์ย่อยและดูว่ามันก่อให้เกิดข้อผิดพลาด


ฉันไม่เห็นความแตกต่างเมื่อใช้ subshell มันยังคงถือว่า${.sh.version}เป็นข้อผิดพลาดทางไวยากรณ์ที่ไม่สามารถกระทบยอด bad substitutionข้อความที่ฉันได้รับคือ
Sildoreth

@sil จุดประสงค์ในการใช้เชลล์ย่อยคือการตรวจจับข้อผิดพลาด เปลี่ยนเส้นทางข้อผิดพลาด/dev/nullและละเว้นสถานะการออก
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

ฉันเข้าใจสิ่งที่คุณพูด สิ่งที่ฉันพูดคือข้อผิดพลาดไม่เปลี่ยนเส้นทาง มันมักจะพิมพ์ไปยังคอนโซล ฉันลองสิ่งนี้ใน Solaris, AIX, และ HP-UX; และ ksh แสดงพฤติกรรมนี้ในทุกคน
Sildoreth

@Sildoreth Ah ฉันเพิ่งทดสอบบน Linux และฉันไม่มี OS เหล่านี้เพื่อทดสอบเลย ไม่eval '_sh_version=$(echo "${.sh.version}")' 2>/dev/nullทำงานใด ๆ ที่ดีกว่า?
Gilles 'ดังนั้นหยุดความชั่วร้าย'

มันดีขึ้นเล็กน้อย มันทำงานได้อย่างยอดเยี่ยมใน Solaris และ HP-UX สำหรับ AIX มันทำงานที่บรรทัดคำสั่ง แต่อยากรู้อยากเห็นเริ่มล้มเหลวอีกครั้งหากฉันพยายามวางไว้ในฟังก์ชั่นเชลล์
Sildoreth

6

KSH_VERSIONไม่ได้ใช้งานksh93ก่อนหน้าเวอร์ชัน 93t ก็จะมีการตั้งอยู่ในmksh, ,pdksh lkshดังนั้นสำหรับการตรวจสอบเวอร์ชั่นของkshเราสามารถลองขั้นตอนเหล่านี้:

ด้วยความคิดเหล่านี้ฉันจะไปกับ:

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

dtksh;

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

ทันสมัย ​​ksh93:

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

สำหรับpdksh/ msh kshโคลนและรุ่น AT&T ที่ทันสมัยkshด้วยนี่คือสิ่งที่ใช้ได้:

$ 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ตัวเลือกของการ 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

ทำไมคุณไม่ใช้${.sh.version}?
cuonglm

@cuonglm เพราะฉันทำไม่ได้ ดูความคิดเห็นในคำตอบที่กิลส์
Sildoreth

น่าเสียดายที่whenceใน Zsh มี-a
Greg A. Woods

@ 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
Jeff Schaller

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} ใน sub-shell จากนั้นทดสอบเพื่อดูว่า sub-shell ออกจากศูนย์แล้วและมีโค้ดใน sub-shell ที่จะทำงานกับเวอร์ชั่นของ 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 บอร์นเชลล์แม้ว่าภายนอก (และทันสมัยมากขึ้น) 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 บางรุ่น - รุ่นที่โพสต์ดั้งเดิมมีความกังวล - ข้อผิดพลาดร้ายแรงในไวยากรณ์นั้น
Sildoreth

ดังที่ฉันได้กล่าวไปแล้วฟังก์ชั่นที่ฉันแสดงนั้นได้รับการทดสอบด้วยเวอร์ชันของ ksh ที่ให้ข้อผิดพลาด "ถึงตาย" เช่นเดียวกับ Ash รุ่นที่ทำเช่นเดียวกัน
เกร็กเอ. วูดส์

สคริปต์ที่ฉันเขียนมีไว้เพื่อให้พกพาและใช้งานโดยเชลล์ที่มีความสามารถ นอกจากนี้อย่างที่ฉันพูดไว้ที่อื่นบางคนไม่จำเป็นต้องรู้ว่าพวกเขากำลังใช้ Zsh เป็น Ksh เพราะเมื่อพวกเขาพิมพ์ 'ksh' ไบนารี Zsh จะถูกเรียกใช้ (โดย argv [0] เป็น "ksh")
เกร็กเอ. วูดส์

นั่นจะทำให้คุณรู้ว่าคุณมาจากไหน อย่างไรก็ตามดูเหมือนว่าจะเป็นข้อกำหนดที่ไม่สมจริง โดยทั่วไปเมื่อนักพัฒนา Unix บอกว่า "พกพา" พวกเขาไม่ได้หมายความว่า "โค้ดนี้จะทำงานในเชลล์ใด ๆ" พวกเขาหมายถึง "สิ่งนี้จะทำงานบนระบบใด ๆ" และถ้าคุณต้องการรันสคริปต์ที่เขียนขึ้นสำหรับเชลล์อีกตัวนั่นถือว่าถูกกฎหมาย เพียงแค่เริ่มต้นอินสแตนซ์ที่ไม่มีการโต้ตอบของเชลล์อื่นในสคริปต์ของคุณ ฉันนำสิ่งนี้มาเพราะฉันต้องการส่งเสริมการเขียนโปรแกรมที่ดี หากวิธีนี้เหมาะกับคุณมาก แต่ฉันจะแนะนำผู้อื่นให้ใช้วิธีที่ง่ายกว่า
Sildoreth

1
ความพยายามใด ๆ ที่จะดึงความเข้ากันได้ย้อนหลังที่ผ่านมาที่ผ่านมามากเกินไปนั้นค่อนข้างโง่ ฉันได้รวบรวมรุ่นโบราณ AT&T Ksh และ Unix Sh เพื่อสนองความต้องการส่วนตัวของฉันเองเพื่อทำความเข้าใจประวัติศาสตร์และวิวัฒนาการของคุณสมบัติบางอย่างและเพื่อฟื้นฟูความทรงจำของฉันว่าสิ่งต่าง ๆ เป็นอย่างไร (ซึ่งมักจะทำให้ฉันประหลาดใจ ดีกว่า "ฉันจำได้ แต่บางครั้งมันก็แย่กว่ามาก)
เกร็กเอ. วูดส์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.