soapergem
soapergem

Reputation: 9989

How can I find all the folders that contain certain file extensions in Bash?

I need to write a Bash script (preferably a one-liner) to find all the subdirectories under the current directory which contain any files with a certain file extension, namely .tf. The tricky part is that there may be more than one .tf file in a folder, and I need the folders - not the filenames.

So for example, suppose my directory contents look something like this:

.
├── docs
│   ├── README.md
│   └── diagram.png
├── project
│   └── main.py
├── Makefile
├── terraform
│   ├── environments
│   │   ├── prod
│   │   │   └── main.tf
│   │   └── staging
│   │       └── main.tf
│   └── module
│       ├── ecs.tf
│       ├── rds.tf
│       ├── s3.tf
│       ├── security_group.tf
│       ├── sqs.tf
│       └── variable.tf
├── tests
|   └── test_main.py
└── .terraform
    └── ignore_me.tf

In this case I would want the command to return terraform/module, terraform/environments/prod, and terraform/environments/staging. I also need a way to filter out certain directories that should never be included. In the example above note that I did not include the dot-file directory .terraform.

I already know how to write the following find command, which can find the .tf files themselves:

find . -name '*.tf' -not -path '*.terraform*'

However I'm not sure how to write a command to find the directories which contain those files.

Upvotes: 7

Views: 4116

Answers (4)

anubhava
anubhava

Reputation: 785008

This find command should also work:

find . -type d -not -path '*/.terraform*' \
-exec bash -c 'shopt -s failglob; ( : "$1"/*.tf ) 2>/dev/null && echo "$1"' - {} \;
  • failglob returns failure if glob pattern fails.
  • find command scans each directory in current path and bash -c checks for presence of *.tf files in each directory.

Upvotes: 1

melpomene
melpomene

Reputation: 85767

You could do it like this:

shopt -s globstar  # to enable support for **

for i in **/*.tf; do echo "${i%/*}"; done | sort -u

If you don't have ** enabled already, the shopt line will do it for you.

We iterate over all *.tf files in all subdirectories and print out their directory names (by chopping off all trailing /* components). sort -u is used to remove duplicates.

Upvotes: 0

Craig
Craig

Reputation: 776

try this:

shopt -s globstar

echo **/*.tf | xargs dirname | sort -u

Upvotes: 1

larsks
larsks

Reputation: 311476

You can use find's -printf action to print out just the directory name, and then remove duplicates:

find . -name '*.tf' -not -path '*.terraform*' -printf '%h\n' | sort -u

The -printf '%h\n' prints out the name of the directory containing matched files, while the sort -u at the end removes duplicates.

Upvotes: 14

Related Questions