บันทึก
ฉันให้คำตอบที่เน้นแรงมากเพราะbashแท็ก
คำตอบสั้น ๆ
ตราบใดที่คุณจัดการกับตัวแปรที่มีชื่อใน Bash ฟังก์ชันนี้ควรจะบอกคุณเสมอว่าตัวแปรนั้นถูกตั้งค่าไว้หรือไม่แม้ว่ามันจะเป็นอาร์เรย์ที่ว่างเปล่าก็ตาม
variable-is-set() {
declare -p "$1" &>/dev/null
}
ทำไมถึงใช้งานได้
ใน Bash (อย่างน้อยที่สุดกลับมาที่ 3.0) หากvarเป็นตัวแปรที่ประกาศ / ตั้งค่าจะdeclare -p varแสดงdeclareคำสั่งที่จะตั้งค่าตัวแปรvarตามชนิดและค่าปัจจุบันของมันและส่งคืนรหัสสถานะ0(สำเร็จ) ถ้าvarจะไม่ได้ประกาศแล้วdeclare -p varแสดงข้อความแสดงข้อผิดพลาดไปและรหัสสถานะผลตอบแทนstderr 1ใช้&>/dev/null, เปลี่ยนทิศทางทั้งปกติstdoutและstderrส่งออกไป/dev/nullเป็นไม่เคยเห็นและไม่มีการเปลี่ยนรหัสสถานะ ดังนั้นฟังก์ชั่นจะส่งคืนรหัสสถานะเท่านั้น
สาเหตุที่วิธีการอื่น (บางครั้ง) ล้มเหลวใน Bash
[ -n "$var" ]:สิ่งนี้จะตรวจสอบว่า${var[0]}ไม่มีข้อมูลหรือไม่ (ใน Bash $varเป็นเช่นเดียวกับ${var[0]}.)
[ -n "${var+x}" ]:สิ่งนี้จะตรวจสอบว่า${var[0]}มีการตั้งค่าหรือไม่
[ "${#var[@]}" != 0 ]:สิ่งนี้จะตรวจสอบว่า$varมีการตั้งค่าอย่างน้อยหนึ่งดัชนี
เมื่อวิธีนี้ล้มเหลวใน Bash
นี้จะทำงานเฉพาะสำหรับตัวแปรชื่อ (รวม$_) ตัวแปรพิเศษไม่แน่ใจ ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ... , และที่ฉันอาจจะลืม) เนื่องจากไม่มีสิ่งเหล่านี้เป็นอาร์เรย์สไตล์ POSIX จึงใช้[ -n "${var+x}" ]งานได้กับตัวแปรพิเศษเหล่านี้ทั้งหมด แต่ระวังการห่อไว้ในฟังก์ชั่นเนื่องจากตัวแปรพิเศษจำนวนมากเปลี่ยนค่า / การดำรงอยู่เมื่อเรียกใช้ฟังก์ชั่น
บันทึกการทำงานร่วมกันของเชลล์
ถ้าสคริปต์ของคุณมีอาร์เรย์และคุณกำลังพยายามที่จะทำให้มันเข้ากันได้กับเปลือกหอยให้มากที่สุดเท่าที่เป็นไปได้แล้วพิจารณาใช้แทนtypeset -p declare -pฉันอ่านแล้วว่า ksh รองรับเฉพาะรุ่นก่อนหน้า แต่ไม่สามารถทดสอบได้ ฉันรู้ว่า Bash 3.0+ และ Zsh 5.5.1 แต่ละตัวรองรับทั้งคู่typeset -pและdeclare -pต่างกันเพียงอันเดียวที่เป็นตัวเลือกสำหรับอีกอันหนึ่ง แต่ฉันไม่ได้ทดสอบความแตกต่างจากคำหลักสองคำเหล่านั้นและฉันยังไม่ได้ทดสอบเชลล์อื่น ๆ
หากคุณต้องการให้สคริปต์ของคุณใช้งานร่วมกับ POSIX ได้คุณจะไม่สามารถใช้อาร์เรย์ได้ โดยไม่ต้องอาร์เรย์[ -n "{$var+x}" ]ทำงาน
รหัสเปรียบเทียบสำหรับวิธีการต่างๆใน Bash
ฟังก์ชั่นนี้จะยกเลิกการตั้งค่าตัวแปรvarซึ่งevalเป็นรหัสที่ผ่านแล้วทำการทดสอบเพื่อตรวจสอบว่าvarมีการตั้งค่าด้วยevalรหัส d หรือไม่และสุดท้ายจะแสดงรหัสสถานะที่ได้สำหรับการทดสอบที่แตกต่างกัน
ฉันกระโดดข้ามtest -v var, [ -v var ]และ[[ -v var ]]เพราะพวกเขาให้ผลลัพธ์ที่เหมือนกันกับมาตรฐาน POSIX [ -n "${var+x}" ]ในขณะที่ต้องทุบตีเวอร์ชัน 4.2 ขึ้นไป ฉันยังข้ามtypeset -pเพราะมันเหมือนกับdeclare -pเชลล์ที่ฉันทดสอบ (Bash 3.0 ถึง 5.0 และ Zsh 5.5.1)
is-var-set-after() {
# Set var by passed expression.
unset var
eval "$1"
# Run the tests, in increasing order of accuracy.
[ -n "$var" ] # (index 0 of) var is nonempty
nonempty=$?
[ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
plus=$?
[ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
count=$?
declare -p var &>/dev/null # var has been declared (any type)
declared=$?
# Show test results.
printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}
รหัสกรณีทดสอบ
โปรดทราบว่าผลการทดสอบอาจไม่คาดคิดเนื่องจาก Bash ใช้ดัชนีอาร์เรย์ที่ไม่ใช่ตัวเลขเป็น "0" หากตัวแปรไม่ได้ถูกประกาศเป็นอาร์เรย์ที่เชื่อมโยง นอกจากนี้อาร์เรย์ที่เชื่อมโยงจะใช้ได้ใน Bash 4.0+ เท่านั้น
# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo" # 0 0 0 0
is-var-set-after "var=(foo)" # 0 0 0 0
is-var-set-after "var=([0]=foo)" # 0 0 0 0
is-var-set-after "var=([x]=foo)" # 0 0 0 0
is-var-set-after "var=([y]=bar [x]=foo)" # 0 0 0 0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''" # 1 0 0 0
is-var-set-after "var=([0]='')" # 1 0 0 0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')" # 1 1 0 0
is-var-set-after "var=([1]=foo)" # 1 1 0 0
is-var-set-after "declare -A var; var=([x]=foo)" # 1 1 0 0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()" # 1 1 1 0
is-var-set-after "declare -a var" # 1 1 1 0
is-var-set-after "declare -A var" # 1 1 1 0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var" # 1 1 1 1
ทดสอบผลลัพธ์
จำการทดสอบในตรงส่วนหัวแถว[ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ]และdeclare -p varตามลำดับ
test: -n +x #@ -p
var=foo: 0 0 0 0
var=(foo): 0 0 0 0
var=([0]=foo): 0 0 0 0
var=([x]=foo): 0 0 0 0
var=([y]=bar [x]=foo): 0 0 0 0
var='': 1 0 0 0
var=([0]=''): 1 0 0 0
var=([1]=''): 1 1 0 0
var=([1]=foo): 1 1 0 0
declare -A var; var=([x]=foo): 1 1 0 0
var=(): 1 1 1 0
declare -a var: 1 1 1 0
declare -A var: 1 1 1 0
unset var: 1 1 1 1
สรุป
declare -p var &>/dev/null (100%) น่าเชื่อถือสำหรับการทดสอบตัวแปรที่มีชื่อใน Bash ตั้งแต่อย่างน้อย 3.0
[ -n "${var+x}" ] เชื่อถือได้ในสถานการณ์ที่สอดคล้องกับ POSIX แต่ไม่สามารถจัดการอาร์เรย์ได้
- มีการทดสอบอื่นเพื่อตรวจสอบว่าตัวแปรไม่ว่างเปล่าหรือไม่และตรวจสอบตัวแปรที่ประกาศในเชลล์อื่น ๆ แต่การทดสอบเหล่านี้เหมาะสำหรับสคริปต์ Bash หรือ POSIX
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.