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!

4 Answers 4

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 
Sign up to request clarification or add additional context in comments.

2 Comments

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.
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.
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 

Comments

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 

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.