1

I am new to bash scripts. I would like to compare the number of files I have in another directory using ls. And then I need to compare the number of files there with a variable I have.

if [ (cd reports_dir/ && ls) | wc  -gt $MAXIMUM_REPORTS ]; then
    echo hello
fi

This code gives the errors:

./monitor.sh: line 70: syntax error near unexpected token `cd'
./monitor.sh: line 70: `    if [(cd reports_dir/ && ls) | wc  -gt $MAXIMUM_REPORTS]; then'

I have idea why cd is unexpected. The command (cd reports_dir/ && ls) | wc works when I run it in the terminal.

This command would be running in a while loop so it will be called repeatedly. Thus I cannot actually cd into the directory as it attempts to cd more than once, resulting in an error.

peak
  • 105,803
  • 17
  • 152
  • 177

3 Answers3

5

techraf's helpful answer explains the problem with the OP's approach and offers an effective solution based on ls.

Since only counting of files is required in this case, use of ls ... | wc -l should work as expected; however, it is generally preferable to work with pathname expansion (globbing) in Bash:

  • Globbing can safely be used to collect filenames in an array and to drive a for loop.
  • Globbing, since it is a Bash-internal feature, is more efficient than using ls, which is an external utility.

The following solutions demonstrates the use of pathname expansion (globbing) as an alternative to ls:

Note: By default, globbing does not include hidden files; set shopt -s dotglob (temporarily) to include them.

if (( $(shopt -s nullglob; set -- reports_dir/*; echo $#) > $MAXIMUM_REPORTS )); then
    echo hello
fi
  • shopt -s nullglob ensures that nothing is returned in case the directory is empty.
  • set -- reports_dir/* sets the positional parameters ($1, ...) to the result of the pathname expansion.
  • $# returns the count of set parameters, which in this case reflects the count of files.
Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
3

The command fails because you need to use command substitution syntax, otherwise it expects a value.

As you noted, you shouldn't be changing directory with cd - you can use the directory as an argument to ls. Also wc command by default returns a count for the number of lines, words, and bytes and in your expression you need only the first one, so you should add an argument wc -l.

Other than that you need to decide if you want to include hidden files and use ls -A <dir> | wc -l. -A makes ls to print all files including hidden ones, but excluding default . and ...

Finally use the double parentheses for arithmetical comparison in the condition clause:

if (( $(ls -A reports_dir | wc -l) > $MAXIMUM_REPORTS )); then
    echo hello, but read the comment below
fi

Having said that, the above recipe will work in majority of cases, however mklement0's answer contains the correct solution.

Community
  • 1
  • 1
techraf
  • 64,883
  • 27
  • 193
  • 198
  • I like this answer more as it is easier to understand and works for most cases. – Avinash Prabhakar Feb 06 '16 at 05:47
  • I am using this script within another bunch of scripts so I just looked the the "hello" output to check if it's working. I didn't know that worked too. Thanks. – Avinash Prabhakar Feb 06 '16 at 06:05
  • Do not use `ls` output for anything. `ls` is a tool for interactively looking at directory metadata. Any attempts at parsing `ls` output with code are broken. Globs are much more simple AND correct. Read [Parsing ls](http://mywiki.wooledge.org/ParsingLs) – Rany Albeg Wein Feb 06 '16 at 06:39
  • @techraf Indeed. Your comments regarding the answer are fine. I just don't like the fact that this answer is the accepted answer, since people with the same problem tend to not read the entire thing and just use the bad code from the answer which has the green checkmark. If your answer is about giving a reference to the correct answer, my logic says that you should give this answer an up-vote instead. – Rany Albeg Wein Feb 06 '16 at 06:47
  • @RanyAlbegWein I don't like it either. :) – techraf Feb 06 '16 at 06:47
  • I think that when it comes to merely _counting_ files, the `ls` / `wc -l` combo is fine, and certainly easier to write. (The only known pitfall - [behavior of `ls -A` on HP-UX](http://mywiki.wooledge.org/BashFAQ/004) - is probably not a real-world concern to many, and, assuming `ls -a` works predictably everywhere, it can be worked around.) That said, parsing `ls` output for _filenames_ should be avoided, and it's advisable to form a habit of not relying on `ls` and working with globs instead. – mklement0 Feb 09 '16 at 16:50
1

Here is another one , cd into a given directory, get count compare with $MAXIMUM_REPORTS and take it from there. passes both test cases.

 [za-  tools]$ MAXIMUM_REPORTS=2; 
     mydir=font-awesome-4.3.0/ ; 
     if [[ $(cd $mydir && ls -1 $@ | wc -l)  -gt $MAXIMUM_REPORTS ]]; then 
        echo "hello" ;
     else echo "byeee" ; fi

Outputs: hello

[za tools]$ MAXIMUM_REPORTS=18;
  mydir=font-awesome-4.3.0/ ;
  if [[ $(cd $mydir && ls -1 $@ | wc -l)  -gt $MAXIMUM_REPORTS ]]; then 
     echo "hello" ; 
  else echo "byeee" ; 
fi

outputs: byeee

OR:

file_count=$(ls $mydir 2>/dev/null | wc -l) ; 
if [ $file_count -gt $MAXIMUM_REPORTS ]; then echo "hello" ; else echo "byee" ; fi

Simulate wc -l with sed

count=$(ls $mydir 2>/dev/null | sed -n '$=' ) ;
if [ $count -gt $MAXIMUM_REPORTS ]; then echo "$count greater then  $MAXIMUM_REPORTS" ; 
else echo "$count less $MAXIMUM_REPORTS" ; fi
z atef
  • 7,138
  • 3
  • 55
  • 50