50

Say a string might be like "a b '' c '' d". How can I check that there is single/double quote and space contained in the string?

codeforester
  • 39,467
  • 16
  • 112
  • 140
derrdji
  • 12,661
  • 21
  • 68
  • 78
  • Why do you want to do this? If you're trying to, say, check for an "invalid" file name, you could instead fix the script to support file names with spaces or quotes. For example. – John Kugelman Sep 24 '09 at 20:56
  • Are you saying that you want to know that: a) there is a single/double quote around a space b) a single/double quote along with a space c) something altogether different from that? – ezpz Sep 24 '09 at 21:10
  • Just to check if a string has any single quote and if it has any space – derrdji Sep 28 '09 at 18:44
  • To check if string has ONLY spaces, tabs, or new lines, try: `[[ $string = *[$" \t\n"]* ]]` – Noam Manos May 11 '20 at 15:52

10 Answers10

42

You can use regular expressions in bash:

string="a b '' c '' d"
if [[ "$string" =~ \ |\' ]]    #  slightly more readable: if [[ "$string" =~ ( |\') ]]
then
   echo "Matches"
else
   echo "No matches"
fi

Edit:

For reasons obvious above, it's better to put the regex in a variable:

pattern=" |'"
if [[ $string =~ $pattern ]]

And quotes aren't necessary inside double square brackets. They can't be used on the right or the regex is changed to a literal string.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • cleanest one IMO, for me I also added tab but looks a little funky as I had to throw the dollar-quote in there `pattern=" |'"$'|\t'` - is there a cleaner way? – nhed May 24 '16 at 14:27
  • 1
    @nhed: In the case of single characters, you can use a character pattern instead of the pipe character alternation. This might be slightly less ugly: `pattern=$'[ \'\t]'`. You could also use a variable: `tab=$'\t'; pattern=" |'|$tab"` or a literal tab (which isn't good for readability) by typing it in using Ctrl-V-Tab. – Dennis Williamson May 24 '16 at 14:42
33
case "$var" in  
     *\ * )
           echo "match"
          ;;
       *)
           echo "no match"
           ;;
esac
Steve B.
  • 55,454
  • 12
  • 93
  • 132
33

You could do this, without the need for any backslashes or external commands:

# string matching

if [[ $string = *" "* ]]; then
  echo "string contains one or more spaces"
else
  echo "string doesn't contain spaces"
fi

# regex matching

re="[[:space:]]+"
if [[ $string =~ $re ]]; then
  echo "string contains one or more spaces"
else
  echo "string doesn't contain spaces"
fi

Based on this benchmark, the string match is much faster than the regex one.


Related:

codeforester
  • 39,467
  • 16
  • 112
  • 140
  • 3
    Best most current answer because it specifically answers the question - a space in a string - by providing two options that are portable and very easy to understand during a code review. Please people, like @codeforester, keep it simple; the best software always is. – akahunahi Oct 10 '18 at 04:41
  • 2
    The regex version is more general, since it also finds \t and \n and \r. [:space:] "Whitespace characters such as space, form-feed, newline,carriage return, horizontal tab, and vertical tab" https://www.oreilly.com/library/view/oracle-regular-expressions/0596006012/re08.html – masterxilo Nov 20 '18 at 20:13
9
[[ "$str" = "${str%[[:space:]]*}" ]] && echo "no spaces" || echo "has spaces"
blueyed
  • 27,102
  • 4
  • 75
  • 71
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
8
string="a b '' c '' d"
if [ "$string" == "${string//[\' ]/}" ]
then 
   echo did not contain space or single quote
else
   echo did contain space or single quote
fi
Paul
  • 376
  • 1
  • 3
4

The portable way to do this is with grep:

S="a b '' c '' d"
if echo $S | grep -E '[ "]' >/dev/null
then
  echo "It's a match"
fi

...a bit ugly, but guaranteed to work everywhere.

alex tingle
  • 6,920
  • 3
  • 25
  • 29
3

How about an approach similar to:

$ A="some string"; echo $A | grep \  | wc -l
1
$ A="somestring"; echo $A | grep \  | wc -l
0

?

Grzegorz Oledzki
  • 23,614
  • 16
  • 68
  • 106
1
function foo() {
    echo "String: $*"
    SPACES=$(($#-1))
    echo "Spaces: $SPACES"
    QUOTES=0
    for i in $*; do
        if [ "$i" == "'" ]; then
            QUOTES=$((QUOTES+1))
        fi
    done
    echo "Quotes: $QUOTES"
    echo
}

S="string with spaces"
foo $S
S="single' 'quotes"
foo $S
S="single '' quotes"
foo $S
S="single ' ' quotes"
foo $S

yields:

String: string with spaces
Spaces: 2
Quotes: 0

String: single' 'quotes
Spaces: 1
Quotes: 0

String: single '' quotes
Spaces: 2
Quotes: 0

String: single ' ' quotes
Spaces: 3
Quotes: 2
ezpz
  • 11,767
  • 6
  • 38
  • 39
1

What about this:

[[ $var == ${var//[ \"]/_} ]] && echo "quotes or spaces not found"

or if you like this:

if [[ $var == ${var//[ \"]/_} ]] ; then  
   echo "quotes or spaces not found" 
else
   echo "found quotes or spaces"
fi

Explanation: I'm evaluating a comparison between the variable ${var} and the variable ${var} itself after a on-the-fly non-destructive string substitution of all the quotes and spaces with an underscore.

Examples:

${var// /_}  # Substitute all spaces with underscores

The following code substitute all characters between the squared brackets (space and quotes) with an underscore. Note that quotes has to be protected with backslash:

${var//[ \"]/_}  
rgiannico
  • 33
  • 6
  • I like the use of this syntax, but you've flipped the search and are seeking the opposite of what OP asked for. Should mention `!=` – Xaekai Apr 15 '20 at 20:45
0

I do wonder why nobody mentioned the [:space:] set. Usually your not only interested in detecting the space character. I often need to detect any white space, e.g. TAB. The "grep" example would look like this:

$ echo " " | egrep -q "[:space:]" && echo "Has no Whitespace" || echo "Has Whitespace"
Has Whitespace
$ echo "a" | egrep -q "[:space:]" && echo "Has no Whitespace" || echo "Has Whitespace"
Has no Whitespace
dz.
  • 1,286
  • 12
  • 12
  • For those interested in this solution, my `grep` informs me that `character class syntax is [[:space:]], not [:space:]`. Otherwise, excellent solution! – jdferreira Mar 21 '13 at 16:55
  • Since question is tagged "bash", no need to start up another process just to test a string: `case "$String" in *[[:space:]]*) echo "match" ;; esac` – Ron Burk May 23 '17 at 19:43