ฉันจะตรวจสอบว่าตัวแปรมีอยู่ในรายการใน BASH ได้อย่างไร


137

ฉันพยายามเขียนสคริปต์ใน bash ที่ตรวจสอบความถูกต้องของอินพุตของผู้ใช้
ฉันต้องการจับคู่อินพุต (พูดตัวแปรx) กับรายการค่าที่ถูกต้อง

สิ่งที่ฉันเกิดขึ้นในขณะนี้คือ:

for item in $list
do
    if [ "$x" == "$item" ]; then
        echo "In the list"
        exit
    fi
done

คำถามของฉันคือถ้ามีวิธีที่ง่ายกว่าในการทำสิ่งนี้
เช่นlist.contains(x)ภาษาโปรแกรมภาษา

เพิ่มเติม:
พูดรายการคือ:

list="11 22 33"

รหัสของฉันจะสะท้อนข้อความสำหรับค่าเหล่านั้นเท่านั้นเนื่องจากlistถือว่าเป็นอาร์เรย์และไม่ใช่สตริงการจัดการสตริงทั้งหมดจะตรวจสอบความถูกต้อง1ในขณะที่ฉันต้องการให้มันล้มเหลว

คำตอบ:


143
[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'

หรือสร้างฟังก์ชั่น:

contains() {
    [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}

ที่จะใช้มัน:

contains aList anItem
echo $? # 0: match, 1: failed

37
ควรเป็น[[ $list =~ (^| )$x($| ) ]] && echo 'yes' || echo 'no'
Matvey Aksenov

12
อาจให้ผลบวกเป็นเท็จหากอินพุตของผู้ใช้มีอักขระพิเศษในนิพจน์ทั่วไปตัวอย่างเช่นx=.
glenn jackman

4
นี้จะไม่ให้ผลบวกปลอม / contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }เชิงลบ:
skozin

6
ทางออกที่กระชับยิ่งขึ้น: [[ " $list " =~ " $x " ]] && echo 'yes' || echo 'no'. มันถูกต้องสมมติว่าพื้นที่คือตัวคั่นและ$xไม่มีช่องว่าง
Tianren Liu

2
ฉันคิดว่าการใช้ฟังก์ชั่น "is in" ดีกว่าเพื่อisIn()ให้คุณสามารถเขียนรายการแรกในพารามิเตอร์ นอกจากนี้คุณสามารถechoบางสิ่งบางอย่างแทนการใช้exitเช่นนี้: [[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0ดังนั้นคุณสามารถใช้ฟังก์ชั่นแบบนี้:result=$(isIn "-t" "-o -t 45") && echo $result
hayj

34

Matvey นั้นถูกต้อง แต่คุณควรอ้างอิง $ x และพิจารณา "ช่องว่าง" ใด ๆ (เช่นบรรทัดใหม่) ด้วย

[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 

ดังนั้นคือ

# list_include_item "10 11 12" "2"
function list_include_item {
  local list="$1"
  local item="$2"
  if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then
    # yes, list include item
    result=0
  else
    result=1
  fi
  return $result
}

จบแล้ว

`list_include_item "10 11 12" "12"`  && echo "yes" || echo "no"

หรือ

if `list_include_item "10 11 12" "1"` ; then
  echo "yes"
else 
  echo "no"
fi

โปรดทราบว่าคุณต้องใช้""ในกรณีของตัวแปร:

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"

3
วิธีนี้ใช้ได้ผลแม้ว่าจะ$itemมีตัวอักษรพิเศษเช่น.(แต่$listอาจต้องใช้ตัวแปรในการทดสอบ) contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }และฟังก์ชั่นอาจจะกำหนดได้ง่าย:
skozin

ไม่ทำงานในกรณีเช่น: list="aa bb xx cc ff"และx="aa bb"
creativeChips

สวัสดี. ฉันพยายามที่จะเรียนรู้พื้นฐาน bashscript และฉันต้องการที่จะรู้วิธีที่คุณสามารถแยกสตริงของรายการลงในรายการและคุณทำการตรวจสอบที่มีอยู่ในรายการนั้น ฉันสามารถเข้าใจคุณแยกจากกันโดยใช้พื้นที่ แต่ฉันไม่เข้าใจการแสดงออกอย่างเต็มที่ คุณสามารถให้ฉันคำหลักไปยัง Google $list =~ (^|[[:space:]])"$item"($|[[:space:]])พื้นฐานของการแสดงออกนี้: หรือถ้าคุณมีเวลาฉันยินดีที่จะฟังคำอธิบายของคุณสำหรับการแสดงออกนี้ หมายเหตุ: ฉันคิดว่ามันเป็นนิพจน์ regex (เริ่มต้นด้วย ^ ฯลฯ ) แต่ฉันไม่รู้ว่า = ~ หมายถึงอะไร ดังนั้นฉันจะรักคำอธิบายโดยรวม: P
Ali Yılmaz

28

วิธีที่ง่ายที่สุด IMHO คือการเพิ่มและต่อท้ายสตริงเดิมด้วยช่องว่างและตรวจสอบกับ regex ด้วย [[ ]]

haystack='foo bar'
needle='bar'

if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
    ...
fi

foo barbazนี้จะไม่เป็นเท็จบวกกับค่าที่มีค่าที่มีเข็มเป็นสตริงย่อยเช่นกับกองหญ้า

(แนวคิดนี้ถูกขโมยไปอย่างไร้hasClass()เหตุผลแบบฟอร์มของ JQuery )


4
หากตัวแยกไม่ได้มีพื้นที่ว่างการแก้ปัญหาจะชัดเจนยิ่งขึ้น สามารถทำได้โดยไม่ต้อง regex haystack="foo:bar"และ[[ ":$haystack:" = *:$needle:* ]]
phiphi

25

เกี่ยวกับ

echo $list | grep -w $x

คุณสามารถตรวจสอบผลลัพธ์หรือ $?บรรทัดข้างต้นเพื่อทำการตัดสินใจ

grep -w ตรวจสอบรูปแบบคำทั้งหมด


1
นี้จะไม่ตรวจสอบ "val" ถ้า "ค่า" ถูกต้อง (เช่นในรายการ) เนื่องจากมันเป็นสตริงย่อย
Ofir Farchy

ใช่มันจะเป็นเช่นเดียวกับคำตอบที่ได้รับการยอมรับ @glennjackman ให้โซลูชันการทำงาน
f.ardelian

3
คุณสามารถใช้ grep -q เพื่อให้ grep เงียบได้
Amir

สิ่งนี้จะยอมรับการจับคู่บางส่วนซึ่งอาจไม่ใช่สิ่งที่ผู้ใช้ต้องการ
carnicer

3
@carnicer จากนั้นใช้ grep -w $ x เพื่อจับคู่ที่ตรงกัน
user1297406

18

คุณสามารถใช้ (* สัญลักษณ์ตัวแทน) นอกคำสั่ง case ได้เช่นกันหากคุณใช้เครื่องหมายวงเล็บคู่:

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi

3
เรียบง่ายและสง่างาม +1
João Pereira

14
สิ่งนี้จะให้ค่าเป็นบวกหาก "My" เป็นสตริงย่อยของสตริงอื่นเช่น string = 'MyCommand string'
ลุคลี

เป็นที่น่าสังเกตว่าสัญลักษณ์ตัวแทน ( *My*) จะต้องอยู่ทางด้านขวาของการทดสอบ
Jacob Vanus

8

caseหากรายชื่อของค่าจะได้รับการกำหนดค่าตายตัวในสคริปต์ก็ค่อนข้างง่ายที่จะใช้ทดสอบ นี่เป็นตัวอย่างสั้น ๆ ซึ่งคุณสามารถปรับให้เข้ากับความต้องการของคุณ:

for item in $list
do
    case "$x" in
      item1|item2)
        echo "In the list"
        ;;
      not_an_item)
        echo "Error" >&2
        exit 1
        ;;
    esac
done

ถ้ารายการนั้นเป็นตัวแปรอาเรย์ที่รันไทม์คำตอบอีกข้อหนึ่งน่าจะเหมาะสมกว่า


7

หากรายการได้รับการแก้ไขในสคริปต์ฉันชอบสิ่งต่อไปนี้ดีที่สุด:

validate() {
    grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}

จากนั้นใช้validate "$x"ทดสอบว่า$xได้รับอนุญาตหรือไม่

หากคุณต้องการซับในหนึ่งอันและไม่สนใจช่องว่างในชื่อรายการคุณสามารถใช้สิ่งนี้ (แจ้งให้ทราบล่วงหน้า-wแทน-x):

validate() { echo "11 22 33" | grep -F -q -w "$1"; }

หมายเหตุ:

  • นี่เป็นshไปตามPOSIX
  • validateไม่ไม่ยอมรับสตริง (ลบ-xตัวเลือกในการ grep ถ้าคุณต้องการที่)
  • validateตีความอาร์กิวเมนต์เป็นสตริงคงที่ไม่ใช่นิพจน์ทั่วไป (ลบ-Fตัวเลือกเพื่อ grep หากคุณต้องการ)

โค้ดตัวอย่างเพื่อใช้ฟังก์ชัน:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done

6

พิจารณาการใช้ประโยชน์จากปุ่มของarrays สมาคม ฉันคิดว่าสิ่งนี้มีประสิทธิภาพสูงกว่าการจับคู่ regex / pattern และ looping แม้ว่าฉันจะไม่ได้ทำโปรไฟล์

declare -A list=( [one]=1 [two]=two [three]='any non-empty value' )
for value in one two three four
do
    echo -n "$value is "
    # a missing key expands to the null string, 
    # and we've set each interesting key to a non-empty value
    [[ -z "${list[$value]}" ]] && echo -n '*not* '
    echo "a member of ( ${!list[*]} )"
done

เอาท์พุท:

one is a member of ( one two three )
two is a member of ( one two three )
three is a member of ( one two three )
four is *not* a member of ( one two three )

2
... และคุณสามารถลดความซับซ้อนของการเปลี่ยนที่ (และไม่ต้องพึ่งพาecho -n) do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; doneด้วยการใช้ความคิดสร้างสรรค์ของพารามิเตอร์:
Toby Speight

มีวิธีง่ายๆในการสร้างอาร์เรย์ดังกล่าวถ้าฉันมีเพียงรายการ (ยาว) เช่น `list =" หนึ่งสองสาม xyz ... "?
Ott Toomet

5

ฉันพบว่าการใช้แบบฟอร์มง่ายขึ้นecho $LIST | xargs -n1 echo | grep $VALUEดังแสดงด้านล่าง:

LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
    ...
fi

สิ่งนี้ใช้ได้กับรายการที่คั่นด้วยช่องว่าง แต่คุณสามารถปรับให้เข้ากับตัวคั่นอื่น ๆ (เช่น:) โดยทำสิ่งต่อไปนี้:

LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
   ...
fi

โปรดทราบว่า"จำเป็นสำหรับการทดสอบการทำงาน


สิ่งนี้จะทำให้เกิดLIST="SOMEITEM1 ITEM2"ความจริงแม้ว่าITEM1จะไม่ได้อยู่ในนั้นก็ตาม
Ofir Farchy

เยี่ยมมากฉันอัปเดตตัวอย่างด้วย grep -e เพื่อยกเว้นการจับคู่บางส่วน
Sébastien Pierre

ฉันเชื่อว่ามี `พิเศษ 'ในตอนท้ายของคำสั่ง" ถ้า " รูปแบบที่ถูกต้องคือ: [-n "` echo $ LIST | xargs -n1 echo | grep -e \ "^ $ VALUE $ \"]
Elad Tabak

3

คิดว่าฉันจะเพิ่มคำตอบให้กับรายการ

# Checks if element "$1" is in array "$2"
# @NOTE:
#   Be sure that array is passed in the form:
#       "${ARR[@]}"
elementIn () {
    # shopt -s nocasematch # Can be useful to disable case-matching
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}

# Usage:
list=(11 22 33)
item=22

if elementIn "$item" "${list[@]}"; then
    echo TRUE;
else
    echo FALSE
fi
# TRUE

item=44
elementIn $item "${list[@]}" && echo TRUE || echo FALSE
# FALSE

1

ตัวอย่าง

$ in_list super test me out
NO

$ in_list "super dude" test me out
NO

$ in_list "super dude" test me "super dude"
YES

# How to use in another script
if [ $(in_list $1 OPTION1 OPTION2) == "NO" ]
then
  echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2"
  exit;
fi

in_list

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ...

  e.g. 

  a b c d                    -> NO
  a b a d                    -> YES
  "test me" how "test me"    -> YES

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

if [ "$#" -eq 0 ]; then
  show_help
fi

SEARCH_FOR=$1
shift;

for ITEM in "$@"
do
  if [ "$SEARCH_FOR" == "$ITEM" ]
  then
    echo "YES"
    exit;
  fi
done

echo "NO"

1

การสมมติตัวแปร TARGET สามารถเป็น 'binomial' หรือ 'regression' เท่านั้นดังนั้นการดำเนินการดังต่อไปนี้:

# Check for modeling types known to this script
if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then
    echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2
    usage
fi

คุณสามารถเพิ่มสตริงเพิ่มเติมลงในรายการโดยแยกพวกเขาด้วย | อักขระ (ไพพ์)

ข้อดีของการใช้ egrep คือคุณสามารถเพิ่ม case insensitivity (-i) หรือตรวจสอบสถานการณ์ที่ซับซ้อนด้วยนิพจน์ทั่วไป


1

นี่เป็นข้อเสนอเดิมของคุณ แต่เกือบจะเป็นแบบ 1 ซับ ไม่ซับซ้อนเท่าคำตอบที่ถูกต้องอื่น ๆ และไม่ขึ้นอยู่กับรุ่นของ bash (สามารถทำงานกับ bash เก่าได้)

OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella"
for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done
[ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }

ฉันเดาว่าข้อเสนอของฉันยังคงได้รับการปรับปรุงทั้งความยาวและสไตล์


0

หากไม่นานเกินไป คุณสามารถโยงมันระหว่างความเท่าเทียมกันตามตรรกะหรือการเปรียบเทียบได้

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 

ฉันมีปัญหาตรงนี้และในขณะที่ข้างต้นเป็นที่น่าเกลียดมันชัดเจนมากขึ้นว่าเกิดอะไรขึ้นกว่าวิธีการแก้ปัญหาทั่วไปอื่น ๆ

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