Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating through grep results line by line not delimited by space

Tags:

grep

bash

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?

like image 618
Metalskin Avatar asked Feb 12 '23 23:02

Metalskin


1 Answers

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
like image 50
mklement0 Avatar answered Apr 28 '23 00:04

mklement0