1

I'm writing a shell script that is supposed to take in one parameter (a directory name) and display how many files, directories, readable files, writable files and executable files are in that directory. If a parameter isn't given when you run it, its supposed to display an error message and abort. If the given parameter doesn't exist it should also display an error message and abort. Otherwise it should display the above info. I cannot for the life of me get it to run. Here is what I have, please help!:

   #!/bin/csh
   $1
   set file=$1
       if ($file==0)then
             echo "usage: assignment6.sh <directory_name>"
             exit0
       else
           if (-e $file && -r $file) then
                echo "Number of Directories: `ls | wc -w`"
                echo "Number of Files: `ls -d */ | wc -w`"
                echo "Number of Readable files: `find * -type f -or -type d -maxdepth 0 -perm +u=r | wc -w`"
                echo "Number of Writable files: `find * -type f -or -type d -maxdepth 0 -perm +u=w | wc -w`"
                echo "Number of Executable files: `find * -type f -or -type d -maxdepth 0 -perm +u=x | wc -w`"

            else
                if (! -e $file) echo "No such directory."
                exit 0
            endif
       endif
 exit 0
Wildcard
  • 36,499
Maddie
  • 19

1 Answers1

2

Issues in this script:

  1. You are parsing the output of ls. Don't parse ls.
  2. You are relying on filenames not to contain spaces or newlines. They can contain either.
  3. You're using csh. All by itself, that's a bad idea for shell scripts. Bash, Ksh, Zsh, almost anything but csh is a better idea. (My opinion, but read through the linked factual reasoning.)

Here is a POSIX compliant version of part of this program. (If I have time later I may include the rest of the features.)

This won't handle cases where there are more files than fit in an argument list, but it could be modified to do so if really necessary.

#!/bin/sh

[ "$#" -eq 1 ] && [ -d "$1" ] || {
  printf 'Usage:\t%s <directory>\n' "$0"
  exit 1
}

dirs="$(find "$1" -path '*/*/*' -prune -o -type d -exec sh -c 'printf %s\\n "$#"' sh {} +)"
files="$(find "$1" -path '*/*/*' -prune -o -type f -exec sh -c 'printf %s\\n "$#"' sh {} +)"

printf 'Number of directories in %s:\t%s\n' "$1" "$dirs"
printf 'Number of files in %s:\t%s\n' "$1" "$files"

Since the -maxdepth primary is not portable, I made use of the techniques described here:

Wildcard
  • 36,499
  • Actually this will report one directory too many, as it reports itself. Unfortunately I don't have time to work on it further at this moment. – Wildcard Nov 18 '16 at 22:51
  • Also, unless I'm misunderstanding, it will fail if there are so many files / directories, or their names are so long, that they cannot be passed on a single command line. But +1 for everything else. – G-Man Says 'Reinstate Monica' Nov 18 '16 at 23:51
  • @G-Man, correct; I mentioned that actually. Feel free to make an edit if you think that wasn't made clear enough. – Wildcard Nov 18 '16 at 23:58
  • @G-Man, I haven't made the necessary huge number of files to test, but I believe all that would be necessary to handle vast numbers of files would be dirs="$(printf %s "$dirs" | tr '\n' + | bc)". Right? – Wildcard Nov 19 '16 at 00:01
  • 1
    Sorry; it's clear enough.  I read it too quickly and skidded over that paragraph. … … … I can't get your answer to work; I believe that you need (printf "%s" "$dirs" | tr '\n' +; echo) | bc.  Or (much as I hate to use unquoted variables), echo $dirs | tr " " + | bc. – G-Man Says 'Reinstate Monica' Nov 19 '16 at 00:24