Search code examples
bashshellgithub-actionsglob

Bash - Find files from a specific directory


I am new to bash (but not to programming). I have a bash script that looks for all .txt files in a project

for i in `find . -name "*.txt"`;
do

 basename= "${i}"
 cp ${basename} ./dest
done

However, I would like to get the .txt files only from a specific sub directory. For e.g this is my project structure:

project/
├── controllers/
│   ├── a/
│   │   ├── src/
│   │   │   ├── xxx
│   │   │   └── xxx
│   │   └── files/
│   │       ├── abc.txt
│   │       └── xxxx
│   └── b/
│       ├── src/
│       │   ├── xxx
│       │   └── xxx
│       └── files/
│           ├── abcd.txt
│           └── xxxx
├── lib
└── tests

I would like to get .txt files only from controllers/a/files and controllers/b/files. I tried replacing find . -name "*.txt" with find ./controllers/*/files/*txt, it works fine, but errors out on GitHub actions with No such file or directory found. So I'm looking for a more robust way of finding .txt files from the subdirectory without having to hardcode the path in the for loop. Is that possible?


Solution

  • You can use brace expansion for the search directory, e.g.

    find ./project/controllers/{a,b} -type f -name "*.txt"
    

    To select file only below ./project/controllers/a and ./project/controllers/b

    Additionally, your use of basename would no longer be needed in your script (and cure the error with the ' ' (space) to the right of the '=' sign. Traditionally in bash, you will use process substitution to feed a while loop rather than using a for loop, e.g.

    while read -r fname; do
      # basename="${fname}"     # note! no ' ' on either side of =
      cp -ua "$fname" ./dest
    done < <(find ./project/controllers/{a,b} -type f -name "*.txt")
    

    Edit Based On Comment of Many Paths

    If you have many controllers not just a and b, then using the -path option instead of the -name options can provide a solution, e.g.

    find . -path "./project/controllers/*/files/*.txt" -type f
    

    would select any ".txt" files below any directory below controllers that contains a files directory.