2

I need to get the next word of a word in a string. I tried to write a script but it's not working. And it's good if you suggest me any other alternatives to achieve this.

Script:

#!/bin/bash
opts="OPTS=\"-name user -age 20 -where Asia -eats Brains\""
echo $opts
ok="0"
for word in $opts; do
  if [ "$word" = "-where" ] ; then
    if [ "$ok" = "1" ] ; then
      echo $word
      break
    fi
    ok="1"
  fi
done

I want to get the word after "-where". But above script not working. I didn't understand where I'm missing. Thanks.

7

I suggest to hire grep for this job:

$ OPTS="\"-name user -age 20 -where Asia -eats Brains\""
$ grep -Po -- '-where \K\w*' <<< "$OPTS"
Asia

Explanation:

  • -P: perl compatible regular expression
  • -o: show only matching parts
  • \K: drop everything before that point
  • \w*: match word constituent (synonym for [_[:alnum:]])

To add " to the list of matching characters:

$ grep -Po -- '-eats \K[_\"[:alnum:]]*' <<< $OPTS
Brains"
  • Here is my string: "\"-name user -age 20 -where Asia -eats Brains\"". And if I give "-eats" then it should return Brains" not Brain – gangadhars Aug 28 '15 at 12:33
  • 1
    @linux_inside, then change \w* to \S+. – Stéphane Chazelas Aug 28 '15 at 12:39
  • @linux_inside You just need to add " to the list of matching characters (\w match only alnum + _). See the edit. – jimmij Aug 28 '15 at 12:40
  • @StéphaneChazelas, yeah, \S+ is even simpler. – jimmij Aug 28 '15 at 12:41
  • Note that with GNU grep and -P, \w and [[:alnum:]] only includes non-ASCII alnums in single-byte locales. You may want to add (*UCP) to the start of your regexp so it also works in UTF-8 locales (try with -where=España for instance). – Stéphane Chazelas Aug 28 '15 at 12:52
7

POSIXly:

after_first_where=${opts#*-where }
word_after_where=${after_first_where%% *}

Or to allow any number of blanks between words:

after_first_where=${opts#*-where}
word_after_where=${after_first_where#"${after_first_where%%[![:blank:]]*}"}
word_after_where=${word_after_where%%[[:blank:]]*}

Or you could do:

unset -v IFS; set -f # split on blanks, no glob
set -- $opts # splits $opts into $1, $2, $3...
while [ "$#" -ge 2 ]; do
  case $1 in
    (-age|-name|-where|-eats)
       eval "${1#-}=\$2" # assigns name=$2 or where=$2...
       shift 2;;
    (*) shift
  esac
done
printf '%s\n' "$name eats $eats in $where"

Note that in that one, blanks are limited to space, tab (and newline) not the other characters that may be considered as [:blank:] in your locale.

  • 1
    This is the best solution – MichalH Aug 28 '15 at 12:38
4

A little edit in your script:

#!/bin/bash
opts="OPTS=\"-name user -age 20 -where Asia -eats Brains\""
echo $opts
ok="0"
for word in $opts; do
  if [ "$ok" = "1" ] ; then
    echo $word
    break
  fi
  if [ "$word" = "-where" ] ; then
    ok="1"
  fi
done
0

Another POSIX one:

printf %s\\n "$opts" | tr -cs '[[:alnum:]-]' '[\n*]' | sed -n '/-where/{
  n
  p
}'
Asia

(Note that GNU tr won't work with multi bytes characters)

0

Using awk

opts="OPTS=\"-name user -age 20 -where Asia -eats Brains\""
awk '{for(i=1;i<=NF;i++) {if($i~"-where") {print $(i+1)}}}' <<< "$opts"

Output

Asia

using tr and sed

tr ' ' '\n' <<< "$opts" | sed -n '/-where/{n;p}'
Asia

using tr and awk

tr ' ' '\n' <<< "$opts" | awk '/-where/{getline;print;}'
Asia

tr ' ' '\n' <<< "$opts" | awk '/-where/{x=NR+1;next}(NR<=x){print}'
Asia

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.