theGrayFox
theGrayFox

Reputation: 931

Counting Files with Bash

I'm trying to keep my function inspect_files to the principle of single responsibility, I know I could easily calculate the file count inside of that function and print it there, but is there a more elegant solution to count files than this?

(Note, I don't want a recursive file count.)

# Snippet:

dir_name="$HOME/$1"

inspect_files() {
  local content
  for content in "$dir_name"/*; do
    printf "%s\n" "${content##*/}"
  done
  count_files "$dir_name"
}

count_files() {
  local count
  local dir="$1"
  count=$(ls -1 "$dir_name" | wc -l)
  printf "\nTotal: %d\n" "$count"
}

if is_found "$dir_name"; then
  inspect_files
else
  echo 'Not a directory.'
fi

Upvotes: 1

Views: 167

Answers (3)

Marco Carlo Moriggi
Marco Carlo Moriggi

Reputation: 414

I think that a more elegant and performant solution to count files can be this one:

find "$dir_name" -mindepth 1 -maxdepth 1 -ignore_readdir_race -printf x | wc -m

So that your counting fuction would look like this:

count_files() {
  local count
  local dir="$1"
  count=$(find "$dir_name" -mindepth 1 -maxdepth 1 -ignore_readdir_race -printf x | wc -m)
  printf "\nTotal: %d\n" "$count"
}

This version works by printing an "x" for each item in the directory, then counting the characters with wc -m. It has a very low memory footprint, does no sorting and you can redirect output to a file to count asynchronously - for very large directories - (side-note: this counts also hidden files and subdirs, so you eventually have to filter them out with -and -not -name '.*').

Tip: -ignore_readdir_race makes find ignore errors due to deletion of files during the operation (maybe you don't want to stop operating on the files in the directory during the count).

There is plenty of questions about counting files with bash, so I imagined that it can be helpful to use a function (like you did) to simplify the operation. I'll copy your approach and put it in my .barshrc and .zshrc ;-)

Upvotes: 0

clt60
clt60

Reputation: 63902

The next function:

inspect_files() {
    find "$1/" -type f -maxdepth 1 -print0 | grep -zc .
}

return the plain file count in a given direcotry, e,g:

 tmpcount=$(inspect_files /tmp)
 echo "File count in /tmp is: $tmpcount"

if you want count everything (not only plain files) use a simlper:

inspect_files() {
    find "$1/" -maxdepth 1 -print0 | grep -zc .
}

The slash after "$1/" ensure you counting in the directories what are symbolic links, like

$ ls -ld /tmp
lrwxr-xr-x@ 1 root  wheel  11 27 jul  2012 /tmp -> private/tmp

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 753665

What about:

list_and_count_files()
{
  local content
  local count
  count=0
  for content in "${1:-.}"/*; do
    printf "%s\n" "${content##*/}"
    ((count++))
  done
  printf "\nTotal: %d\n" "$count"
}

This name is a more accurate description of what your original function does. Note that the directory to be analyzed is passed as an argument to the function, but defaults to . (the current directory) if you don't pass anything. This decouples the function from the variable $dir_name, which is much better for isolation.

Upvotes: 1

Related Questions