ฉันจะตรวจสอบว่ามีตัวแปรอยู่ในคำสั่ง 'if' ได้อย่างไร?


69

ฉันต้องการตรวจสอบการมีอยู่ของตัวแปรในifคำสั่ง สิ่งที่มีผลต่อ:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

และคำถามที่ใกล้เคียงที่สุดคือคำถามนี้ซึ่งไม่ได้ตอบคำถามของฉัน


หากคุณต้องการตั้งค่า$somevarให้เป็นค่า / ${somevar:=42}สตริงถ้าตัวแปรไม่มีอยู่:
Cyrus

โดยส่วนตัวแล้วฉันมักจะตรวจสอบความว่างเปล่า ( [ -n "$var" ]หรือ[ ! -z "$var" ]) ฉันคิดว่าการตรวจสอบการมีอยู่ / ไม่มีอยู่นั้นบอบบางเกินไปและฉันชอบรหัสที่หยาบและเรียบง่าย
PSkocik

ทำไมคุณต้องว่า VS [ -n "$var" ]? ที่เกี่ยวข้อง: stackoverflow.com/questions/3601515/ …
Ciro Santilli 新疆改造中心法轮功六四事件

คำตอบ:


97

ในทุบตีที่ทันสมัย ​​(รุ่น 4.2 ขึ้นไป):

[[ -v name_of_var ]]

จากhelp test:

-v VAR จริงถ้าตัวแปรเชลล์ VAR ถูกตั้งค่า


3
[ -v name_of_var ]ยังทำงานร่วมกับวงเล็บเดียว:
meuh

7
ระวังว่าสำหรับแฮชและอาร์เรย์มันจะคืนค่าเท็จเว้นแต่ตัวแปรจะมีองค์ประกอบของคีย์ / ดัชนี "0" สำหรับ namerefs จะทดสอบว่ามีการกำหนดเป้าหมายหรือไม่ มันไม่ทำงานสำหรับพารามิเตอร์พิเศษเช่น$1, $-, $#...
Stéphane Chazelas

4
คุณลักษณะนี้มีเฉพาะใน bash builtin testหรือ[; /usr/bin/testมันไม่ได้มีอยู่ใน เปรียบเทียบกับman test help test
Mark Lakata

@MarkLakata ถูกต้องเนื่องจากคำสั่งภายนอกไม่สามารถรู้สถานะภายในของเชลล์ได้
Chris Down

อืมมันไม่ควรจะเป็น [[-v "$ name_of_var"]] หรือไม่
Alexander Mills

24

ขึ้นอยู่กับสิ่งที่คุณหมายถึงอยู่

ไม่ตัวแปรที่ได้รับการประกาศ แต่ไม่ได้รับมอบหมายอยู่ ?

ตัวแปรอาเรย์ (หรือแฮช) ที่กำหนดให้มีรายการว่างอยู่หรือไม่

มีตัวแปร nameref ที่ชี้ไปที่ตัวแปรที่ไม่ได้กำหนดไว้ในปัจจุบันหรือไม่?

คุณพิจารณา$-, $#, $1ตัวแปร? (POSIX ไม่ได้)

ในกระสุนคล้ายบอร์นวิธีบัญญัติคือ:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

ใช้งานได้กับตัวแปรสเกลาร์และพารามิเตอร์อื่น ๆ เพื่อบอกว่าตัวแปรได้รับการกำหนดค่า (ว่างเปล่าหรือไม่, โดยอัตโนมัติ, จากสภาพแวดล้อม, การreadกำหนดforหรืออื่น ๆ )

สำหรับเปลือกหอยที่มีtypesetหรือdeclareคำสั่งที่จะไม่รายงานเป็นตั้งค่าตัวแปรที่ได้รับการประกาศแต่ไม่ได้รับมอบหมายzshยกเว้นใน

สำหรับเชลล์ที่สนับสนุนอาร์เรย์ยกเว้นyashและzshที่จะไม่รายงานว่าเป็นตัวแปรชุดอาร์เรย์ยกเว้นว่าองค์ประกอบของดัชนี 0 ได้รับการตั้งค่าแล้ว

สำหรับbash(แต่ไม่ใช่ksh93หรือzsh) สำหรับตัวแปรของอาเรย์แบบเชื่อมโยงประเภทที่จะไม่รายงานพวกเขาตามที่ตั้งค่าไว้เว้นแต่ว่าองค์ประกอบของคีย์ "0" ได้รับการตั้งค่าแล้ว

สำหรับksh93และbashสำหรับตัวแปรประเภทnamerefว่ามีเพียงผลตอบแทนจริงถ้าตัวแปรอ้างอิงโดย nameref เป็นตัวเองถือว่าเป็นชุด

สำหรับksh, zshและbashเป็นวิธีการที่ดีกว่าอาจจะเป็น:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

สำหรับksh93, zshและbash4.4 หรือสูงกว่านอกจากนี้ยังมี:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

ซึ่งจะรายงานตัวแปรที่ได้รับการตั้งค่าหรือประกาศ


1
declare -p/ typeset -pทำงานในbashตอนนี้ด้วย
cas

1
@cas ไม่มี ไม่ใช่สำหรับการประกาศ แต่ไม่ได้ตั้งค่าตัวแปร ลองbash -c 'typeset -i a; typeset -p a'และเปรียบเทียบกับหรือksh93 zsh
Stéphane Chazelas

+1 ขอบคุณที่มาหาฉันที่นี่ ยังเห็นคำถามของฉันunix.stackexchange.com/q/280893/674
Tim

@cas, ที่เปลี่ยนด้วย bash-4.4 (เปิดตัวในเดือนกันยายน 2559) ฉันได้แก้ไขสิ่งต่อไปนี้แล้ว
Stéphane Chazelas

9

ดังที่ได้กล่าวไว้ในคำตอบของSO ต่อไปนี้เป็นวิธีตรวจสอบ:

if [ -z ${somevar+x} ]; then echo "somevar is unset"; else echo "somevar is set to '$somevar'"; fi

โดยที่ $ {somevar + x} คือการขยายพารามิเตอร์ที่ประเมินเป็นโมฆะถ้า var ไม่มีการตั้งค่าและแทนที่สตริง "x" เป็นอย่างอื่น

การใช้-nตามคำแนะนำของคำตอบอื่น ๆ จะตรวจสอบว่าตัวแปรมีสตริงว่างหรือไม่ มันจะไม่ตรวจสอบการมีอยู่ของมัน


2
คุณจำเป็นต้องพูดในการจัดการ$somevar ทั้งที่หรือใบเสนอราคาIFS=x x
mikeserv

1
@mikeserv ขอบคุณฉันชอบเรียนรู้เกี่ยวกับกรณีขอบ :) คุณหมายถึงif [ -z "${somevar+x}" ]อะไร จะ quoting ยังคงต้องอยู่ภายใน[[และ]]?
Tom Hale

@ TomHale - ใช่ในบางกรณี รูทีน[ testยอมรับพารามิเตอร์บรรทัดคำสั่งและดังนั้นการขยายและการตีความตามปกติตามที่สั่งในวิธีปกติควรถูกใช้เพื่อเรนเดอร์เมื่อทำการร้องขอการทดสอบที่ใช้ซึ่งคุณควรทำให้อ่านได้ด้วยบรรทัดคำสั่งของโปรแกรม ทดสอบ {! + "!"}
mikeserv

@mikeserv ฉันเดาว่าใช่คือคำถามที่ 2 ของฉัน ... ใช่หรือไม่เป็นคนแรกใช่ไหม?
Tom Hale

1
+1; สิ่งนี้ดูเหมือนจะเป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้เมื่อset -uมีผลบังคับใช้และเวอร์ชั่น Bash เป็นเวอร์ชั่น 4.2 ล่วงหน้า
Kyle Strand

4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

หรือคุณสามารถให้เชลล์แสดงข้อความให้คุณ:

(: "${somevar?}")
zsh: somevar: parameter not set

@mikeserv: ใช่แน่นอนมีหลายวิธีที่จะทำ ประการแรกผมคิดว่าฉันจะทำซ้ำด้วยนี้ แต่ในคำถามนี้ OP ต้องการตรวจสอบเท่านั้นเขาไม่ได้อ้างว่าเขาต้องการออกหรือรายงานหากตัวแปรไม่มีการตั้งค่าดังนั้นฉันจึงมาพร้อมกับเช็คอินย่อย
cuonglm

2
ฉันรู้ แต่มันแม่นยำเพราะคุณต้องทำใน subshell แบบที่บ่งบอกว่ามันอาจไม่ใช่วิธีที่ดีที่สุดในการทดสอบที่นี่ - นั่นเป็นการหยุดการกระทำที่ล้มเหลวไม่อนุญาตให้มีวิธีการง่ายๆ ความล้มเหลว. สามารถพกพาได้แม้กระทั่งtrapEXIT เท่านั้น นั่นคือทั้งหมดที่ฉันพูด - มันไม่ได้ใช้เป็นผ่าน / ล้มเหลวได้เป็นอย่างดี และนี่ไม่ใช่ฉันกำลังพูดถึง - ฉันได้ทำสิ่งนี้มาก่อนและใช้การแชทแสดงความคิดเห็นเล็กน้อยเช่นนี้เพื่อโน้มน้าวฉัน ดังนั้นฉันแค่คิดว่าฉันจะจ่ายไปข้างหน้า
mikeserv

1
@mikeserv: ดีก็เป็นจุดที่ดี ฉันสงสัยไม่เพิ่มที่สามารถทำให้สับสน OP กับไวยากรณ์ :)
cuonglm

2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi

ฉันคิดว่ามันจะไม่มีประสิทธิภาพ แต่มันง่ายและshเข้ากันได้ซึ่งเป็นสิ่งที่ฉันต้องการ
hoijui

2
printf ${var+'$var exists!\n'}

... จะไม่พิมพ์อะไรเลยเมื่อไม่มี หรือ...

printf $"var does%${var+.}s exist%c\n" \ not !

... จะบอกวิธีใด

คุณสามารถใช้ค่าส่งคืนของการทดสอบเพื่อขยายเป็นสตริงรูปแบบที่เหมาะสมสำหรับเงื่อนไขของคุณ:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

คุณสามารถทำให้printfล้มเหลวโดยอิงจากการทดแทน ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... ที่พิมพ์$var does not exist!ไปยัง stderr และส่งกลับค่าอื่นที่ไม่ใช่ 0 เมื่อ$varไม่ได้ตั้งค่า แต่พิมพ์$var does exist!ไปที่ stdout และส่งกลับ 0 เมื่อ$varตั้งค่า


1

บรรทัดง่าย ๆ นี้ใช้งานได้ (และทำงานบนเชลล์ POSIX ส่วนใหญ่):

${var+"false"} && echo "var is unset"

หรือเขียนในรูปแบบที่ยาวกว่า:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

การขยายตัวคือ:

  • หาก var มีค่า (เป็นโมฆะ) จะมีการแทนที่ค่าเท็จ
  • หาก var มี "no value" ดังนั้น "no value" (null) จะถูกแทนที่

การ${var+"false"}ขยายตัวขยายเป็น "null" ของ "false"
จากนั้นจะเรียกใช้งาน "nothing" หรือ "false" และชุดรหัสออก

ไม่จำเป็นต้องเรียกคำสั่งtest( [หรือ[[) เนื่องจากค่าการออกถูกกำหนดโดย (การดำเนินการ) การขยายตัวเอง


ใช่ยกเว้น zsh บางเวอร์ชันในแบบจำลองการจำลองเมื่อ$IFSมี f, a, l, s หรือ e เช่นเดียวกับคำตอบอื่น ๆ มีกรณีของอาร์เรย์อาร์เรย์แฮชหรือตัวแปรชนิดอื่น ๆ ที่หนึ่งคนอาจต้องการพูดถึง
Stéphane Chazelas

@ most POSIX shellsStéphaneChazelasผมเขียน most หมายความว่าIn the greatest number of instancesไม่ใช่ทั้งหมด ... ... ดังนั้นใช่ในสภาพที่คลุมเครือwhen $IFS contains f, a, l, s or eและสำหรับเปลือกที่คลุมเครือsome old versions of zshสิ่งนี้จะล้มเหลว: ช่างเป็นเรื่องที่น่าตกใจ! ฉันควรสมมติว่าข้อผิดพลาดดังกล่าวได้รับการแก้ไขนานแล้ว ... ... คุณกำลังเสนอว่าเราต้องเขียนโค้ดสำหรับกระสุนที่ชำรุดมานานแล้วหรือไม่?

@ StéphaneChazelasยัง: ชื่อคำถามที่เฉพาะเจาะจงมาก: Bash

ม้าลายไบนารี ???
mikeserv

0

วิธีเปลือกบริสุทธิ์:

[ "${var+1}" ] || echo "The variable has not been set"

สคริปต์ทดสอบ:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

ผล:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done

1
ล้มเหลวเมื่อตัวแปรตั้งค่าเป็นสตริงว่าง
Tom Hale

0

ด้วย bash 4.4.19 ต่อไปนี้ใช้งานได้สำหรับฉัน นี่คือตัวอย่างที่สมบูรณ์

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi

-1

คุณไม่สามารถใช้ifคำสั่งเพื่อตรวจสอบการมีอยู่ของตัวแปรที่ประกาศใน bash แต่-vมีตัวเลือกใน bash ที่ใหม่กว่า แต่มันไม่สามารถพกพาได้และคุณไม่สามารถใช้มันในbashเวอร์ชั่นที่เก่ากว่าได้ เพราะเมื่อคุณใช้ตัวแปรหากไม่มีอยู่มันจะเกิดขึ้นพร้อมกัน

เช่นลองนึกภาพว่าฉันไม่ได้ใช้หรือกำหนดค่าให้กับMYTESTตัวแปร แต่เมื่อคุณใช้คำสั่ง echo มันจะไม่แสดงอะไรเลย! หรือถ้าคุณใช้if [ -z $MYTEST ]มันคืนค่าเป็นศูนย์! ไม่ได้ส่งคืนสถานะการออกอื่นซึ่งบอกคุณว่าตัวแปรนี้ไม่มีอยู่จริง!

ตอนนี้คุณมีสองวิธี (ไม่มี-vตัวเลือก):

  1. การใช้declareคำสั่ง
  2. การใช้setคำสั่ง

ตัวอย่างเช่น:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

แต่น่าเสียดายที่คำสั่งเหล่านี้แสดงฟังก์ชันที่คุณโหลดในหน่วยความจำด้วย! คุณสามารถใช้declare -p | grep -q MYTEST ; echo $?คำสั่งเพื่อผลลัพธ์ที่สะอาดขึ้น


-1

ฟังก์ชั่นเพื่อตรวจสอบว่ามีการประกาศตัวแปร / unset

รวมถึงที่ว่างเปล่า $array=()


นอกจากคำตอบของ @ Gilles

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

- ซึ่งฉันไม่ได้หาวิธีที่จะแค็ปซูลภายในฟังก์ชั่น - ฉันต้องการเพิ่มเวอร์ชันที่เรียบง่ายซึ่งส่วนหนึ่งขึ้นอยู่กับคำตอบของRichard Hansenแต่ที่อยู่ยังเป็นข้อผิดพลาดที่เกิดขึ้นกับที่ว่างเปล่า:array=()

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • โดยการทดสอบครั้งแรกหากมีการตั้งค่าตัวแปร (un) การเรียกเพื่อประกาศสามารถหลีกเลี่ยงได้หากไม่จำเป็น
  • อย่างไรก็ตามหาก$1มีชื่อว่าง$array=()การเรียกร้องให้ประกาศจะทำให้แน่ใจว่าเราได้รับผลลัพธ์ที่ถูกต้อง
  • ไม่มีข้อมูลมากถูกส่งผ่านไปยัง / dev / null เนื่องจากการประกาศจะถูกเรียกใช้เฉพาะถ้าตัวแปรไม่ได้ถูกตั้งค่าไว้หรืออาร์เรย์ว่างเปล่า


ด้วยรหัสต่อไปนี้ฟังก์ชั่นสามารถทดสอบได้:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

สคริปต์ควรกลับมา

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

-1

ฟังก์ชั่นทุบตีที่ทำงานได้ทั้งเกลาและประเภทอาเรย์ :

คำนิยาม

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

การภาวนา

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.