Kate D.
Kate D.

Reputation: 23

bash script not filtering

I'm hoping this is a simple question, since I've never done shell scripting before. I'm trying to filter certain files out of a list of results. While the script executes and prints out a list of files, it's not filtering out the ones I don't want. Thanks for any help you can provide!

#!/bin/bash

# Purpose: Identify all *md files in H2 repo where there is no audit date
#
#
#
# Example call: no_audits.sh
#
# If that call doesn't work, try ./no_audits.sh
#
# NOTE: Script assumes you are executing from within the scripts directory of
#       your local H2 git repo.
#
# Process:
# 1) Go to H2 repo content directory (assumption is you are in the scripts dir)
# 2) Use for loop to go through all *md files in each content sub dir
#    and list all file names and directories where audit date is null
#

#set counter
count=0

# Go to content directory and loop through all 'md' files in sub dirs
cd ../content

FILES=`find .  -type f -name '*md' -print`

for f in $FILES
do

   if [[ $f == "*all*" ]] || [[ $f == "*index*" ]] ;
   then
      # code to skip
      echo " Skipping file:  " $f
      continue
   else
   # find audit_date in file metadata
   adate=`grep audit_date $f`

   # separate actual dates from rest of the grepped line
   aadate=`echo $adate | awk -F\' '{print $2}'`

   # if create date is null - proceed
      if [[ -z "$aadate" ]] ;
      then

         # print a list of all files without audit dates
         echo "Audit date: " $aadate " " $f;
         count=$((count+1));
      fi
   fi
done
echo $count " files without audit dates "

Upvotes: 0

Views: 147

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295736

First, to address the immediate issue:

[[ $f == "*all*" ]]

is only true if the exact contents of f is the string *all* -- with the wildcards as literal characters. If you want to check for a substring, then the asterisks shouldn't be quoted:

[[ $f = *all* ]]

...is a better-practice solution. (Note the use of = rather than == -- this isn't essential, but is a good habit to be in, as the POSIX test command is only specified to permit = as a string comparison operator; if one writes [ "$f" == foo ] by habit, one can get unexpected failures on platforms with a strictly compliant /bin/sh).


That said, a ground-up implementation of this script intended to follow best practices might look more like the following:

#!/usr/bin/env bash
count=0
while IFS= read -r -d '' filename; do
  aadate=$(awk -F"'" '/audit_date/ { print $2; exit; }' <"$filename")
  if [[ -z $aadate ]]; then
    (( ++count ))
    printf 'File %q has no audit date\n' "$filename"
  else
    printf 'File %q has audit date %s\n' "$filename" "$aadate"
  fi
done < <(find . -not '(' -name '*all*' -o -name '*index*' ')' -type f -name '*md' -print0)
echo "Found $count files without audit dates" >&2

Note:

  • An arbitrary list of filenames cannot be stored in a single bash string (because all characters that might otherwise be used to determine where the first name ends and the next name begins could be present in the name itself). Instead, read one NUL-delimited filename at a time -- emitted with find -print0, read with IFS= read -r -d ''; this is discussed in [BashFAQ #1].
  • Filtering out unwanted names can be done internal to find.
  • There's no need to preprocess input to awk using grep, as awk is capable of searching through input files itself.
  • < <(...) is used to avoid the behavior in BashFAQ #24, wherein content piped to a while loop causes variables set or modified within that loop to become unavailable after its exit.
  • printf '...%q...\n' "$name" is safer than echo "...$name..." when handling unknown filenames, as printf will emit printable content that accurately represents those names even if they contain unprintable characters or characters which, when emitted directly to a terminal, act to modify that terminal's configuration.

Upvotes: 2

Kate D.
Kate D.

Reputation: 23

Nevermind, I found the answer here:

bash script to check file name begins with expected string

I tried various versions of the wildcard/filename and ended up with:

if [[ "$f" == *all.md ]] || [[ "$f" == *index.md ]] ;

The link above said not to put those in quotes, and removing the quotes did the trick!

Upvotes: 0

Related Questions