ทดสอบการรองรับอาร์เรย์โดยเชลล์


12

มีวิธีรัดกุมในการทดสอบเพื่อสนับสนุนอาร์เรย์โดยเชลล์คล้าย Bourne ในบรรทัดคำสั่งหรือไม่

สิ่งนี้เป็นไปได้เสมอ:

$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi

หรือการทดสอบ$SHELLและรุ่นเปลือก:

$ eval $(echo "$SHELL --version") | grep version

จากนั้นอ่าน man page โดยสมมติว่าฉันสามารถเข้าถึงได้ (ถึงตรงนั้น, เขียนจาก/bin/bash, ฉันสมมติว่าเชลล์เหมือนบอร์นทั้งหมดยอมรับตัวเลือกแบบยาว--version, เมื่อแบ่งเช่น ksh เป็นต้น )

ฉันกำลังมองหาการทดสอบอย่างง่ายที่อาจไม่ต้องใส่ข้อมูลและรวมไว้ในส่วนการใช้งานที่จุดเริ่มต้นของสคริปต์หรือแม้กระทั่งก่อนที่จะเรียกมัน


ฉันคิดว่าคุณต้องการ จำกัด เปลือกหอยที่มีลักษณะคล้ายบอร์นหรือไม่?
Stéphane Chazelas

@ StéphaneChazelas: ใช่ถ้าคุณหมายถึง (ไม่ละเอียดถี่ถ้วน) กลุ่มหลักที่ทำจาก: sh, csh, ksh, tcsh, bash, zsh และเพื่อนสนิท ฉันไม่รู้ว่า yash อยู่ตำแหน่งใดในกลุ่มดาวนี้
Cbhihe

2
cshไม่ใช่เปลือก bourne tcshไม่ได้เป็นหนึ่งอย่างใดอย่างหนึ่ง (มันcshมีข้อบกพร่องบางอย่างคงที่)
CAS

1
โปรดทราบว่า$SHELLเป็นเชลล์ที่ต้องการของผู้ใช้ like $EDITORคือเท็กซ์เอดิเตอร์ที่ต้องการของเขา มันมีส่วนเกี่ยวข้องกับเชลล์ที่รันอยู่ในปัจจุบันเล็กน้อย
Stéphane Chazelas

1
evaluating เอาท์พุทของ$SHELL --versionในขณะที่รหัสเปลือกไม่สมเหตุสมผล
Stéphane Chazelas

คำตอบ:


12

สมมติว่าคุณต้องการ จำกัด บอร์นเหมือนเปลือกหอย (หอยอื่น ๆ อีกมากมายชอบcsh, tcsh, rc, esหรือfishอาร์เรย์สนับสนุน แต่การเขียนสคริปต์ที่เข้ากันได้ในเวลาเดียวกันกับบอร์นเหมือนเปลือกหอยและผู้ที่เป็นเรื่องยุ่งยากและไม่มีจุดหมายโดยทั่วไปที่พวกเขาเป็นล่ามสำหรับการที่แตกต่างกันอย่างสมบูรณ์และ ภาษาที่เข้ากันไม่ได้) โปรดทราบว่าการใช้งานมีความแตกต่างอย่างมีนัยสำคัญ

Bourne like shells ที่รองรับอาร์เรย์คือ:

  • ksh88(นั่นเป็นครั้งแรกที่ใช้อาร์เรย์ ksh88 ยังคงพบว่าเป็นkshUnices เชิงพาณิชย์แบบดั้งเดิมส่วนใหญ่ซึ่งเป็นพื้นฐานสำหรับsh)

    • อาร์เรย์เป็นหนึ่งมิติ
    • อาร์เรย์จะถูกกำหนดเป็นset -A array foo barหรือset -A array -- "$var" ...ถ้าคุณไม่สามารถรับประกันได้ว่า$varจะไม่เริ่มต้นด้วยหรือ-+
    • 0ดัชนีอาร์เรย์เริ่มต้นที่
    • a[1]=valueองค์ประกอบมากมายส่วนบุคคลจะถูกกำหนดให้เป็น
    • อาร์เรย์นั้นกระจัดกระจาย นั่นคือการa[5]=fooทำงานแม้ว่าa[0,1,2,3,4]จะไม่ได้ตั้งค่าและจะปล่อยให้ไม่ได้ตั้งค่า
    • ${a[5]}ในการเข้าถึงองค์ประกอบของดัชนี 5 (ไม่จำเป็นต้องเป็นองค์ประกอบที่ 6 หากอาร์เรย์ไม่มีข้อมูล) 5จะมีการแสดงออกใด ๆ ทางคณิตศาสตร์
    • ขนาดอาร์เรย์และตัวห้อยถูก จำกัด (ไปที่ 4096)
    • ${#a[@]} คือจำนวนขององค์ประกอบที่กำหนดในอาร์เรย์ (ไม่ใช่ดัชนีที่กำหนดมากที่สุด)
    • ไม่มีวิธีที่จะรู้รายการของตัวห้อยที่กำหนด (นอกเหนือจากการทดสอบองค์ประกอบ 4096 แต่ละรายการด้วย[[ -n "${a[i]+set}" ]])
    • $a${a[0]}เป็นเช่นเดียวกับ นั่นคืออาร์เรย์ที่จะขยายตัวแปรสเกลาร์ด้วยการให้ค่าพิเศษแก่พวกเขา
  • pdkshและอนุพันธ์ (นั่นคือพื้นฐานสำหรับkshและบางครั้งshของ BSD หลายแห่งและเป็นการนำโอเพนซอร์ส ksh มาใช้เพียงครั้งเดียวก่อนที่จะปล่อยแหล่ง ksh93):

    ส่วนใหญ่ชอบksh88แต่ทราบ:

    • การใช้งานแบบเก่าบางอย่างไม่สนับสนุนset -A array -- foo bar( --ไม่จำเป็นต้องมี)
    • ${#a[@]}เป็นหนึ่งบวกกับดัชนีของดัชนีที่ยิ่งใหญ่ที่สุดที่ได้รับมอบหมาย ( a[1000]=1; echo "${#a[@]}"เอาต์พุต 1,001 แม้ว่าอาร์เรย์จะมีองค์ประกอบเดียวเท่านั้น
    • ในรุ่นที่ใหม่กว่าขนาดอาร์เรย์จะไม่ จำกัด อีกต่อไป (นอกเหนือจากขนาดของจำนวนเต็ม)
    • รุ่นล่าสุดของmkshมีผู้ประกอบการพิเศษไม่กี่แรงบันดาลใจจากbash, ksh93หรือzshชอบที่ได้รับมอบหมายลาa=(x y), a+=(z), ${!a[@]}ที่จะได้รับรายชื่อของดัชนีมอบหมาย
  • zsh. zshอาร์เรย์โดยทั่วไปได้รับการออกแบบที่ดีขึ้นและใช้เวลาที่ดีที่สุดของkshและcshอาร์เรย์ พวกเขาคล้ายกับkshแต่มีความแตกต่างที่สำคัญ:

    • ดัชนีเริ่มต้นที่ 1 ไม่ใช่ 0 (ยกเว้นในkshอีมูเลชัน) ซึ่งสอดคล้องกับอาร์เรย์ Bourne (พารามิเตอร์ตำแหน่ง $ @ ซึ่งzshยังแสดงเป็นอาร์เรย์ $ argv) และcshอาร์เรย์
    • มันเป็นประเภทที่แยกจากตัวแปรปกติ / สเกลาร์ ผู้ให้บริการจะมีความแตกต่างกับพวกเขาและเหมือนกับที่คุณคาดหวัง $aไม่เหมือนกัน${a[0]}แต่ขยายไปยังองค์ประกอบที่ไม่ว่างของอาร์เรย์ ( "${a[@]}"สำหรับองค์ประกอบทั้งหมดที่ต้องการksh)
    • พวกเขาเป็นอาร์เรย์ปกติไม่ใช่อาร์เรย์หร็อมแหร็ม a[5]=1ทำงานได้ แต่กำหนดองค์ประกอบทั้งหมดจาก 1 ถึง 4 สตริงว่างถ้าพวกเขาไม่ได้รับมอบหมาย ดังนั้น${#a[@]}(เช่นเดียวกับ${#a}ใน ksh คือขนาดขององค์ประกอบของดัชนี 0) คือจำนวนขององค์ประกอบในอาร์เรย์และดัชนีที่ได้รับมอบหมายมากที่สุด
    • สนับสนุนอาเรย์แบบเชื่อมโยง
    • รองรับโอเปอเรเตอร์จำนวนมากในการทำงานกับอาร์เรย์มีขนาดใหญ่เกินกว่าจะแสดงได้ที่นี่
    • a=(x y)อาร์เรย์กำหนดให้เป็น set -A a x yใช้งานได้ แต่set -A a -- x yไม่ได้รับการสนับสนุนยกเว้นในการจำลอง ksh ( --ไม่จำเป็นในการจำลอง zsh)
  • ksh93. (ที่นี่อธิบายถึงรุ่นล่าสุด) การทดลองที่มีการksh93พิจารณามานานแล้วสามารถพบได้ในระบบมากขึ้นเรื่อย ๆ ในขณะนี้ซึ่งเผยแพร่เป็น FOSS ยกตัวอย่างเช่นมันเป็น(ที่มันถูกแทนที่ด้วย Bourne เปลือกที่ POSIX เชลล์ยังคงอยู่บนพื้นฐาน) และของ อาร์เรย์ของมันขยายและปรับปรุง ksh88/bin/sh/usr/xpg4/bin/shksh88kshSolaris 11

    • a=(x y)สามารถใช้เพื่อกำหนดอาร์เรย์ แต่เนื่องจากa=(...)ยังใช้เพื่อกำหนดตัวแปรผสม ( a=(foo=bar bar=baz)) a=()เป็นที่กำกวมและประกาศตัวแปรผสมไม่ใช่อาร์เรย์
    • อาร์เรย์เป็นหลายมิติ ( a=((0 1) (0 2))) และองค์ประกอบของอาร์เรย์ยังสามารถเป็นตัวแปรผสมa=((a b) (c=d d=f)); echo "${a[1].c}"ได้
    • a=([2]=foo [5]=bar)ไวยากรณ์สามารถนำมาใช้ในการกำหนดอาร์เรย์เบาบางในครั้งเดียว
    • ยกข้อ จำกัด ด้านขนาด
    • ไม่เพียงzshแต่ผู้ประกอบการจำนวนมากได้รับการสนับสนุนเช่นกันเพื่อจัดการกับอาร์เรย์
    • "${!a[@]}" เพื่อดึงรายการของดัชนีอาเรย์
    • อาร์เรย์ที่เชื่อมโยงได้รับการสนับสนุนเป็นชนิดแยกต่างหาก
  • bash. bashเป็นเปลือกของโครงการ GNU มันใช้เป็นshOS / X รุ่นล่าสุดและการกระจาย GNU / Linux บางส่วน bashอาร์เรย์ส่วนใหญ่เลียนแบบksh88คนที่มีคุณสมบัติบางอย่างของและksh93zsh

    • a=(x y)ได้รับการสนับสนุน. set -A a x y ไม่รองรับ a=()สร้างอาร์เรย์ที่ว่างเปล่า (ไม่มีตัวแปรประกอบbash)
    • "${!a[@]}" สำหรับรายการดัชนี
    • a=([foo]=bar)ไวยากรณ์การสนับสนุนเช่นเดียวกับคนอื่น ๆ ไม่กี่และksh93zsh
    • bashเวอร์ชันล่าสุดยังรองรับอาร์เรย์ที่เชื่อมโยงเป็นชนิดแยกต่างหาก
  • yash. มันเป็นการนำ POSIX sh ไปใช้กับ POSIX sh เมื่อไม่นานมานี้ ไม่ได้ใช้งานอย่างกว้างขวาง อาร์เรย์เป็นอีก API แบบคลีนที่คล้ายกับzsh

    • อาร์เรย์ไม่กระจัดกระจาย
    • ดัชนีอาร์เรย์เริ่มต้นที่ 1
    • นิยาม (และประกาศ) ด้วย a=(var value)
    • องค์ประกอบแทรกลบหรือแก้ไขด้วยarraybuiltin
    • array -s a 5 valueการปรับเปลี่ยน 5 THองค์ประกอบจะล้มเหลวถ้าองค์ประกอบที่ไม่ได้กำหนดไว้ล่วงหน้า
    • จำนวนขององค์ประกอบในอาร์เรย์คือ${a[#]}, ${#a[@]}เป็นขนาดขององค์ประกอบที่เป็นรายการที่
    • อาร์เรย์เป็นชนิดแยกต่างหาก คุณต้องa=("$a")กำหนดตัวแปรสเกลาร์ใหม่เป็นอาร์เรย์ก่อนจึงจะสามารถเพิ่มหรือแก้ไของค์ประกอบได้
    • shอาร์เรย์จะไม่ได้รับการสนับสนุนเมื่อเรียกว่าเป็น

ดังนั้นจากนั้นคุณจะเห็นว่าการตรวจหาการรองรับอาเรย์ซึ่งคุณสามารถทำได้:

if (unset a; set -A a a; eval "a=(a b)"; eval '[ -n "${a[1]}" ]'
   ) > /dev/null 2>&1
then
  array_supported=true
else
  array_supported=false
fi

ไม่เพียงพอที่จะใช้อาร์เรย์เหล่านั้น คุณจะต้องกำหนดคำสั่ง wrapper เพื่อกำหนดอาร์เรย์ให้เป็นองค์ประกอบทั้งตัวและแต่ละส่วนและตรวจสอบให้แน่ใจว่าคุณไม่ได้พยายามสร้างอาร์เรย์แบบกระจาย

ชอบ

unset a
array_elements() { eval "REPLY=\"\${#$1[@]}\""; }
if (set -A a -- a) 2> /dev/null; then
  set -A a -- a b
  case ${a[0]}${a[1]} in
    --) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=0;;
     a) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=1;;
   --a) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
    ab) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
  esac
elif (eval 'a[5]=x') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() { eval "$1[\$2]=\$3"; }
  first_indice=0
elif (eval 'a=(x) && array -s a 1 y && [ "${a[1]}" = y ]') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() {
    eval "
      $1=(\${$1+\"\${$1[@]}"'"})
      while [ "$(($2))" -ge  "${'"$1"'[#]}" ]; do
        array -i "$1" "$2" ""
      done'
    array -s -- "$1" "$((1+$2))" "$3"
   }
  array_elements() { eval "REPLY=\${$1[#]}"; }
  first_indice=1
else
  echo >&2 "Array not supported"
fi

และจากนั้นคุณสามารถเข้าถึงองค์ประกอบมากมายกับ"${a[$first_indice+n]}"รายการทั้งที่มี"${a[@]}"และใช้ฟังก์ชั่นเสื้อคลุม ( array_elements, set_array, set_array_element) เพื่อให้ได้จำนวนขององค์ประกอบของอาร์เรย์ (ใน$REPLY) ตั้งแถวเป็นแต่ละองค์ประกอบทั้งหมดหรือกำหนด

อาจไม่คุ้มค่ากับความพยายาม ฉันต้องการใช้perlหรือการ จำกัด อาร์เรย์บอร์น / POSIX "$@"เปลือก:

หากเจตนาคือให้มีไฟล์บางไฟล์ที่เชลล์แหล่งโต้ตอบของผู้ใช้กำหนดฟังก์ชันที่ใช้อาร์เรย์ภายในต่อไปนี้เป็นหมายเหตุเพิ่มเติมเล็กน้อยที่อาจมีประโยชน์

คุณสามารถกำหนดค่าzshให้อาร์เรย์เป็นเหมือนkshอาร์เรย์ในขอบเขตภายใน (ในฟังก์ชันหรือฟังก์ชันที่ไม่ระบุชื่อ)

myfunction() {
  [ -z "$ZSH_VERSION" ] || setopt localoption ksharrays
  # use arrays of indice 0 in this function
}

คุณยังสามารถจำลองksh(ปรับปรุงความเข้ากันได้กับkshสำหรับอาร์เรย์และพื้นที่อื่น ๆ ) ด้วย:

myfunction() {
  [ -z "$ZSH_VERSION" ] || emulate -L ksh
  # ksh code more likely to work here
}

ด้วยความคิดนั้นและคุณยินดีที่จะลดการสนับสนุนyashและksh88และpdkshอนุพันธ์รุ่นเก่าและตราบใดที่คุณไม่พยายามสร้างอาร์เรย์แบบกระจัดกระจายคุณควรใช้อย่างต่อเนื่อง:

  • a[0]=foo
  • a=(foo bar)(แต่ไม่ใช่a=())
  • "${a[#]}", "${a[@]}","${a[0]}"

ในฟังก์ชั่นเหล่านั้นที่มีในemulate -L kshขณะที่zshผู้ใช้ยังคงใช้อาร์เรย์ของเขา / เธอตามปกติวิธี zsh


7

คุณสามารถใช้evalลองไวยากรณ์อาร์เรย์:

is_array_support() (
  eval 'a=(1)'
) >/dev/null 2>&1

if is_array_support; then
  echo support
else
  echo not
fi

2
ksh88สนับสนุนอาร์เรย์ a=()แต่ไม่ ในksh93, a=()ประกาศตัวแปรสารประกอบไม่อาร์เรย์เว้นแต่ตัวแปรได้รับการประกาศเป็นอาร์เรย์ก่อน
Stéphane Chazelas

2
นอกจากนี้โปรดทราบว่ามีความแตกต่างอย่างมีนัยสำคัญระหว่างการใช้งานอาร์เรย์ ตัวอย่างเช่นบางคนมีดัชนีอาร์เรย์เริ่มต้นที่ 0 (bash, ksh, zsh ในการจำลอง ksh) บางคนเริ่มต้นที่หนึ่ง (zsh, yash) บางรายการเป็นอาร์เรย์ / รายการปกติบางรายการเป็นชุดเบาบาง (ชุดเชื่อมโยงที่มีคีย์ จำกัด จำนวนเต็มบวกเช่นใน ksh หรือ bash)
Stéphane Chazelas

ในyashคุณไม่ต้องทำa[5]=1แต่array -s a 5 1
Stéphane Chazelas

@ StéphaneChazelas: ขอบคุณสำหรับ precisions ในกรณีของฉันทุกสิ่งที่เดือดลงไปไม่ว่าจะเป็นอาร์เรย์ (เชื่อมโยงหรือไม่) ได้รับการสนับสนุนเลย รายละเอียดเกี่ยวกับดัชนีฐานสามารถทำงานได้อย่างง่ายดายแม้ในสคริปต์หมายถึงการทำงานแบบอัตโนมัติ
Cbhihe

@ StéphaneChazelas: ตัวแปรประกอบksh93ทำให้ฉันแปลกใจคุณจะช่วยให้ฉันเป็นส่วนหนึ่งของเอกสารเกี่ยวกับมัน ฉันเพิ่มลง1ในอาร์เรย์เพื่อให้มันใช้งานได้
cuonglm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.