ทดสอบสตริงที่มีความยาวไม่เป็นศูนย์ใน Bash: [-n“ $ var”] หรือ [“ $ var”]


182

ฉันเคยเห็นสคริปต์ Bash ทดสอบสตริงที่มีความยาวไม่เป็นศูนย์ในสองวิธีที่แตกต่างกัน สคริปต์ส่วนใหญ่ใช้-nตัวเลือก:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

แต่ตัวเลือก -n ไม่จำเป็นต้องใช้:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

วิธีไหนดีกว่ากัน?

ในทำนองเดียวกันซึ่งเป็นวิธีที่ดีกว่าสำหรับการทดสอบความยาวเป็นศูนย์:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

หรือ

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi

คำตอบ:


394

แก้ไข:นี้เป็นรุ่นที่สมบูรณ์มากขึ้นที่ความแตกต่างระหว่างการแสดงมากขึ้น[(aka test) [[และ

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

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

หากคุณต้องการทราบว่าตัวแปรมีความยาวไม่เป็นศูนย์หรือไม่ให้ทำสิ่งใดสิ่งหนึ่งต่อไปนี้:

  • พูดตัวแปรในวงเล็บเดียว (คอลัมน์ 2a)
  • ใช้-nและอ้างอิงตัวแปรในวงเล็บเดียว (คอลัมน์ 4a)
  • ใช้วงเล็บคู่ที่มีหรือไม่มีข้อความและมีหรือไม่มี-n(คอลัมน์ 1b - 4b)

โปรดสังเกตในคอลัมน์ 1a เริ่มต้นที่แถวที่มีป้ายกำกับ "สอง" ที่ผลลัพธ์บ่งชี้ว่า[กำลังประเมินเนื้อหาของตัวแปรราวกับว่าพวกเขาเป็นส่วนหนึ่งของนิพจน์เงื่อนไข (ผลลัพธ์ตรงกับการยืนยันโดยนัยโดย "T" หรือ "F" ใน คอลัมน์คำอธิบาย) เมื่อ[[ใช้ (คอลัมน์ 1b) เนื้อหาตัวแปรจะถูกมองว่าเป็นสตริงและไม่ได้รับการประเมิน

ข้อผิดพลาดในคอลัมน์ 3a และ 5a เกิดจากข้อเท็จจริงที่ว่าค่าตัวแปรรวมถึงช่องว่างและตัวแปรไม่ได้ถูกอ้างอิง อีกครั้งดังที่แสดงในคอลัมน์ 3b และ 5b [[ประเมินเนื้อหาของตัวแปรเป็นสตริง

ตามลำดับสำหรับการทดสอบสตริงที่มีความยาวเป็นศูนย์คอลัมน์ 6a, 5b และ 6b แสดงวิธีที่ถูกต้องในการทำเช่นนั้น นอกจากนี้โปรดทราบว่าการทดสอบใด ๆ เหล่านี้สามารถถูกปฏิเสธได้หากปฏิเสธแสดงเจตนาที่ชัดเจนมากกว่าการใช้การปฏิบัติตรงข้าม ตัวอย่างเช่นif ! [[ -n $var ]].

หากคุณกำลังใช้[กุญแจสำคัญในการตรวจสอบให้แน่ใจว่าคุณไม่ได้รับผลลัพธ์ที่ไม่คาดคิดคือการอ้างอิงตัวแปร ใช้[[มันไม่สำคัญ

ข้อความแสดงข้อผิดพลาดซึ่งกำลังถูกระงับคือ "ตัวดำเนินการเอกนารีที่คาดไว้" หรือ "ตัวดำเนินการแบบไบนารีที่คาดหวัง"

นี่คือสคริปต์ที่สร้างตารางด้านบน

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF

1
ขอบคุณ! ฉันตัดสินใจที่จะใช้รูปแบบของ "อ้างอิงตัวแปรในวงเล็บเดียว (คอลัมน์ 2a)" IMO, -n เพิ่งเพิ่มเสียงรบกวนและลดความสามารถในการอ่าน ในทำนองเดียวกันสำหรับการทดสอบความยาวเป็นศูนย์หรือไม่ได้ตั้งค่าฉันจะใช้ [! "$ var"] แทน [-z "$ var"]
AllenHalsey

2
ดังนั้นแผนภูมิของคุณสำหรับ["vs [-n"(คำถามแรกของ OP) แสดงให้เห็นว่าพวกเขามีความเท่าเทียมกันโดยสมบูรณ์ใช่มั้ย
เตาแก๊ส

1
@hobs: ใช่และสิ่งที่ใช้ขึ้นอยู่กับว่ามีความชัดเจนมากขึ้น คุณจะสังเกตเห็นได้ว่าพวกเขาเทียบเท่าเมื่อใช้แบบฟอร์มวงเล็บคู่ซึ่งเป็นที่ต้องการเมื่อใช้ Bash
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
ดังนั้นเพื่อสรุปตารางที่ยอดเยี่ยมของคุณวิธีที่ชัดเจนกว่า ("ดีกว่า") ในการทดสอบสตริงที่ไม่เป็นนัลคือ[["
hobs

22

มันเป็นการดีที่จะใช้พลังมากขึ้น [[เท่าที่ Bash เกี่ยวข้อง

กรณีปกติ

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

โครงสร้างทั้งสองข้างต้นดูสะอาดตาและอ่านง่าย พวกเขาควรจะพอเพียงในกรณีส่วนใหญ่

โปรดทราบว่าเราไม่จำเป็นต้องพูดการขยายตัวแปรภายใน[[เนื่องจากไม่มีอันตรายจากการแยกคำและการโค้งงอ

เพื่อป้องกันไม่ให้shellcheck 's การร้องเรียนเกี่ยวกับการอ่อน[[ $var ]]และ[[ ! $var ]]เราสามารถใช้-nตัวเลือก

กรณีที่หายาก

ในกรณีที่เกิดขึ้นได้ยากเราต้องแยกความแตกต่างระหว่าง "การตั้งค่าเป็นสตริงว่าง" และ "ไม่ได้ตั้งค่าเลย" เราสามารถใช้สิ่งเหล่านี้:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

เรายังสามารถใช้การ-vทดสอบ:

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

โพสต์และเอกสารที่เกี่ยวข้อง

มีโพสต์มากมายที่เกี่ยวข้องกับหัวข้อนี้ นี่คือบางส่วน:


9

นี่คือการทดสอบเพิ่มเติมบางส่วน

จริงถ้าสตริงไม่ว่าง:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

เป็นจริงถ้าสตริงว่างเปล่า:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"

นี่ไม่ได้ตอบคำถามของ OP ว่าอะไรคือวิธีที่ดีกว่า
codeforester

0

คำตอบที่ถูกต้องคือ:

if [[ -n $var ]] ; then
  blah
fi

สังเกตการใช้งานของ[[...]]ซึ่งจะจัดการกับการอ้างอิงตัวแปรสำหรับคุณอย่างถูกต้อง


2
ทำไมต้องใช้-nเมื่อไม่จำเป็นต้องใช้ใน Bash?
codeforester

0

ทางเลือกและวิธีที่โปร่งใสกว่าในการประเมินตัวแปรสภาพแวดล้อมที่ว่างเปล่าคือการใช้ ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi

10
นี่เป็นโรงเรียนเก่าจากสมัยเชลล์บอร์น อย่าทำให้เป็นนิสัยเก่านี้ bashเป็นเครื่องมือที่คมชัดกว่ารุ่นก่อน
tbc0

2
คุณไม่จำเป็นต้องใช้สิ่งนี้ด้วย pre-bash shells หากคุณหลีกเลี่ยงไวยากรณ์ที่มาตรฐาน POSIX ทำเครื่องหมายว่าล้าสมัยอย่างชัดเจน [ "$ENV_VARIABLE" != "" ]จะทำงานกับเชลล์ทุกตัวที่มีการติดตั้งที่เป็นไปตาม POSIX testไม่ใช่เพียงแค่ทุบตี แต่เป็น Ash / dash / ksh / etc
ชาร์ลส์ดัฟฟี่

... ที่จะกล่าวว่าอย่าใช้-aหรือ-oรวมการทดสอบ แต่แทนที่จะใช้[ ... ] && [ ... ]หรือ[ ... ] || [ ... ]และในกรณีที่มีเพียงมุมเดียวที่สามารถนำไปใช้กับการใช้ข้อมูลของตัวแปรที่กำหนดเองในการทดสอบนั้นปิดอย่างไม่น่าสงสัย
ชาร์ลส์ดัฟฟี่

-2

ใช้case/esacในการทดสอบ:

case "$var" in
  "") echo "zero length";;
esac

12
ไม่ได้โปรด นี่ไม่ใช่สิ่งที่caseมีไว้สำหรับ
tbc0

2
caseทำงานได้ดีที่สุดเมื่อมีทางเลือกมากกว่าสองทาง
codeforester

caseไม่ได้มีไว้สำหรับการใช้งานประเภทนี้
Luis Lavaire
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.