0

I have a directory which has multiple subdirectories in it. I am looping through the subdirectories one by one. Then I change the directory using cd. If in that sub directory, I have a file having an extension .partial, save the file name. Then I nee to print the sub directory name and partial file name. I have written something like this

for f in *
do
    if [ -d "$f" ]; then
        cd "$f"
        a=$(find ./ -name "*.partial")
        if [ -e "$a" ];then
            echo -e "$f" "\t" "$a"
        fi
        cd ..
    fi
done

The problem with find is that it recursively looks for .partial file extensions in further sub directories, which I don't want. How can I restrict it to only current directory?

2
  • Stepping backwards from your process description a moment, what is it you're actually trying to achieve? Is it to list all the .partial files in each subdirectory? Jan 3, 2020 at 22:08
  • 1
    find out files which are downloaded partially and failed so that I can resume their downloads Jan 3, 2020 at 22:09

3 Answers 3

3

To just output the full pathname for each .partial file relative to the current directory:

printf '%s\n' ./*/*.partial

Setting the nullglob shell option in bash would make this not output the pattern itself if there is no match (there would still be an empty line outputted), and setting dotglob would additionally match hidden names.


The following splits these pathnames up in a directory path and a filename part:

for name in ./*/*.partial; do
    [ -f "$name" ] && printf '%s\t%s\n' "${name%/*}" "${name##*/}"
done

This would print the subdirectory name and the .partial filename for each .partial name found in any subdirectory of the current directory.

In bash, you may additionally want to set the shell option dotglob if you need to be able to match hidden names as well.

The two parameter substitutions could be replaced by calls to dirname and basename:

for name in ./*/*.partial; do
    [ -f "$name" ] && printf '%s\t%s\n' "$(dirname "$name")" "$(basename "$name")"
done

A variation of the above for printing the subdirectory name only once (this requires a bash-like shell for the ${*%%*/} parameter expansion which removes the directory paths and leaves the filenames, for each positional parameter):

IFS=$'\t'
for dirname in ./*/; do
    set -- "$dirname"/*.partial;
    [ -f "$1" ] && printf '%s\t%s\n' "$dirname" "${*##*/}"
done

This loops over each subdirectory and expands the *.partial globbing pattern in each. If the first match is a regular file (or a link to one), the subdirectory name is printed followed by the names that matched the pattern, with the directory path removed.

5
  • Doesn't that print the directory name for each file? My understanding is that output should be directory1 file1 file2 ... fileN, one line per directory Jan 3, 2020 at 23:03
  • @roaima This is how I interpreted "Then I nee to print the sub directory name and partial file name."
    – Kusalananda
    Jan 3, 2020 at 23:13
  • @roaima I added a bit of code for your interpretation of the question as well.
    – Kusalananda
    Jan 3, 2020 at 23:27
  • Actually with or without -a, that won't work properly to have two columns with dir and file as zsh sorts the list. Adding oN to disable sorting would help, but the list wouldn't be sorted. Jan 4, 2020 at 13:32
  • @StéphaneChazelas I've deleted that bit for now.
    – Kusalananda
    Jan 4, 2020 at 14:23
2

The find utility supports the option -maxdepth

-maxdepth levels
Descend at most levels (a non-negative integer) levels of directories below the starting-points. -maxdepth 0 means only apply the tests and actions to the starting-points themselves.

To restrict find to the specified directory, you can add the option -maxdepth 1.


If you're trying to list all regular files *.partial at depth 2 with an output similar to that in your question, you could try a command such as

 find . -mindepth 2 -maxdepth 2 -type f -name '*.partial' -printf '%h\t./%f\n'

which could give an output similar to

./b    ./file2.partial
./b    ./file3.partial
./a    ./file1.partial
0
1

If you have GNU find you can do something like this

for dir in *
do
    files=$(find "$dir" -maxdepth 1 -type f -name '*.partial' -printf "%f\n" | xargs)
    [[ -n "$files" ]] && printf "%s\t%s\n" "$dir" "$files"
done

If you don't have GNU find you can emulate the -printf '%f\n' at the expense of some efficiency, like this

for dir in *
do
    files=$(cd "$dir" && find . -maxdepth 1 -type f -name '*.partial' -print | sed 's!^./!!' | xargs)
    [[ -n "$files" ]] && printf "%s\t%s\n" "$dir" "$files"
done

Neither solution handles files with newlines or strange-printing characters, but if you are listing files in a directory on a single line I would expect you not to have weird filenames. If you have a shell that doesn't understand [[ ... ]] just replace the double brackets with single brackets, [ ... ].

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .