1

I have a variable which contain for example ['Cars', 'House', 'Bike']. I want to be able to search for an exact match with values contained in the string.


Exemple:

  • I check if the string Cars exist in the variable. It should return true.
  • I check if the string Car exist in the variable. It should return false.

What I tried :

#!/bin/bash
search="Car"
arr="['Cars', 'House', 'Bike']"
if [[ $search =~ .*"$arr".* ]]; then
    echo "true"
else
    echo "false"
fi
# Output true | Expected false

Another script :

#!/bin/bash
search="Cars"
arr="['Cars', 'House', 'Bike']"
check=0
grep -o "'[^']*'" <<<"$arr" | sed "s/'//g" |
while read -r elem; do
    if [ "$search" == "$elem" ]; then
         check=1
    fi
done
if [ "$check" == 1 ]; then
    echo "true"
else
    echo "false"
fi
# Output false | Expected true
executable
  • 2,788
  • 3
  • 16
  • 39
  • Parsing JSON in `bash` is like sawing lumber with a baby: you really don't want to do it, and if you do, there'll be a lot of emotional pain. Use `jq` or any higher programming language. These days, Ruby, Python and Perl are all pretty much guaranteed to be installed, and `jq` is easy to install. See [Parsing JSON with Unix tools](https://stackoverflow.com/questions/1955505/parsing-json-with-unix-tools), and [How to check if element exists in array with jq](https://stackoverflow.com/questions/43259563/how-to-check-if-element-exists-in-array-with-jq) – Amadan Feb 07 '19 at 12:41
  • I can use python in my script. Updated tags – executable Feb 07 '19 at 12:43
  • I think this should work `variableTocheck='Cars` `listData = ['Cars', 'House', 'Bike']` `valPresent = variableTocheck in listData` now valPresent will be true if the data is present else it will be false – Vaibhav gusain Feb 07 '19 at 12:50
  • 1
    Obviously, this question turns into a [very different question](https://stackoverflow.com/questions/3685970/check-if-a-bash-array-contains-a-value) if you are able to just use native bash arrays, not this unholy JSONoid. :P – Amadan Feb 07 '19 at 12:55
  • I can't use array because I get the response from a cURL command – executable Feb 07 '19 at 13:03

7 Answers7

1

In Python:

search="Car"
arr="['Cars', 'House', 'Bike']"

import ast
output = search in ast.literal_eval(arr)

As a one-liner in a shell script that outputs True or False:

python -c 'import sys; import ast; print(sys.argv[1] in ast.literal_eval(sys.argv[2]))' "$search" "$arr"

As a one-liner in a shell script that returns 0 (okay) or -1 (not okay) exit status, as is usual in shell:

python -c 'import sys; import ast; sys.exit(0 if sys.argv[1] in ast.literal_eval(sys.argv[2]) else -1)' "$search" "$arr"
Amadan
  • 169,219
  • 18
  • 195
  • 256
1

As others said, use jq to parse JSON in bash.

If you really don't want that, you can try any of these:

search="'Car'" # add the single quotes to the search string
# or
search="\bCar\b" # \b is regex syntax for word boundary, meaning the word begins/ends there
jvdmr
  • 636
  • 5
  • 12
1

Some answers about how to do this in some other language. I think you want to know why your bash script isn't working.

The reason is fundamentally that when you are setting check=1 inside the if statement, you're in a different shell, so it doesn't affect the check that's defined in the outer script. It's a different shell because you're piping input into the while from another command.

A few ways to make this work:

Send the output of the grep pipeline to a temporary file, then execute the while loop in your main shell script and read the input from the file.

You could also return whether or not you found the string you're looking for via the exit code and preserve it in check using the $? variable that stores the exit code of the last process.

grep -o "'[^']*'" <<<"$arr" | sed "s/'//g" |
while read -r elem; do
    if [ "$search" = "$elem" ]; then
         exit 1
    fi
done 
check=$?

Since using the exit code in your while loop basically makes it into a poor version of fgrep, you could just use fgrep and check the exit code.

Willis Blackburn
  • 7,338
  • 16
  • 33
1

There are several problems with the regular expression match in the first Bash example code in the question. The biggest problem is that $search and $arr are on the wrong sides of the match operator. The lack of (quoted) single quotes in the pattern is also a serious problem. Regular expression matching is overkill for this anyway. The simpler "glob" pattern matching is sufficient. Try this:

#!/bin/bash

search=Car
arr="['Cars', 'House', 'Bike']"

if [[ $arr == *"'$search'"* ]]; then
    echo true
else
    echo false
fi

See glob - Greg's Wiki for information about glob patterns.

pjh
  • 2,848
  • 11
  • 15
0

I found a way to modify the global variable in the loop. Thank you all for suggestion. Here is the script :

#!/bin/bash

search="Cars"
arr="['Cars', 'House', 'Bike']"
check=0
for elem in $(grep -o "'[^']*'" <<<"$arr" | sed "s/'//g")
do
    if [ "$search" == "$elem" ]; then
         check=1
    fi
done
if [ "$check" == 1 ]; then
    echo "true"
else
    echo "false"
fi
# Output true | Expected true
executable
  • 2,788
  • 3
  • 16
  • 39
0

With a ruby one-liner:

$ json_arr="['Cars', 'House', 'Bike']"

$ ruby -rjson -e '
    data = JSON.parse(ARGV.shift)
    puts data.include?(ARGV.shift)
' "${json_arr//\'/\"}" Cars
true

$ ruby -rjson -e '
    data = JSON.parse(ARGV.shift)
    puts data.include?(ARGV.shift)
' "${json_arr//\'/\"}" Donkey
false

Note that JSON strings must use double quotes (https://json.org/), so I'm using the shell's parameter substitution to translate single quotes to double quotes.

glenn jackman
  • 207,528
  • 33
  • 187
  • 305
0

If you don't want to use anything but bash, you can use bash arrays.

There are pros and cons, as always. One of the pro is that you don't have to install any third program in order to make it work and it will run also in other shells like cygwin.

I wrote a piece of code for example:

#!/bin/bash
search="Car"
arr="['Cars', 'House', 'Bike']"
arr="$(printf "%s" "${arr}" | sed -e "s/,//g" -e "s/^\[/(/" -e "s/\]$/)/")"
eval arr="${arr}"
for var in ${arr[@]}; do
   if [ "${var}" = "${search}" ]; then
      printf "%s\n" "true"
      exit
   fi  
done
printf "%s\n" "false"

This code anyway takes for granted some things and have some cons.

For example, it takes for granted that all your input is like the sample, without any "," inside the actual variables.

ingroxd
  • 860
  • 1
  • 9
  • 27