Zaratruta
Zaratruta

Reputation: 2275

Iterating over file (and directory) names with bash

I was trying to write a bash script for counting the number of files and the number of directories of the local directory. This was my first try:

#!/bin/bash
files=0
dir=0
for file in `ls`
do
    if [ -d $file ]
    then
        dir=$(($dir+1))
    else
        files=$(($files+1))
    fi
done 
echo "files=$files, direcotries=$dir"

However, the for command is not iterating over the names of files and directories as I would expect. If the name of the file ou directory has spaces, this does not work well. When there are spaces in the names, the variable "file" assumes the value of each of the words in the file (or directory) name.

Is there any way to do this?

Upvotes: 1

Views: 941

Answers (2)

John1024
John1024

Reputation: 113814

I think that Jonathan Leffler's answer is exactly what you need.

An alternative that shows the power of bash arrays and eliminates the need for loops:

shopt -s nullglob
dirs=(*/)
ndir="${#dirs[@]}"
files=(*)
nfile=$(( "${#files[@]}" - ndir))
echo "files=$nfile, directories=$ndir"

This works as follows:

  • dirs=(*/) creates an array of the names of the directories.

  • ndir="${#dirs[@]}" counts the number of directories.

  • files=(*) creates an array of the names of all files and directories.

  • nfile=$(( "${#files[@]}" - ndir)) computes the number of files by taking the number of elements of files and subtracting from that the number of directories.

  • echo "files=$nfile, directories=$ndir" prints out the results.

This will only work in a shell, like bash, that supports arrays. On many systems, sh is dash which does not support arrays. So, use bash script when executing this script.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 753525

Use a wild card: for file in *; do …; done. That keeps the spaces in the names correct. Consider shopt -s nullglob too. Neither your code nor my suggestion lists names starting with a dot ..

Also, use if [ -d "$file" ] with double quotes around the variable value to avoid spacing problems.

Hence:

#!/bin/bash

shopt -s nullglob
files=0
dir=0
for file in *
do
    if [ -d "$file" ]
    then
        dir=$(($dir+1))
    else
        files=$(($files+1))
    fi
done 
echo "files=$files, directories=$dir"

In Bash, there are also other ways of writing the arithmetic, such as ((files++)).

Upvotes: 1

Related Questions