Thursday, November 18, 2021

[SOLVED] How to grep for multiple strings in the same line inside a file with similar pattern?

Issue

I have a file named my.txt:

abc_default_flow

#abc_default_flow -p sam  
abc_default_flow -p sam 

# abc_default_flow -p david
abc_default_flow -p david -z what_is_it

I want to match a particular line which has multiple strings and want to match the exact line which contains all the strings.

I tried the below piece of code, but as soon as it matches the partial string it comes out of the loop, rather than actual line which contains all the strings.

#!/bin/bash -f

f_name=abc_default_flow
p_name=sam

file_content=./my.txt
#echo "file_content: $file_content"

while IFS= read -r file_line 
do
  echo $file_line
  if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -E "${f_name}|${p_name}"; then
  #if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -v "${f_name}\|${p_name}"; then
  #if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && [[ $file_line =~ $f_name  ]] && [[ $file_line =~ $p_name  ]]; then
     if [[ -z "$p_name" ]]; then 
         f_name=${f_name}_${p_name}
     fi
     
     echo "f_name: ${f_name}"
     break
  fi
done < $file_content

What would be the right way to grep or use any other process to find the right line within the file?

Update: With the below code I am able to get the output, but is there any simple way with grep or sed or awk to find the result in single line instead of nested if loops.

#!/bin/bash -f

f_name=abc_default_flow
p_name=david

file_content=./my.txt
echo "f_name: $f_name, p_name: $p_name"

while IFS= read -r file_line 
do
  if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]]; then
    echo "l1"
    if [[ ! -z "$f_name" ]] && [[ $file_line =~ "$f_name" ]]; then
      echo "l2, $file_line, $f_name, $p_name"
      if [[ ! -z "$p_name" ]] && [[ $file_line =~ "$p_name" ]]; then
         f_name=${f_name}_${p_name}
         echo "f_name: ${f_name}"
         break
      elif [[ ! -z "$p_name" ]] && [[ ! $file_line =~ "$p_name" ]]; then
         continue 
      else
         break
      fi 
    fi 
  fi
done < $file_content

Solution

You could use the same [[ ]] style checking for the two strings you're looking for:

if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"* ]] && [[ $file_line == *"$p_name"* ]]; then
    ...
fi

I removed the empty string check since an empty line won't contain $f_name and $p_name anyways.

If you expect sam will always come after abc_default_flow then you could combine the two checks into a single test:

if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"*"$p_name"* ]]; then
    ...
fi

If we look at the script as a whole, it'd be nice to get away from the explicit line-by-line loop. Scripts are more idiomatic when they chain together tools that process entire files. Something like:

sed -r '/^\s*#/d' my.txt | grep "$f_name" | grep "$p_name"


Answered By - John Kugelman