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!

5
  • just tried it, returns "$i is not a directory" Commented May 26, 2016 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. Commented May 26, 2016 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. Commented May 26, 2016 at 19:53
  • 1
    As mentioned, you can use indirection (${!i}) also, but in your case, just looking at the arguments themselves ($@) is simpler. Commented May 26, 2016 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. Commented May 27, 2016 at 9:04

2 Answers 2

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
0
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.

1
  • +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. Commented May 27, 2016 at 16:02

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.