How can I check if a string is in an array without iterating over the elements?

后端 未结 9 2278
眼角桃花
眼角桃花 2021-02-13 18:54

Is there a way of checking if a string exists in an array of strings - without iterating through the array?

For example, given the script below, how I can correctly impl

9条回答
  •  暗喜
    暗喜 (楼主)
    2021-02-13 19:31

    Reading your post I take it that you don't just want to know if a string exists in an array (as the title would suggest) but to know if that string actually correspond to an element of that array. If this is the case please read on.

    I found a way that seems to work fine .

    Useful if you're stack with bash 3.2 like I am (but also tested and working in bash 4.2):

    array=('hello' 'world' 'my' 'name' 'is' 'perseus')
    IFS=:     # We set IFS to a character we are confident our 
              # elements won't contain (colon in this case)
    
    test=:henry:        # We wrap the pattern in the same character
    
    # Then we test it:
    # Note the array in the test is double quoted, * is used (@ is not good here) AND 
    # it's wrapped in the boundary character I set IFS to earlier:
    [[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
    not found :(               # Great! this is the expected result
    
    test=:perseus:      # We do the same for an element that exists
    [[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
    found! :)               # Great! this is the expected result
    
    array[5]="perseus smith"    # For another test we change the element to an 
                                # element with spaces, containing the original pattern.
    
    test=:perseus:
    [[ ":${array[*]}:" =~ $test ]] && echo "found!" || echo "not found :("
    not found :(               # Great! this is the expected result
    
    unset IFS        # Remember to unset IFS to revert it to its default value  
    

    Let me explain this:

    This workaround is based on the principle that "${array[*]}" (note the double quotes and the asterisk) expands to the list of elements of array separated by the first character of IFS.

    1. Therefore we have to set IFS to whatever we want to use as boundary (a colon in my case):

      IFS=:
      
    2. Then we wrap the element we are looking for in the same character:

      test=:henry:
      
    3. And finally we look for it in the array. Take note of the rules I followed to do the test (they are all mandatory): the array is double quoted, * is used (@ is not good) AND it's wrapped in the boundary character I set IFS to earlier:

      [[ ":${array[*]}:" =~ $test ]] && echo found || echo "not found :("
      not found :(
      
    4. If we look for an element that exists:

      test=:perseus:
      [[ ":${array[*]}:" =~ $test ]] && echo "found! :)" || echo "not found :("
      found! :)
      
    5. For another test we can change the last element 'perseus' for 'perseus smith' (element with spaces), just to check if it's a match (which shouldn't be):

      array[5]="perseus smith"
      test=:perseus:
      [[ ":${array[*]}:" =~ $test ]] && echo "found!" || echo "not found :("
      not found :(
      

      Great!, this is the expected result since "perseus" by itself is not an element anymore.

    6. Important!: Remember to unset IFS to revert it to its default value (unset) once you're done with the tests:

      unset IFS
      

    So so far this method seems to work, you just have to be careful and choose a character for IFS that you are sure your elements won't contain.

    Hope it helps anyone!

    Regards, Fred

提交回复
热议问题