1

I have a bash script where I need to loop over all files in a folder.

This is my currently function to tothat:

function listDir(){

   for file in "$1"/*;
   do 
      echo $file
   done 
}

The problem is that it's not listing hidden files or folder (.hiddenFile). How can I achieve that?

Acording to Kusalananda answer, I've edited my script to looks like:

listDir () {
    for file in $(find "$1" -maxdepth 1 -mindepth 1 -print);
    do
       echo $file
    done
}

However, this is not working with folders with spaces in name. Note that I need the for loop to do other stuff with that file names, not just listing them.

How can I solve that?

  • Kusalananda's answer didn't include a for loop with an echo. Just run find as he suggested without a loop. – Andy Dalton Oct 5 '18 at 13:36
  • If using find, don't loop. If you need to loop, use then loop "within find" using -exec as I just showed below. – Kusalananda Oct 5 '18 at 15:55
2

The * shell globbing pattern does not by default match hidden names.

In bash, you may make it do that by enabling the dotglob shell option. You do this with shopt -s dotglob.

Note that you also would have to quote the expansion of $file in your code, or the shell would perform word splitting and filename globbing on its value.

listDir () (
    shopt -s dotglob
    for pathname in "$1"/*; do
        printf '%s\n' "$pathname"
    done
)

If you need to do other things with $pathname in the loop above, make sure to double quote the expansion of the variable as "$pathname". This would make the function work for filenames that contain all manner of characters.

When calling the function, make sure to quote the directory name if it contains spaces:

listDir "my directory/here somewhere"

Two alternative approaches (assuming that you just need to list the files):

listDir () (
    shopt -s dotglob
    printf '%s\n' "${1:-.}"/*
)

By enclosing the function body in ( ... ) (a subshell), we ensure that he dotglob shell option is not set in the calling shell.

By using ${1:-.} in place of just $1, we use . (the current directory) as the default directory to list if no directory (or an empty string) is given.

The printf utility will reuse its format string if given more arguments than there are placeholders in the formatting string. Here, we use that fact to print out the filenames as a newline-delimited list.

Using find, which does not need dotglob:

listDir () {
    find "${1:-.}" -maxdepth 1 -mindepth 1 -print
}

If you want to do something else with the found files when you are using find, then don't try to loop over the output of find. Instead, use -exec:

listDir () {
    find "${1:-.}" -maxdepth 1 -mindepth 1 -exec sh -c '
        for pathname do
            # code that does something with "$pathname"
        done' sh {} +
}

Related:

  • Thanks for your answer. What you suggest is working fine with hidden files, but, what happens with folder names with spaces? That's why I had "$1" with quotes in my original script, how can I solve this with your answer? – SwDev Oct 5 '18 at 12:24
  • @SwDev Quoting $1 is the right thing to do. This makes your code work on folders with spaces. Note that you would have to quote the folder name on the command line, e.g., listDir "my folder". Without the quotes when calling listDir, $1 would only get the value my in this example. – Kusalananda Oct 5 '18 at 12:26
  • @SwDev See updated answer. – Kusalananda Oct 5 '18 at 15:51
0

Since you've tagged bash, you could also use arrays:

shopt -s dotglob
files=( "$1"/* )

Then you can print them or operate on them:

printf "File: %s\n" "${files[@]}"

or

rm "${files[@]}"

or

for f in "${files[@]}"; do mv "${f}" "renamed-${f}"; done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.