บันทึก
ฉันให้คำตอบที่เน้นแรงมากเพราะ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
.