I am looking at some shell code that is meant to get the count of the number of files in a directory. It reads:
COUNT=$(ls -1 ${DIRNAME} | wc -l)
What does the -1
part mean? I can't find anything about this in any other questions, just passing references to iterating over files in a directory which isn't what I am looking at. Also, removing it from the command seems to have no effect.
COUNT=$(ls -1 ${DIRNAME} | wc -l)
...is a buggy way to count files in a directory: ls -1
tells ls
not to put multiple files on a single line; making sure that wc -l
will then, by counting lines, count files.
Now, let's speak to "buggy":
ls
handles this is implementation-defined; some versions could double-count such files (GNU systems won't, but I wouldn't want to place bets about, say, random releases of busybox
floating around on embedded routers).${DIRNAME}
allows the directory name to be string-split and glob-expanded before being passed to ls
, so if the name contains whitespace, it can become multiple arguments. This should be "$DIRNAME"
or "${DIRNAME}"
instead....also, this is inefficient, as it invokes multiple external tools (ls
and wc
) to do something the shell can manage internally.
If you want something more robust, this version will work with all POSIX shells:
count_entries() { set -- "${1:-.}"/*; if [ -e "$1" ]; then echo "$#"; else echo 0; fi; }
count=$(count_entries "$DIRNAME") ## ideally, DIRNAME should be lower-case.
...or, if you want it to be faster-executing (not requiring a subshell), see the below (targeting only bash):
# like above, but write to a named variable, not stdout
count_entries_to_var() {
local destvar=$1
set -- "${2:-.}"/*
if [[ -e "$1" || -L "$1" ]]; then
printf -v "$destvar" %d "$#"
else
printf -v "$destvar" %d 0
fi
}
count_entries_to_var count "$DIRNAME"
...or, if you're targeting bash and don't want to bother with a function, you can use an array:
files=( "$DIRNAME"/* )
if [[ -e "${files[0]}" || -L "${files[0]}" ]]; then
echo "At least one file exists in $DIRNAME"
echo "...in fact, there are exactly ${#files[@]} files in $DIRNAME"
else
echo "No files exist in $DIRNAME"
fi
Finally -- if you want to deal with a list of file names too large to fit in memory, and you have GNU find
, consider using that:
find "$DIRNAME" -mindepth 1 -maxdepth 1 -printf '\n' | wc -l
...which avoids putting the names in the stream at all (and thus generates a stream for which one could simply measure length in bytes rather than number of lines, if one so chose).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With