1

I am currently working on a script that would be rearranging the contents of a csv file. if I had a line that was similar to this:

stack,over,flow,dot,com

how could I go about determining the location of a string/word in the command delimited string. So for instance if I were to search for stack, it would return the number 1, if i were to search for flow, the number 3 would be returned, and so on. I've thought of a few ways in which I could do this, but they are mostly long drawn out scripts, so I have the feeling that there may be a shorter/simpler way to do this. If anyone could offer advice/help I would really appreciate it, thanks. also this is being performed in bash environment

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
lacrosse1991
  • 2,972
  • 7
  • 38
  • 47
  • things like this are best done using a scripting language, e.g. Perl or Python – amphibient Nov 12 '12 at 16:18
  • 1
    have you put __any__ effort in looking for answer on your own? http://stackoverflow.com/questions/1560393/bash-shell-scripting-csv-parsing, http://stackoverflow.com/questions/4286469/how-to-have-bash-parse-a-csv-file, http://www.thelinuxblog.com/working-with-csv-files-in-bash/ – maialithar Nov 12 '12 at 16:23

8 Answers8

3

awk oneliner:

awk -F, -vs=$search '{for (i=1;i<=NF;i++)if($i~"^"s"$"){print i;exit;}}{print "not found"}' yourString

(see the example test below)

kent$  l="stack,over,flow,dot,com"
kent$  echo $l
stack,over,flow,dot,com
kent$  search=over
kent$  echo $search
over    
kent$  awk -F, -vs=$search '{for (i=1;i<=NF;i++)if($i~"^"s"$"){print i;exit;}}{print "not found"}' <<<$l
2
kent$  search=foobar    
kent$  awk -F, -vs=$search '{for (i=1;i<=NF;i++)if($i~"^"s"$"){print i;exit;}}{print "not found"}' <<<$l 
not found
Kent
  • 189,393
  • 32
  • 233
  • 301
2
echo $line | awk -F, '{
  for(i=1;i<=NF;i++){
    if($i=="your_string") print i;
  }
}'

Note: NF stands for Number of Fields.

Jose Ricardo Bustos M.
  • 8,016
  • 6
  • 40
  • 62
Vijay
  • 65,327
  • 90
  • 227
  • 319
1

a bash function:

position() {
    local search=$1
    local IFS=,
    local i=1
    set -- $2
    for word; do
        if [[ $word = $search ]]; then
            echo $i
            return
        fi
        ((i++))
    done
    echo -1
}

Then:

$ position stack stack,over,flow,dot,com
1
$ position tack stack,over,flow,dot,com
-1
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

Just because you asked for a 100% bash solution (this does not use sed, awk, seq, etc.):

L='stack,over,flow,dot,com'
IFS=,
set -- $L
declare -A A
for ((i=1; i<=$#; i++))
do
    A[${!i}]=$i
done

# where's flow?
echo "flow=${A[flow]}"
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
0

You can count the commas up to the matching string:

for word in stack over flow dot com ; do
    echo $word
    grep -o ".*$word" <<< stack,over,flow,dot,com \
    | grep -o , \
    | wc -l
done

But if you want to do some more manipulation with CSV, switching to Perl and using Text::CSV would be the way to go.

choroba
  • 231,213
  • 25
  • 204
  • 289
0

Split Lines, Then Find Line Number

You can split the lines with sed, and then find the matching line number. For example:

search_term='flow'
echo 'stack,over,flow,dot,com' |
    sed -e  's/,/\n/g' |
    sed -ne "/^${search_term}\$/ {=; q}"

Because sed is line-oriented, it's necessary to transform the whole file first before searching for the matching line number. That's why we're piping to another instance of sed, instead of simply using a second expression in the current process.

There are certainly other ways to do this, but this is easier. YMMV.

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
0

sed and grep represented so far. Here's an awk solution:

echo "stack,over,flow,dot,com" | awk -F, '{ for (i=1; i < NF; ++i) if ($i == "flow") print i; }'
twalberg
  • 59,951
  • 11
  • 89
  • 84
0

Suppose you want to find all of the words:

$ LINE=stack,over,flow,dot,com
$ read ${LINE//,/\ } rest < <(echo $(seq 100))
$ echo $stack $over $flow $dot $com
1 2 3 4 5

Of course, that could easily give you name collisions so you might want to prefix something to the names:

$ LINE=stack,over,flow,dot,com
$ read field_${v//,/\ field_} rest < <(echo $(seq 100))
$ echo $field_stack $field_over $field_flow $field_dot $field_com
1 2 3 4 5
rici
  • 234,347
  • 28
  • 237
  • 341