Skip to content
Advertisement

Iterating over lines in a file with “for” in shell never matching target

i wrote a shell script as you can see followed. The script reads all line which in .dat file and writes screen matching words according to specified parameter but if/else block doesn’t properly.Both block if and else fired same time.

sh file

#!/bin/bash
p="*.dat"
k="$1"
for f in $p
do
    if [ "$k" == "$f" ]; then
        echo "$f present"
    else
        echo "not found $k"
    fi
    cat $f
done

dat file

lorem
ipsum
loremium
dolor
sit
amet
ipso

terminal

$ ./loc.sh lor

result

not found lor
lorem
ipsum
loremium
dolor
sit
amet
ipso

Advertisement

Answer

The original code didn’t ever look inside of a file until after running the comparison — it merely compared the name of each .dat file to the target, and allowed only exact matches (not substrings).

Consider instead:

while read -r line; do
  if [[ $line = *"$1"* ]]; then
    echo "$1 present in $line"
  else
    echo "$1 not found in $line"
  fi
done < <(cat *.dat)
  • Using cat *.dat combines all the files into a single stream. Enclosing this in <(cat *.dat) generates a filename which can read from to yield that stream; using < <(cat *.dat) redirects stdin from this file (within the scope of the while loop for which this redirection takes place).
  • Using while read processes an input stream line-by-line (see BashFAQ #1).
  • Using a test of [[ $line = *"$1"* ]] allows the target (contents of $1) to be found inside a line, instead of only matching when $1 matches the entire line as a whole. You can also have this effect with [[ $line =~ "$1" ]]. Note that the quotes are mandatory for correct operation in either of these cases.
  • Using a for loop to iterate over lines is extremely poor practice; see Don’t Read Lines With For. If you want to use a for loop, use it to iterate over files instead:

    for f in *.dat; do
      # handle case where no files exist
      [[ -e "$f" ]] || continue 
      # read each given file
      while read -r line; do
        if [[ $line = *"$1"* ]]; then
          echo "$1 present in $line in file $f"
        else
          echo "$1 not present in $line in file $f"
        fi
      done <"$f"
    done
    
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement