executable
executable

Reputation: 3600

How to match an exact string in a string between quotes

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:

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

Upvotes: 1

Views: 704

Answers (7)

ingroxd
ingroxd

Reputation: 1025

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.

Upvotes: 0

pjh
pjh

Reputation: 8184

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.

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 247042

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.

Upvotes: 0

executable
executable

Reputation: 3600

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

Upvotes: 0

Willis Blackburn
Willis Blackburn

Reputation: 8204

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.

Upvotes: 1

Amadan
Amadan

Reputation: 198436

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"

Upvotes: 1

jvdmr
jvdmr

Reputation: 715

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

Upvotes: 1

Related Questions