Cách dễ nhất để kiểm tra một chỉ mục hoặc một khóa trong một mảng?


89

Sử dụng:

set -o nounset
  1. Có một mảng được lập chỉ mục như:

    myArray=( "red" "black" "blue" )
    

    Cách ngắn nhất để kiểm tra xem phần tử 1 có được đặt không?
    Đôi khi tôi sử dụng như sau:

    test "${#myArray[@]}" -gt "1" && echo "1 exists" || echo "1 doesn't exist"
    

    Tôi muốn biết nếu có một cái ưa thích.

  2. Làm thế nào để đối phó với các chỉ mục không liên tiếp?

    myArray=()
    myArray[12]="red"
    myArray[51]="black"
    myArray[129]="blue"
    

    Làm thế nào để kiểm tra nhanh 51đã được thiết lập chẳng hạn?

  3. Làm thế nào để xử lý các mảng liên kết?

    declare -A myArray
    myArray["key1"]="red"
    myArray["key2"]="black"
    myArray["key3"]="blue"
    

    Làm thế nào để kiểm tra nhanh key2ví dụ đã được sử dụng?

Câu trả lời:


130

Để kiểm tra xem phần tử đã được đặt chưa (áp dụng cho cả mảng được lập chỉ mục và mảng kết hợp)

[ ${array[key]+abc} ] && echo "exists"

Về cơ bản những gì ${array[key]+abc}hiện là

  • nếu array[key]được thiết lập, trở lạiabc
  • nếu array[key]không được đặt, không trả lại gì


Người giới thiệu:

  1. Xem Mở rộng tham số trong sách hướng dẫn Bash và lưu ý nhỏ

    nếu dấu hai chấm bị bỏ qua, toán tử chỉ kiểm tra sự tồn tại [của tham số ]

  2. Câu trả lời này thực sự được điều chỉnh từ các câu trả lời cho câu hỏi SO này: Làm thế nào để biết nếu một chuỗi không được xác định trong tập lệnh bash shell ?


Hàm trình bao bọc:

exists(){
  if [ "$2" != in ]; then
    echo "Incorrect usage."
    echo "Correct usage: exists {key} in {array}"
    return
  fi   
  eval '[ ${'$3'[$1]+muahaha} ]'  
}

Ví dụ

if ! exists key in array; then echo "No such array element"; fi 

Tôi đã giải quyết theo cách này: if test "$ {myArray ['key_or_index'] + Isset}"; sau đó echo "có"; khác echo "không"; fi; Đối với tôi, nó có vẻ là cách đơn giản nhất và nó áp dụng cho các mảng được lập chỉ mục và liên kết. Cảm ơn bạn
Luca Borrione

1
@doubleDown Làm cách nào để bạn sử dụng [$ {array [key] + abc}] trong mệnh đề if để chỉ thực hiện điều gì đó nếu [$ {array [key] + abc}] không tồn tại?
olala

1
Cũng không hoạt động khi bạn vô tình truy vấn mảng được liệt kê dưới dạng liên kết.
Tomáš Zato - Phục hồi Monica

1
@duanev: Không có +abc, [ ${array[key]} ]sẽ đánh giá thành false nếu phần tử thực sự được đặt nhưng ở một giá trị trống, vì vậy nó thực sự đang kiểm tra giá trị không rỗng chứ không phải là tồn tại khóa.
musiphil

@duanev Without +abccũng không thành công khi array[key]không được đặt và set -ucó hiệu lực.
Ding-Yi Chen

35

Từ man bash , biểu thức điều kiện:

-v varname
              True if the shell variable varname is set (has been assigned a value).

thí dụ:

declare -A foo
foo[bar]="this is bar"
foo[baz]=""
if [[ -v "foo[bar]" ]] ; then
  echo "foo[bar] is set"
fi
if [[ -v "foo[baz]" ]] ; then
  echo "foo[baz] is set"
fi
if [[ -v "foo[quux]" ]] ; then
  echo "foo[quux] is set"
fi

Điều này sẽ cho thấy rằng cả foo [bar] và foo [baz] đều được đặt (mặc dù cái sau được đặt thành giá trị trống) và foo [quux] thì không.


1
Tôi đã bỏ lỡ nó trong nháy mắt; chú ý rằng cú pháp mở rộng mảng điển hình không được sử dụng.
Nathan Chappell

Với set -u, tại sao lại [[ -v "${foo[bar]}" ]]tạo ra lỗi biến không liên kết nếu barkhông tồn tại trong từ điển? Hoạt động tốt mà không có ${}; Tôi chỉ quen sử dụng nó cho mọi thứ theo mặc định.
bgfvdu3w

"${foo[bar]}"đánh giá biến mảng trước, vì vậy [[ -vlệnh kiểm tra biến có tên của giá trị đó
andysh

10

Câu trả lời mới

Từ phiên bản 4.2 của (và mới hơn), có một -vtùy chọn mới cho testlệnh tích hợp.

array=([12]="red" [51]="black" [129]="blue")

for i in 10 12 30 {50..52} {128..131};do
    if [ -v array[i] ];then
        echo "Variable 'array[$i]' is defined"
    else
        echo "Variable 'array[$i]' not exist"
    fi
done
Variable 'array[10]' not exist
Variable 'array[12]' is defined
Variable 'array[30]' not exist
Variable 'array[50]' not exist
Variable 'array[51]' is defined
Variable 'array[52]' not exist
Variable 'array[128]' not exist
Variable 'array[129]' is defined
Variable 'array[130]' not exist
Variable 'array[131]' not exist

Điều này hoạt động với các mảng liên kết theo cách tương tự:

declare -A aArray=([foo]="bar" [bar]="baz" [baz]=$'Hello world\041')

for i in alpha bar baz dummy foo test;do
    if [ -v aArray[$i] ];then
        echo "Variable 'aArray[$i]' is defined"
    else
        echo "Variable 'aArray[$i]' not exist"
    fi
done
Variable 'aArray[alpha]' not exist
Variable 'aArray[bar]' is defined
Variable 'aArray[baz]' is defined
Variable 'aArray[dummy]' not exist
Variable 'aArray[foo]' is defined
Variable 'aArray[test]' not exist

Có một chút khác biệt:
Trong mảng thông thường, biến giữa dấu ngoặc vuông ( [i]) là số nguyên, do đó, $không cần ký hiệu đô la ( ), nhưng đối với mảng kết hợp, vì khóa là một từ, $là bắt buộc ( [$i])!

Câu trả lời cũ cho trước V4.2

Thật không may, bash không có cách nào để tạo ra sự khác biệt giữa biến rỗngkhông xác định .

Nhưng có một số cách:

$ array=()
$ array[12]="red"
$ array[51]="black"
$ array[129]="blue"

$ echo ${array[@]}
red black blue

$ echo ${!array[@]}
12 51 129

$ echo "${#array[@]}"
3

$ printf "%s\n" ${!array[@]}|grep -q ^51$ && echo 51 exist
51 exist

$ printf "%s\n" ${!array[@]}|grep -q ^52$ && echo 52 exist

(không trả lời)

Và đối với mảng kết hợp, bạn có thể sử dụng tương tự:

$ unset array
$ declare -A array
$ array["key1"]="red"
$ array["key2"]="black"
$ array["key3"]="blue"
$ echo ${array[@]}
blue black red

$ echo ${!array[@]}
key3 key2 key1

$ echo ${#array[@]}
3

$ set | grep ^array=
array=([key3]="blue" [key2]="black" [key1]="red" )

$ printf "%s\n" ${!array[@]}|grep -q ^key2$ && echo key2 exist || echo key2 not exist
key2 exist

$ printf "%s\n" ${!array[@]}|grep -q ^key5$ && echo key5 exist || echo key5 not exist
key5 not exist

Bạn có thể thực hiện công việc mà không cần đến các công cụ bên ngoài (không cần printf | grep làm hàm bash thuần túy ), và tại sao không, hãy xây dựng checkIfExist () dưới dạng một hàm bash mới:

$ checkIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) return 0 ;;
        * ) return 1 ;;
      esac";
}

$ checkIfExist array key2 && echo exist || echo don\'t
exist

$ checkIfExist array key5 && echo exist || echo don\'t
don't

hoặc thậm chí tạo một hàm bash getIfExist mới trả về giá trị mong muốn và thoát với mã kết quả sai nếu giá trị mong muốn không tồn tại:

$ getIfExist() {
    eval 'local keys=${!'$1'[@]}';
    eval "case '$2' in
        ${keys// /|}) echo \${$1[$2]};return 0 ;;
        * ) return 1 ;;
      esac";
}

$ getIfExist array key1
red
$ echo $?
0

$ # now with an empty defined value
$ array["key4"]=""
$ getIfExist array key4

$ echo $?
0
$ getIfExist array key5
$ echo $?
1

Ok cho phiếu phản đối: Câu trả lời này đã được đăng trước V4.2 của bash ! Đã chỉnh sửa câu trả lời!
F. Hauri

Không hoạt động trên bash 4.2.46. Không hoạt động bash 4.4.12.
Irfy

@Irfy Cái gì không hoạt động? -vtùy chọn của testhoặc getIfExistchức năng?
F. Hauri

-vkhông hoạt động trên các mảng trên CentOS 7.7.1908 của tôi với bash 4.2.46. Mã từ khối mã đầu tiên của bạn được in not existtrong mọi trường hợp dưới bash đó. (Cũng đã thử [$i]thay vì [i], không có sự khác biệt)
Irfy

5

được thử nghiệm trong cơ sở 4.3.39 (1) -release

declare -A fmap
fmap['foo']="boo"

key='foo'
# should echo foo is set to 'boo'
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi
key='blah'
# should echo blah is unset in fmap
if [[ -z "${fmap[${key}]}" ]]; then echo "$key is unset in fmap"; else echo "${key} is set to '${fmap[${key}]}'"; fi

Điều đó không thành công khi giá trị của khóa là một chuỗi rỗng. Một cách giải quyết khác là bạn có thể sử dụng +mở rộng tham số để thay thế một giá trị trống bằng một số trình giữ chỗ như dấu gạch dưới. Ví dụ declare -A a[x]=;[[ ${a[x]} ]];echo $?bản in 1, nhưng declare -A a[x]=;[[ ${a[x]+_} ]];echo $?bản in 0.
nisetama

3

Còn về một -zbài kiểm tra và:- nhà điều hành?

Ví dụ, tập lệnh này:

#!/usr/bin/env bash

set -e
set -u

declare -A sample

sample["ABC"]=2
sample["DEF"]=3

if [[ ! -z "${sample['ABC']:-}" ]]; then
  echo "ABC is set"
fi

if [[ ! -z "${sample['DEF']:-}" ]]; then
  echo "DEF is set"
fi

if [[ ! -z "${sample['GHI']:-}" ]]; then
  echo "GHI is set"
fi

Bản in:

ABC is set
DEF is set

Giải pháp nhỏ gọn tuyệt vời đáp ứng như mong đợi cho một chuỗi trống
Ryan Dugan

1

Đây là cách dễ nhất mà tôi tìm thấy cho các tập lệnh.

<search>là chuỗi bạn muốn tìm, ASSOC_ARRAYtên của biến chứa mảng liên kết của bạn.

Phụ thuộc vào những gì bạn muốn đạt được:

khóa tồn tại :

if grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key is present; fi

khóa không tồn tại :

if ! grep -qe "<search>" <(echo "${!ASSOC_ARRAY[@]}"); then echo key not present; fi

giá trị tồn tại :

if grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value is present; fi

giá trị tồn tại không :

if ! grep -qe "<search>" <(echo "${ASSOC_ARRAY[@]}"); then echo value not present; fi

1

Tôi đã viết một hàm để kiểm tra xem khóa có tồn tại trong một mảng trong Bash hay không:

# Check if array key exists
# Usage: array_key_exists $array_name $key
# Returns: 0 = key exists, 1 = key does NOT exist
function array_key_exists() {
    local _array_name="$1"
    local _key="$2"
    local _cmd='echo ${!'$_array_name'[@]}'
    local _array_keys=($(eval $_cmd))
    local _key_exists=$(echo " ${_array_keys[@]} " | grep " $_key " &>/dev/null; echo $?)
    [[ "$_key_exists" = "0" ]] && return 0 || return 1
}

Thí dụ

declare -A my_array
my_array['foo']="bar"

if [[ "$(array_key_exists 'my_array' 'foo'; echo $?)" = "0" ]]; then
    echo "OK"
else
    echo "ERROR"
fi

Đã thử nghiệm với GNU bash, phiên bản 4.1.5 (1) -release (i486-pc-linux-gnu)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.