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


16

สำหรับเวอร์ชัน Bash ก่อนหน้า "GNU bash เวอร์ชัน 4.2" มีทางเลือกอื่นใดเทียบเท่าสำหรับ-vตัวเลือกของtestคำสั่งหรือไม่ ตัวอย่างเช่น:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo

-vไม่ใช่ตัวเลือกtestแต่เป็นโอเปอเรเตอร์สำหรับนิพจน์ที่มีเงื่อนไข
ทิม

@ Tim มันเป็นสิ่งที่สามข้างเป็นโทเค็น, สตริงและเป็นส่วนหนึ่งของเส้น: การoptionที่จะสั่งtest -vการoperatorไปconditional expressionและสำหรับunary test primary [ ]อย่าผสมภาษาอังกฤษกับคำจำกัดความของเชลล์

คำตอบ:


36

พกพาไปยังเปลือก POSIX ทั้งหมด:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

ทำเช่นนั้น${foobar:+1}หากคุณต้องการที่จะปฏิบัติfoobarในลักษณะเดียวกันไม่ว่าจะว่างเปล่าหรือไม่ได้กำหนดไว้ นอกจากนี้คุณยังสามารถใช้${foobar-}เพื่อรับสตริงว่างเมื่อfoobarไม่ได้กำหนดและค่าเป็นfoobarอย่างอื่น (หรือใส่ค่าเริ่มต้นอื่น ๆ หลังจากนั้น-)

ใน ksh ถ้าfoobarถูกประกาศ แต่ไม่ได้กำหนดเช่นเดียวกับในtypeset -a foobarแล้ว${foobar+1}ขยายไปยังสตริงที่ว่างเปล่า

Zsh ไม่มีตัวแปรที่ประกาศ แต่ไม่ได้ตั้งค่า: typeset -a foobarสร้างอาร์เรย์ว่าง

ในทุบตีอาร์เรย์ทำงานในวิธีที่แตกต่างและน่าแปลกใจ ${a+1}จะขยายเป็น1ถ้าaไม่ใช่อาร์เรย์ว่างเท่านั้นเช่น

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

หลักการเดียวกันนี้ใช้กับอาเรย์แบบเชื่อมโยง: ตัวแปรอาเรย์จะได้รับการปฏิบัติตามที่กำหนดไว้หากมีดัชนีที่ไม่ว่างเปล่า

วิธีที่แตกต่างและเฉพาะเจาะจงในการทดสอบว่ามีการกำหนดตัวแปรชนิดใดหรือไม่เพื่อตรวจสอบว่ามีการระบุไว้หรือไม่ รายงานนี้อาร์เรย์ว่างเปล่าตามที่กำหนดซึ่งแตกต่างจากแต่รายงานตัวแปรที่ประกาศ แต่ไม่ได้กำหนด ( ) เป็นไม่ได้กำหนด${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac

นี่เทียบเท่ากับการทดสอบค่าส่งคืนของtypeset -p foobarหรือdeclare -p foobarยกเว้นว่าtypeset -p foobarล้มเหลวกับตัวแปรที่ประกาศ แต่ไม่ได้กำหนด

ในทุบตีเหมือนใน ksh, ก่อให้เกิดข้อผิดพลาดในความพยายามที่จะขยายตัวแปรไม่ได้กำหนดset -o nounset; typeset -a foobar; echo $foobar foobarไม่เหมือนกับใน ksh set -o nounset; foobar=(); echo $foobar(หรือecho "${foobar[@]}") นอกจากนี้ยังก่อให้เกิดข้อผิดพลาด

โปรดทราบว่าในทุกสถานการณ์อธิบายไว้ที่นี่${foobar+1}จะขยายสตริงที่ว่างเปล่าและถ้าหากจะทำให้เกิดข้อผิดพลาดภายใต้$foobarset -o nounset


1
แล้วอาร์เรย์ล่ะ? ในเวอร์ชัน Bash "GNU bash เวอร์ชัน 4.1.10 (4) - ปล่อย (i686-pc-cygwin)" echo "${foobar:+1}"ไม่ได้พิมพ์1หากdeclare -a foobarออกมาก่อนหน้านี้และfoobarเป็นอาร์เรย์ที่ทำดัชนี รายงานได้อย่างถูกต้องdeclare -p foobar declare -a foobar='()'ไม่"${foobar:+1}"ทำงานเฉพาะสำหรับตัวแปรที่ไม่ใช่อาร์เรย์?
ทิม Friske

@TimFriske ${foobar+1}(หากไม่มี:, ฉันกลับสองตัวอย่างในคำตอบเดิมของฉัน) ถูกต้องสำหรับอาร์เรย์ในทุบตีถ้าคำจำกัดความของคุณของ "กำหนด" เป็น "จะ$foobarทำงานภายใต้set -o nounset" หากคำจำกัดความของคุณแตกต่าง bash จะค่อนข้างแปลก ดูคำตอบที่อัปเดตของฉัน
Gilles 'หยุดความชั่วร้าย'

1
เกี่ยวกับหัวข้อ "ใน bash อาร์เรย์ทำงานในลักษณะที่แตกต่างและน่าประหลาดใจ" พฤติกรรมสามารถอธิบายได้จาก bash (1) manpages, ส่วน "Array" มันระบุว่า "การอ้างอิงตัวแปรอาร์เรย์ที่ไม่มีตัวห้อยจะเท่ากับการอ้างอิงอาร์เรย์ที่มีตัวห้อย 0" ดังนั้นหากทั้ง0ดัชนีมิได้สำคัญถูกกำหนดให้เป็นมันเป็นความจริงสำหรับa=(), ${a+1}อย่างถูกต้องผลตอบแทนอะไร
ทิม Friske

1
@TimFriske ฉันรู้ว่าการใช้ bash นั้นสอดคล้องกับเอกสารของมัน แต่การรักษาอาเรย์ที่ว่างเปล่าเหมือนตัวแปรที่ไม่ได้กำหนดเป็นการออกแบบที่แปลกจริงๆ
Gilles 'หยุดความชั่วร้าย'

ฉันชอบผลลัพธ์จาก: จะตรวจสอบว่าตัวแปรถูกตั้งค่าเป็น Bash ได้อย่างไร? -> วิธีที่ถูกต้อง ... ฉันนัดคอร์ดด้วยสิ่งที่ฉันควรจะทำงาน กล้าพูดว่า Windows มีdefinedโอเปอเรเตอร์ Test สามารถทำอย่างนั้น; มันไม่ยากหรอก ( อืม .... )
จะ

6

เพื่อสรุปคำตอบของ Gilles ฉันได้ทำตามกฎต่อไปนี้:

  1. ใช้[[ -v foobar ]]สำหรับตัวแปรใน Bash version> = 4.2
  2. ใช้declare -p foobar &>/dev/nullสำหรับตัวแปรอาร์เรย์ในเวอร์ชัน Bash <4.2
  3. ใช้(( ${foo[0]+1} ))หรือ(( ${bar[foo]+1} ))สำหรับห้อยของดัชนี ( -a) และคีย์ ( -A) อาร์เรย์ ( declare) ตามลำดับ ตัวเลือก 1 และ 2 ไม่ทำงานที่นี่

3

ฉันใช้เทคนิคเดียวกันสำหรับตัวแปรทั้งหมดใน bash และใช้งานได้เช่น:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

เอาท์พุท:

foobar is unset

ในขณะที่

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

เอาท์พุท:

foobar is set

ต้องลบ [@] ถ้าอาร์เรย์มีค่ามากกว่าหนึ่งค่า
Stein Inge Morisbak

1
วิธีนี้ใช้งานได้ดีตราบใดที่คุณต้องการทดสอบว่ามีค่าหรือไม่ เช่นนั้นจะรายงานว่าfoobar="" foobar is unsetไม่ต้องรอฉันรับมันกลับมา ทำการทดสอบจริง ๆ ว่าองค์ประกอบแรกนั้นว่างเปล่าหรือไม่เท่านั้นดังนั้นจึงเป็นความคิดที่ดีถ้าคุณรู้ว่าตัวแปรไม่ใช่อาร์เรย์และคุณสนใจเฉพาะความว่างเปล่าไม่ใช่ความชัดเจน
Ron Burk

ทำงานเฉพาะถ้าสคริปต์ของคุณกำลังทำงานอยู่กับตัวแปรที่ไม่ได้กำหนดที่ได้รับอนุญาต (ไม่มีชุด -u)
Florian Heigl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.