3

The directory tree is like this:

.
├── A_123
│   └── 123.txt
├── A_456
│   ├── tmp
│   └── tmp.log
└── A_789
    └── 789.txt

There're 3 directories (A_123, A_456, A_789). The pattern of a directory name is: A_{numbers} and the file I'm interested in is {numbers}.txt.

I was wondering whether there's a way to get the directories A_{numbers} that has no {numbers}.txt file in them. For example above, this script should return:

./A_456

as A_456 doesn't have 456.txt in its folder but A_123 and A_789 have their {numbers}.txt files in the relevant folder.

Anyone has ideas about this? Thanks!

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
Hanfei Sun
  • 45,281
  • 39
  • 129
  • 237

4 Answers4

4

Here's one approach:

for dir in *;do 
  if [ $(find "$dir" -maxdepth 1 -regex '.*/[0-9][0-9]*\.txt' | wc -l) = 0 ]; then 
    echo $dir
  fi
done
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • 1
    There are a couple of problems with this. `*` will also match files. The regex will match a file named ".txt". Using an ERE regextype, you can say `[0-9]+`. There are some similar matching issues in my answer though. The `-regex` option for find is not POSIX, but should be fine since the question is tagged linux. – jordanm Oct 30 '12 at 06:11
  • 1
    If for some reason, `A_123` directory contains `000.txt` (but not `123.txt`), the directory won't be echoed. Although from OP's sample, it seems that the text file's name always matches the folder's number (if it exists) so this might not be a concern. – doubleDown Oct 30 '12 at 13:23
1

Since the A_[0-9] directories are not nested, you can easily do this with a glob in a loop. This implementation is pure bash, and does not spawn in external utilities:

for d in A_[0-9]*/; do         # the trailing / causes only directories to be matched
   files=("$d"/[0-9]*.txt)     # populate an array with matching text files
   ((!${#files})) && echo "$d" # echo $d if the array is empty
done

There are some problems with this implementation. It will match a file such as "12ab.txt" and requires loading all the filenames for a directory into the array.

Here is another method in bash that does a more accurate filename matching:

re='^[0-9]+[.]txt$'
for d in A_[0-9]*/; do
   for f in *; do
      if [[ -f $f && $f =~ $re ]]; then
         echo "$d"
         break
      fi
   done
done
jordanm
  • 33,009
  • 7
  • 61
  • 76
1

A slight variation on a couple of other answers: use bash extended pattern matching:

shopt -s extglob nullglob
for dir in  A_+([0-9]); do
    files=($dir/+([0-9]).txt)
    (( ${#files[@]} == 0 )) && echo $dir
done
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
0
for file in *; do
  if [[ "$file" =~ ^A_([0-9]+)$ && ! -f "$file/${BASH_REMATCH[1]}.txt" ]]; then
    echo $file;
  fi
done

How it works:

  1. Checks using regexp (note the =~ ) that the file/folder's name is "A_" followed by number.
  2. At the same time, it captures the numbers (note the parentheses) and stores them in ${BASH_REMATCH[1]}
  3. Next, check if the folder contains {number}.txt.
  4. If it does not, echo the folder's name.
doubleDown
  • 8,048
  • 1
  • 32
  • 48