0

i have the following script:

    #!/bin/bash
    for ((i=1; i<=$#; i++));
    do
       if [ ! -d $i ]
       then
          echo $i its not a directory >> file.txt
       else
          DIRECTORY=$(ls -l $i | grep ^d | wc -l)
          LINK=$(find $i -type l | wc -l)
          FILE=$(ls -A $i | wc -l)

          echo `date "+%H:%M:%S %d-%m-%y"` directory $i file count: $FILE link count: $LINK subdirectory count: $DIRECTORY >> file.txt

       fi
    done

this script count the subdirectories, links and files from a directory entered by parameter(it can be more than 1).

i am struggling with the loop, it returns "echo $i its not a directory" reading $i as 1 instead of $1, i do understand why it does that but i am starting with scripts and don't know how to fix this. i think a "while" can be a substitute for that "for" but don't know how to use it properly.

thanks for the help!

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Kuma
  • 3
  • just tried it, returns "$i is not a directory" – Kuma May 26 '16 at 19:24
  • You're never actually looking at the arguments themselves, just numbers from 1 to n (where n is the number of arguments), and testing those numbers and not the filenames you think you're testing. – DopeGhoti May 26 '16 at 19:37
  • i saw that, but i didn't have a clue on how to solve it, ill have to work harder on scripts, thanks. – Kuma May 26 '16 at 19:53
  • 1
    As mentioned, you can use indirection (${!i}) also, but in your case, just looking at the arguments themselves ($@) is simpler. – DopeGhoti May 26 '16 at 19:55
  • Don't parse ls. Use find instead. e.g. DIRECTORY=$(find "$i" -maxdepth 1 -type d ! -name '.*' -print0 | sed -z -n -e '$=' (requires a recent GNU sed for the -z option). and use something similar for symlinks (-type l) and regular files (-type f). The ! -name '.*' excludes hidden dot-files/dirs from being counted. – cas May 27 '16 at 09:04

2 Answers2

1

You are looping from 1 to [number of arguments], and then testing if each of those numbers is in fact a directory. It might be simpler to do something like:

#!/bin/bash
for item in "$@"; do  # iterate over the arguments themselves
    if [[ ! -d "$item" ]]; then
        echo "$item is not a directory" >> file.txt
    else
        DIRECTORY=$(ls -l $item | grep ^d | wc -l)
        LINK=$(find $item -type l | wc -l)
        FILE=$(ls -A $item | wc -l)

        echo `date "+%H:%M:%S %d-%m-%y"` directory $item file count: $FILE link count: $LINK subdirectory count: $DIRECTORY >> file.txt
    fi
done
DopeGhoti
  • 76,081
1

What you are trying to do is called variable indirection—you want to refer to the variable whose name is stored inside another variable. This is possible in bash—just refer to "${!i}" instead of $i.

Also note the importance of double quotes around your variables.

That said...you can simplify your script drastically. For instance, here is a one-liner that does nearly everything you are trying to do with your script.

Wildcard
  • 36,499
  • +1. that one-liner in the else clause of @dopeghoti's bash script (and run date ... before the one-liner) would be good. and move the >> file.txt to the end of the done line. – cas May 27 '16 at 16:02