How to find the index of a word in a string in bash?

10

2

In bash script,

I have a string which contains several words separated by one or more than one spaces. ie:

Name   Age Sex  ID         Address

If I want to find any of the word, for instance I want to find the index of word "Age", how can I do it?

Is there any command that will return the index number of the word I want directly?

Thanks.

GJ.

Posted 2012-06-09T00:47:17.663

Reputation: 233

Related post: How to print certain columns by name?

– zx8754 – 2019-07-18T07:24:19.200

Does the solution have to be strictly in bash? Or can awk, grep, etc. be used? – jftuga – 2012-06-09T06:57:44.817

Answers

12

Bash performs word splitting in strings all by itself – in fact, more often than not, avoiding that is an issue, and the reason quoting is so important. It’s easy to leverage that in your case: just put your string into an array without quoting it – bash will use word splitting to separate the individual elements. Assuming your string is stored in the variable $str,

ar=($str) # no quotes!

will return an array of 5 elements. Your array index is your word index (counting up from 0, like in most scripting and programming languages), i.e. “Age” is accessed using

${ar[1]}  # 0 => Name, 1 => Age, 2 => Sex, 3 => ID, 4 => Address

or, if you need to find the element index by content, loop over the array, i.e.

function el_index {
    cnt=0; for el in "${ar[@]}"; do
        [[ $el == "$1" ]] && echo $cnt && break
        ((++cnt))
    done
}
el_index "Age" # => 1

kopischke

Posted 2012-06-09T00:47:17.663

Reputation: 2 056

wow...i didn't know that without the quotes then it would be an array. thanks! – GJ. – 2012-06-10T00:08:15.133

4

$ export FOO="Name   Age Sex  ID         Address"

Replace *Age with Age -- this will remove anything before "Age":

$ echo ${FOO/*Age/Age}
Age Sex ID Address

Get anything before "Age"

$ echo ${FOO/Age*/}
Name

Get the length of that string (which is the index of "Age"):

$ BEGIN=${FOO/Age*/}
$ echo ${#BEGIN}
7

user1034081

Posted 2012-06-09T00:47:17.663

Reputation: 161

Doesn't answer the question, but wow! Slick trick. It even works in ash, and with embedded variables: export L='debug info warn error'; export GTE='warn'; echo ${L/*${GTE}/${GTE}} prints 'warn error' – Steve Tarver – 2018-09-01T01:16:39.970

0

A mix of two previously given answers, using pure bash arrays and substring replacement.

The idea is to get a string of all words before the one you want, then count the number of words in that substring by making it into an array.

$ haystack="Name   Age Sex  ID         Address"
$ words_before=( ${haystack%Age*} )     # truncate string, make array
$ echo ${#words_before[*]}              # count words in array
1

Of course Age can be stored in another variable needle, then use ${haystack%$needle*}. Expect problems if the word you search for is a subset of another word, in which case kopischke's answer is still working.

Cimbali

Posted 2012-06-09T00:47:17.663

Reputation: 153

0

Note: Assuming here that by index you mean you want to know which word it is (starting from 0), not which character in the string the word starts on. Other answers address the latter.

Not that I'm aware of, but you can make one. Two tricks:

  1. Use the inborn abilities of the for construct to split up an unquoted input by whitespace.
  2. Handle the case where you can't find the column you want. In this case, I chose to send the found index to stout and let the status code indicate whether the find was successful. There are other possibilities.

Code:

#!/bin/bash
find_index() {
    local str=$1
    local search=$2
    let local n=0
    local retval=1 # here, 1 is failure, 0 success
    for col in $str; do # $str unquoted -> whitespace tokenization!
    if [ $col = $search ]; then
        echo $n
        retval=0
        break
    else
        ((n++))
    fi
    done
    return $retval
}

test="Name   Age Sex  ID         Address"
idx=`find_index "$test" Age`
if [ $? -ne 0 ]; then
    echo "Not found!"
else
    echo "Found: $idx"
fi

Owen S.

Posted 2012-06-09T00:47:17.663

Reputation: 288

0

If you don't have to strictly use bash, but can use other programs commonly found on systems with bash then you could use something like this:

echo "Name   Age Sex ID  Addr" | python -c 'print(raw_input().index("Age"))+1'

Python starts it's string indexing at zero, therefore I added +1 to the end of the command.

jftuga

Posted 2012-06-09T00:47:17.663

Reputation: 2 877

0

You can use bash's native regex

# a function to print the index of a field and its name
printIx() { 
  for ((l=0,i=1;i<$1;i++)) ;do 
     ((l+=${#BASH_REMATCH[i]}))
  done
  printf '%3s %s\n' $l "$2"
}

#   Using a zero based index
#   "0----+----1----+----2----+----3----+----4"
str="  Name   Age Sex  ID         Address   "

if [[ $str =~ ^(\ *)(Name)(\ +)(Age)(\ +)(Sex)(\ +()ID)(\ +)(Address)\ *$ ]] ;then
  F=(Name Age Sex ID Address)
  f=(   2   4   6  8      10)  # regex back-references
  for ((g=0;g<${#f[@]};g++)) ;do
     printIx  ${f[g]} "${F[g]}"
  done 
fi

Output

  2 Name
  9 Age
 13 Sex
 20 ID
 29 Address

Peter.O

Posted 2012-06-09T00:47:17.663

Reputation: 2 743

0

Try the following javascript oneliner in a shell (use javascript shell) :

$ js <<< "x = 'Name   Age Sex  ID         Address'; print(x.indexOf('Age'));"
7

Or with a here-doc:

js <<EOF
x = 'Name   Age Sex  ID         Address';
print(x.indexOf('Age'));
EOF

Gilles Quenot

Posted 2012-06-09T00:47:17.663

Reputation: 3 111

0

I found a solution that works fine.

$ string='now is the time'
$ buf=the${string#*the}
$ echo $buf
output: the time
$ index=$(( ${#string} - ${#buf} + 1 ))
$ echo $index
output: 8 -> index of first word "the"

It works similar as function indexOf() in Java which returns first occurrence of an input string.

Found this solution in here http://www.linuxquestions.org/questions/linux-newbie-8/bash-string-manipulation-help-670627/ (last post). This guy saved my day. Credit to him.

Faster way if you want to do substring from first indexof.

$ a="some long string"
$ b="ri"
$ echo ${a/*$b/$b}
ring
$ echo ${a/$b*/$b}
some long stri

https://stackoverflow.com/questions/10349102/shell-script-substring-from-first-indexof-substring

Linh Lino

Posted 2012-06-09T00:47:17.663

Reputation: 101

0

If coreutils are available you can do it in following way:

echo ${str/Age//}| cut -d/ -f1| wc -w

Per MariusMatutiae request I'm adding an explanation how this 3 steps operation works:

echo ${str/Age//} 1. replace string which is being searched for unique char (in my case /)

cut -d/ -f1 2. cut off whole part of string which is after unique char

wc -w 3. count and print words which are left this will give us an index number

For references please check:

http://www.tldp.org/LDP/abs/html/parameter-substitution.html (go to: "Variable expansion / Substring replacement")
http://www.gnu.org/software/coreutils/manual/coreutils.html (go to: "The cut command" and "wc invocation"

PiotrO

Posted 2012-06-09T00:47:17.663

Reputation: 101

While this solves the problem at hand, such terse replies are frowned upon in these sites. It would be more helpful to spend a few words explaining exactly why this works. Please do so. – MariusMatutiae – 2014-07-09T10:49:43.260