I want to get the file count & file names & folder names in directory:
mkdir -p /tmp/test/folder1
mkdir -p /tmp/test/folder2
touch /tmp/test/file1
touch /tmp/test/file2
file_names=$(find "/tmp/test" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} basename "{}")
echo $file_names
here is the output:
file2 file1
For folder:
folder_names=$(find "/tmp/test" -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -I {} basename "{}")
echo $folder_names
here is the output:
folder2 folder1
For count:
file_count=0 && $(find "/tmp/test" -mindepth 1 -maxdepth 1 -type f -print0 | let "file_count=file_count 1")
echo $file_count
folder_count=0 && $(find "/tmp/test" -mindepth 1 -maxdepth 1 -type d -print0 | let "folder_count=folder_count 1")
echo $folder_count
The file_count
and folder_count
does not work
Question 1:
How to get the correct file_count
and folder_count
?
Question 2:
Is it possible for getting names into an array
and check the count from array size
?
CodePudding user response:
The answer to the second question is really the answer to the first, too.
mapfile -d '' files < <( find /tmp/test -type f \
-mindepth 1 -maxdepth 1 \
-printf '%f\0')
echo "${#files} files"
printf '%s\n' "${files[@]}"
The use of double quotes and @
in the array expansion are essential for printing file names with whitespace correctly. The use of a null byte terminator between file names ensures that even newlines in file names are disambiguated.
Notice also the use of -printf
with a specific format string to avoid having to run basename
separately. However, the -printf
option and its various format strings, as well as the -print0
option you used, are a GNU find
extension, and thus not portable. (Linux typically ships with GNU tools; on other platforms, they are obviously easy to install, but typically not installed out of the box.)
If you have an older version of Bash which doesn't support mapfiles
, try an explicit loop:
files=()
while IFS= read -r -d $'\0' file; do
files =("$file")
done < <(find ...)
If you don't have GNU find
, a common workaround is to print a fixed string for each found file, and then the line or character count reliably reflects the number of found files.
find /tmp/test -type f \
-mindepth 1 -maxdepth 1 \
-exec printf . \; |
wc -c
Though then, how do you collect the file names? If (as in your case) you don't require recursion into subdirectories, simply loop over all items in the directory. In which case, again, the number of items in the collected array will also tell you how many there are.
files=()
dirs=()
for item in /tmp/test/*; do
if [[ -f "$item"]]; then
files =("$item")
elif [[ -d "$item" ]]; then
dirs =("$item")
fi
done
echo "${#dirs[@] directories}
printf '- %s\n' "${dirs[@]}"
echo "${#files[@]} files"
printf '%s\n' "${dirs[@]}"
For a further discussion, see also https://mywiki.wooledge.org/BashFAQ/020
Needlessly collecting items into an array so you can loop over it once is a common beginner antipattern. If you just want to process each item in isolation and then forget it, there is no need to waste memory on remembering all the other items - just loop over them directly.
As an aside, running find
in a subprocess will create a new shell instance with its own set of variables; thus in your attempt, the pipe to let
would increment from 0 to 1 each time you ran it (though of course, piping to let
also does not do anything useful anyway).