1

I am using a bash script and have a string of comma separated values string="string1,string2,string". There would not be any embedded commas in each string, or spaces. Want to test whether the string elements occur in an array.

What can I do to match string elements with elements of an arbitrary array?

string="element1,element2,element3" array=($(echo $string | tr ',' ' ')) for i in "${array[@]}"; do if [ "$i" == "element2" ]; then echo "element found" fi done 
7
  • Is you original string of comma-delimited values encoded in some special way to allow for spaces or embedded commas? Are any such element, for example, quoted, as they would be in a CSV record? The issue here is not the testing of individual element from your array against the query string, but the correct creation of the array from the initial string. An example CSV record could be a,b,"c d","e,f ""g"",h" which encodes the values a, b, c d, and e,f "g",h (quoted fields may also contain newlines).
    – Kusalananda
    CommentedFeb 4, 2023 at 10:12
  • No embedded commas and no spaces.
    – Vera
    CommentedFeb 4, 2023 at 10:25
  • 1
    Welcome to the community. If the comments prompt new info or clarifications, please add it directly to the question by editing it. This way the relevant info is directly available for the community members. In general I'd suggest reading through the Help section (at least the Asking / Answering -parts) to learn how things work - SE communities aren't discussion forums.CommentedFeb 4, 2023 at 10:42
  • 1
    Is this search being invoked multiple times? You might consider making the array associative (hashed), so that the values are the indexes of the array. You can then just check if that element exists directly, rather than searching the whole array.CommentedFeb 4, 2023 at 11:27
  • Yes, The array will have values obtained from a line of text from a file. But the comma separated text will always be the same.
    – Vera
    CommentedFeb 4, 2023 at 11:33

4 Answers 4

1

Maybe you can just do something like:

string=element1,element2,element3 element=element2 case ",$string," in (*,"$element",*) echo element is in string;; (*) echo it is not;; esac 

(standard sh syntax).

To work with arrays or split strings, bash is one the poorest choices of shells.

With zsh, to split a string on a given separator, there's a dedicate operator: the split parameter expansion flag:

array=( "${(@s[,])string}" ) 

(@ and quotes used to preserve empty elements like with the "$@" of the Bourne shell)

To check whether an array has a given element:

if (( $array[(Ie)$element] )); then print element is in the array else print it is not fi 

To split in bash, you can use the split+glob operator (which you did a bit awkwardly with the unquoted $(...)) like in ksh/sh:

IFS=, # split on , instead of the default of SPC/TAB/NL set -o noglob # disable the glob part which you don't want array=( $string'' ) # split+glob; '' added to preserve an empty trailing element # though that means an empty $string is split into one empty # element rather than no element at all 

To lookup array, bash has not dedicated operator either, but you could define a helper function:

is_in() { local _i _needle="$1" local -n _haystack="$2" for _i in "${_haystack[@]}"; do [ "$_i" = "$_needle" ] && return done false } if is_in "$element" array; then echo element is in the array else it is not fi 
1
  • All this is getting more complicated than it should be. Can there be just a two loop procedure?
    – Vera
    CommentedFeb 5, 2023 at 4:09
1

You can set up a hashed table of the list of valid words, and then search it (efficiently) for all items in a second list.

#! /bin/bash #.. ./LookUp 04-Feb-2023: Paul_Pedant. declare -A Hash #.. Create at global scope. Setup () { #.. Set up a hash table of the required elements. local j; declare -a q #.. Make the input string into an array like: q=([0]="Monday" ...) IFS=, read -r -a q <<<"${1}" #.. Invert that array like: Hash([Monday]="0" ...) for j in "${!q[@]}"; do Hash+=(["${q[j]}"]="${j}"); done } Query () { #.. Search for words in a list. local s; declare -a q IFS=, read -r -a q <<<"${1}" for s in "${q[@]}"; do if [[ -z "${Hash[${s}]+x}" ]]; then printf '%s is missing\n' "${s}" else printf '%s is index %s\n' "${s}" "${Hash[${s}]}" fi done } Setup "Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday" Query "Tuesday,BadHairDay,Friday,Holiday,Sunday,Today,Monday,BadDay" 

And the test:

$ ./LookUp Tuesday is index 1 BadHairDay is missing Friday is index 4 Holiday is missing Sunday is index 6 Today is missing Monday is index 0 BadDay is missing $ 
    0

    If I understand your question properly, ignoring the bits where you convert the csv to an array, the following can sometimes be used to search for an element within an array however there are caveats when it comes to potential whitespace/newlines, and probably more.

    arr=(foo bar baz) if [[ "${arr[*]}" =~ foo ]]; then echo "element found" fi 
    6
    • I could store the string values in another array, then I would have two arrays.
      – Vera
      CommentedFeb 4, 2023 at 12:34
    • @Avacha I'm not sure I follow that logic. You only need one array.
      – jesse_b
      CommentedFeb 4, 2023 at 12:35
    • I want to search a list of strings (variable composed of comma separated strings) to see if any are present in an array.
      – Vera
      CommentedFeb 4, 2023 at 12:48
    • 2
      @Avacha if that is your only use case it's best to just skip the array and search the comma separated string using a csv parser. But you said "I want the array to be an arbitrary array" which means ignore the csv example and assume this should work with any array.
      – jesse_b
      CommentedFeb 4, 2023 at 12:50
    • Right, I could use a csv parser. The problem here is that I have both a comma separated string and and array of values. The comma separated string and the array are not the same thing. I want to search whether the elements in the comma separated string exist in the array.
      – Vera
      CommentedFeb 5, 2023 at 3:00
    -2

    I have got the following solution

    aggr=("bash" "resource" "rsync") ukeys="resource,rsync" if [[ "${aggr[@]}" =~ $(echo "$ukeys" | tr ',' '|') ]]; then echo "All strings exist in the array." else echo "One or more strings do not exist in the array." fi 

    This uses the tr command to replace the commas in the string with pipes, creating a regular expression that can be used with the =~ operator. The $( ... ) syntax is used to run the tr command and capture its output, which is then used as the pattern for the =~ operator. If the regular expression matches any of the elements in the array, the if block will be executed, indicating that all strings in the string exist in the array.

    I want to adapt this in a way that if an element in ukeys matches an element in array aggr, then I set display=1.

    2
    • 1
      In comments to the question you said that the string would not contain fields with embedded commas or spaces, but this solution also disallows | and any substring that could be interpreted as a regular expression, such as .*, [[:alnum:]]* etc. You also introduce an array and a string with comma-delimited fields here. In the question you had a string and a query word, which is different from what you have here. If you need to ask a new question, then do so with a new question. If you need to clarify your question, then do so by editing the question.
      – Kusalananda
      CommentedFeb 5, 2023 at 9:22
    • It would also be interesting to know where the data comes from, because I have a feeling you don't even have to read these things into arrays the first place.
      – Kusalananda
      CommentedFeb 5, 2023 at 9:24

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.