1

learning bash scripting and i'm trying to filter results with a nested grep in a for loop:

sample filenames:
  this-file_AAC.txt
  this-other_file_AAAC.txt
  yep-a-file_AAC.nfo
  oops_another_one.reamde

and this is the script:

shopt -s globstar nullglob dotglob
for file in ./**/*.{txt,reamde,nfo}; do
  printf "input: ${file}\n"
  if grep -qF _AAC $file; then
    printf "output: ${file}\n"
    # do stuff with $file
  fi
done

the idea being any file that contains _AAC will have operations applied to it; in this specific case, just print to stdout to confirm the results are correct.

problem is: nothing makes it past the if statement. so i'm missing i-don't-know-what. all that works is the printf "input... line.

what am i missing?

5
  • grep is looking inside the content of the file... are you trying to apply a command if the name of the file contains _AAC? Nov 3, 2021 at 21:58
  • yes. i thought grep worked on a filename as well... sounds like it doesn't?
    – WhiteRau
    Nov 3, 2021 at 21:59
  • 1
    for file in ./**/*_AAC*.{txt,reamde,nfo}; do ... Nov 3, 2021 at 22:07
  • @roaima can this be negated like so for file ! in ./**/*_AAC*....?
    – WhiteRau
    Nov 3, 2021 at 22:19
  • 1
    No, but your question wanted matches, not anti-matches Nov 3, 2021 at 22:40

2 Answers 2

5

If you want to match all files that are named like .../something_AACxyz, with _AAC in the last filename part, just add the _AAC part directly to the glob pattern/wildcard:

for file in ./**/*_AAC*.{txt,reamde,nfo}; do

If you want filenames where _AAC can appear in any part, you could do (in Bash):

for file in ./**/*.{txt,reamde,nfo}; do
    if [[ $file == *_AAC* ]]; then
         echo "'$file' contains _AAC"
    else
         echo "'$file' has NO _AAC"
    fi
done

Here, you also get the files that match the globs, but don't have _AAC.


In any case, on Bash, you could also enable extglob and rewrite the pattern with @(...|...) instead of the brace expansion:

for file in ./**/*_AAC*.@(txt|reamde|nfo); do

The brace expansion produces multiple distinct globs, while with @(..) it's just a single glob. (i.e. *.{foo,bar} is the same as *.foo *.bar) With nullglob set the non-matching ones don't matter, but if you had failglob set, just one non-matching one would be enough to fail the whole command.

1
  • this is fantastic! thank you! wow. this has been a very productive and useful post! thanks again @ikkachu.
    – WhiteRau
    Nov 4, 2021 at 14:41
2

Where your script says:

  if grep -qF _AAC $file; then

it is saying, "if the contents of the file named $file contain the literal string _AAC then ...." execute the contents of the then block.

If what you mean is to instead search for _AAC in the string resulting from the expansion of the variable $file (i.e. to check whether _AAC appears in the filename itself), then in bash, you need to write:

  if grep -qF _AAC <<< "$file"; then

Or, as @roaima points out, the literal string you're searching for can be included in the wildcard file specification.

6
  • 1
    Just use a glob. There's no need for grep here at all Nov 3, 2021 at 22:08
  • And it's worth noting the variable surrounded by doueble quotes, which OP doesn't have. Nov 3, 2021 at 22:09
  • 1
    and even if you have the filename in a variable, use case $file in *_AAC*) ... ;; esac or [[ $file == *_AAC* ]] in shells that have it
    – ilkkachu
    Nov 3, 2021 at 22:09
  • boop. that's the bunny right there. thank you! very useful knowledge. much appreciated. :)
    – WhiteRau
    Nov 3, 2021 at 22:09
  • @roaima don't know how to use a glob in bash. PHP, yes. Python, yes. Bash, no. :P
    – WhiteRau
    Nov 3, 2021 at 22:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .