Iterating through grep results line by line not delimited by space

bash, grep

Solution

tl;dr:

- Change `for i in ls -d Module*; do` to `for i in Module*/;do`

- Replace

for j in `grep -B 4 "Module-0000/003.Design/005.Types/${1:0:1}/$1" $i/003.Design/003.Data.efx | grep "name"` ; do
  echo $j
done

with

while IFS= read -r j; do 
  echo "$j"
done < <(grep -B 4 "Module-0000/003.Design/005.Types/${1:0:1}/$1" "$i/003.Design/003.Data.efx" | grep "name")

See below for a full discussion.

`for i in ls -d Module*; do` will simply enumerate tokens `ls`, `-d` and (possibly expanded) `Module*` instead of enumerating the output from an `ls` command.

To correct that immediate problem, you'd have to use command substitution (`for i in $(ls -d Module*; do)`), but that is not advisable, because using globbing (pathname expansion) directly is the robust, proper solution: `for i in Module*/; do`

Similarly, it's not advisable to use a `for` loop to parse command output line by line, because the output is subject to word splitting, among other shell expansions. Effectively, the `for` loop enumerates whitespace-separated tokens rather than full lines.

Using a `while` loop with `IFS= read -r`, with input provided via process substitution, is the right approach - it ensures that lines are read unmodified.

Also, it's generally advisable to double-quote variable references to avoid unwanted shell expansions.

A revised form of your code is therefore:

shopt -s nullglob  # ensure that a glob matching nothing expands to empty string
for i in Module*/; do 
  if [[ -f "$i/003.Design/003.Data.efx" ]]; then
    if grep -qe "Module-0000/003.Design/005.Types/${1:0:1}/$1" "$i/003.Design/003.Data.efx"; then
      echo "$i $i/003.Design/003.Data.efx"
      while IFS= read -r j; do 
        echo "$j"
      done < <(grep -B 4 "Module-0000/003.Design/005.Types/${1:0:1}/$1" "$i/003.Design/003.Data.efx" | grep "name")
    fi
  fi
done

Problem

I'm using a bash script to identify where something is used and then find a corresponding reference (name). The script is: ``` #!/bin/bash echo "running, searching for instances of ${1:0:1}/$1" for i in ls -d Module*; do if [ -d "$i" ]; then if [ -f $i/003.Design/003.Data.efx ]; then if grep -qe "Module-0000/003.Design/005.Types/${1:0:1}/$1" $i/003.Design/003.Data.efx; then echo $i $i/003.Design/003.Data.efx grep -B 4 "Module-0000/003.Design/005.Types/${1:0:1}/$1" $i/003.Design/003.Data.efx | grep "name" fi fi fi done ``` I'm trying to change it to: ``` #!/bin/bash echo "running, searching for instances of ${1:0:1}/$1" for i in ls -d Module*; do if [ -d "$i" ]; then if [ -f $i/003.Design/003.Data.efx ]; then if grep -qe "Module-0000/003.Design/005.Types/${1:0:1}/$1" $i/003.Design/003.Data.efx; then echo $i $i/003.Design/003.Data.efx for j in `grep -B 4 "Module-0000/003.Design/005.Types/${1:0:1}/$1" $i/003.Design/003.Data.efx | grep "name"` ; do echo $j done fi fi fi done ``` When I make the above change to iterate through the results, the results are all screwed up. I suspect that my use of `for` is incorrect. I should note that the results before the change are: ``` $ ./findEllipseType.sh AveragePrice running, searching for instances of A/AveragePrice Module-3110 Module-3110/003.Design/003.Data.efx <ownedAttribute xmi:id="_P7sUcAg7EdysLcyAWbYneA" name="EstPrice"> Module-3140 Module-3140/003.Design/003.Data.efx <ownedAttribute xmi:id="_Z2v4IaW-EduyI4lEFgFo8g" name="StoresSalePrice"> <ownedAttribute xmi:id="_bpCVJaW-EduyI4lEFgFo8g" name="StoresSalePrice"> <ownedAttribute xmi:id="_j0crsMnVEeOm9cfLF6jryw" name="EstPrice"> Module-3210 Module-3210/003.Design/003.Data.efx <ownedAttribute xmi:id="_dYMlpaW-EduyI4lEFgFo8g" name="GrossPriceP"> <ownedAttribute xmi:id="_duQVQqW-EduyI4lEFgFo8g" name="GrossPriceP"> <ownedAttribute xmi:id="_dujQI6W-EduyI4lEFgFo8g" name="CurrNetPrP"> <ownedAttribute xmi:id="_eNZISqW-EduyI4lEFgFo8g" name="GrossPriceP"> <ownedAttribute xmi:id="_eNi5QKW-EduyI4lEFgFo8g" name="PrevGpP"> <ownedAttribute xmi:id="_eNsqQqW-EduyI4lEFgFo8g" name="CurrNetPrP"> <ownedAttribute xmi:id="_eNsqSqW-EduyI4lEFgFo8g" name="PrevNetPrP"> <ownedAttribute xmi:id="_fRV4kqW-EduyI4lEFgFo8g" name="CurrNetPrP"> <ownedAttribute xmi:id="_fRV4mqW-EduyI4lEFgFo8g" name="GrossPriceP"> <ownedAttribute xmi:id="_fgsjMqW-EduyI4lEFgFo8g" name="EstPrice"> <ownedAttribute xmi:id="_fg1tJaW-EduyI4lEFgFo8g" name="ActGrossPr"> Module-3560 Module-3560/003.Design/003.Data.efx <ownedAttribute xmi:id="_xM44IKW-EduyI4lEFgFo8g" name="UnitPrice"> <ownedAttribute xmi:id="_xOYF8KW-EduyI4lEFgFo8g" name="UnitPrice"> <ownedAttribute xmi:id="_xdbOkKW-EduyI4lEFgFo8g" name="AmendPrice"> ``` But when I've made the change it appears as (truncated): ``` $ ./findEllipseType.sh AveragePrice running, searching for instances of A/AveragePrice Module-3110 Module-3110/003.Design/003.Data.efx <ownedAttribute xmi:id="_P7sUcAg7EdysLcyAWbYneA" name="EstPrice"> Module-3140 Module-3140/003.Design/003.Data.efx <ownedAttribute xmi:id="_Z2v4IaW-EduyI4lEFgFo8g" name="StoresSalePrice"> <ownedAttribute xmi:id="_bpCVJaW-EduyI4lEFgFo8g" name="StoresSalePrice"> <ownedAttribute xmi:id="_j0crsMnVEeOm9cfLF6jryw" name="EstPrice"> Module-3210 Module-3210/003.Design/003.Data.efx <ownedAttribute xmi:id="_dYMlpaW-EduyI4lEFgFo8g" ``` Looking through the results it appears as though the `for in` is using a space delimiter when I want it to use a line delimiter. How do I change this please?

Original source