0

While trying to make a conditional statement to check the amount of files in a directory, I have stumbled upon a problem. My initial way of writing this script:

ELEM=`ls -l $DIR | wc -l` 
if [ $ELEM -lt 5 ] ; then

works. However I want to move my $ELEM into the conditional parameter block so it can be interpreted when I reach that if statement. I have tried playing around with different combinations of single quotes, double quotes, back ticks, and parentheses.

Does anyone know how to solve this problem?

1
  • Where is the problem? If makes no difference whether you count your files outside or inside the if statement. Commented Jul 8, 2010 at 14:36

5 Answers 5

2

Never use ls in batch mode, use globbing instead. Also avoid backquotes, unquoted variables, and the [ builtin:

shopt -s nullglob  # expand to an empty array if directory is empty
shopt -s dotglob   # also glob dotfiles
files=("$DIR"/*)
count=${#files[@]}
if ((count > 5))
then
  ...
fi
Sign up to request clarification or add additional context in comments.

8 Comments

Why is it bad to use 'ls' in in shell scripts? Why do I need to avoid using back ticks, unquoted variables, and brackets? I thought globbing was used primarily for regex manipulations.
See mywiki.wooledge.org/BashPitfalls, mywiki.wooledge.org/BashFAQ/082, mywiki.wooledge.org/BashFAQ/031, mywiki.wooledge.org/BashGuide/Patterns, mywiki.wooledge.org/BashGuide/Practices. Bash programming has so many pitfalls that you should really read all pages in Greg's wiki related to Bash before writing any scripts.
@Melissa: Globs are not regular expressions.
Heh, I didn't say they were regular expressions.
The matter is made complicated because bash supports both globbing and regexes, with superficially similar syntaxes. To get a reliable file count, set the shell options nullglob and dotglob.
|
0

For some reason it didn't occur to me until just now to use this:

ELEM="`ls -l $DIR | wc -l`"

Thanks for your time.

4 Comments

I'm not sure how that solves your problem; the expression is still evaluated at that line, the result is just quoted
Is there anyway to fix this problem?
Plus that the quoting is wrong ($DIR) is unquoted. Also I don't see the problem at all: it is completely equivalent whether the counting happens as part of an assignment or inside a test.
My intention was to completely avoid assigning the variable if I could. Thank you for your help.
0

What you seem to want to be able to do is something that C allows:

if (elem = countfiles(dir) < 5)

Bash can do it like this:

if (( (elem = $(ls "$DIR" 2>/dev/null | wc -l) ) < 5))

Or you could create a function called countfiles and use it like this:

if (( ( elem = $(countfile "$DIR") ) < 5))

where the function might be something like:

countfiles() {
    find "$1" -maxdepth 1 -type f | wc -l
}

2 Comments

The fourth version won't work on file names containing newlines, and the second one will probably be nonportable unless the behavior of ls w.r.t. file names with newlines is standardized. Why calling two external processes for something that Bash can do without external help?
@Philipp: The point of my answer was not to address the issues with using ls inappropriately or other pitfalls of Bash. You covered those well in your comment attached to your answer. I should have made reference to that in my answer for clarity. The point of my answer was to address the evidence that I saw in the OP's question and various comments that there seemed to be a desire to use C-style side effects to set a variable and test it at the same time. Speaking of filenames, this is required reading.
0

Most of the solutions posted so far are (IMHO) overcomplicated. As long as the ls command on your system supports the -B option (display special characters as octal escape sequences), you can just use:

if [[ $(ls -B "$DIR" | wc -l) -lt 5 ]]; then

Note that while parsing ls is generally a bad idea, in this case all we're trying to do is find the number of listings, so there's a lot less to go wrong than usual. In fact, the only thing that wc -l cares about is the number of lines, so the only thing that could go wrong is that a filename might have a newline in it, and hence be mistaken for two filenames; using the -B option to ls protects against this, so it winds up being safe (at least as far as I can see).

Comments

0

Improved countfiles function (which doesn't get confused by newlines in file names):

countfiles() {
   find "$1" -maxdepth 1 -type f -print0 | tr -dc '\0' | wc -c
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.