0
#!/bin/bash for i in manu; do if [ -f $i ] && [ -s $i ]; then echo " $(ls $i | wc -l) non-empty files" elif [ -f $i ] && [ ! -s $i ]; then echo " $(ls $i | wc -l) empty files" fi done 

Output of this script is:

1 non-empty files 1 empty files 1 non-empty files 1 empty files 1 empty files 1 empty files 

... and the output should be:

2 non-empty files 4 empty files 

I don't know where I'm going wrong. Can anybody help me?

2
  • for i in manu executes exactly one iteration, setting i equal to "manu".
    – Kaz
    CommentedSep 1, 2021 at 20:37
  • ls $i | wc -l counts the number of lines in the listing of the file's name. It will produce 0 if the file doesn't exist (because ls then prints an error message to the error stream, and nothing on the output stream) or 1 if the file exists, regardless of that file's size.
    – Kaz
    CommentedSep 1, 2021 at 20:39

2 Answers 2

3

I'm not quite sure why you use ls. The ls utility is usually used for listing the files in a directory. Here you want to loop over files, which is easiest done with a file globbing pattern and a for loop.

You also output something that looks like it should be a summary, but you do that in each iteration rather than at the end.

I'm assuming that manu is a directory in the current directory and that you want to count the number of empty and non-empty files in that directory. I furthermore assume that you don't want to count any other filetype than regular files.

#!/bin/bash shopt -s nullglob dotglob nonempty=0 empty=0 for name in manu/*; do if [ -f "$name" ] && [ ! -h "$name" ]; then # $name is a regular file if [ -s "$name" ]; then nonempty=$(( nonempty + 1 )) else empty=$(( empty + 1 )) fi fi done printf 'There were %d empty files and %d non-empty files\n' "$empty" "$nonempty" 

The nullglob shell option stops the loop from running even once if the pattern manu/* does not match anything, and the dotglob shell option allows the pattern to match hidden names too.

In each iteration, we then determine whether the current file is a regular file (the -h test tests for a symbolic link) and then whether it is empty or not. We update one of two counters depending on the outcome of the -s test. The result is shown at the end.

    2

    Loops are generally the wrong way to do things in shells.

    Here, you could pipe a directory listing command (ls) to a counting command (awk):

    LC_ALL=C ls -Aqn manu/ | LC_ALL=C awk ' /^-/ {if ($5 == 0) empty++; else non_empty++} END {printf "%d empty files\n%d non-empty files\n", empty, non_empty}' 

    Where LC_ALL=C disables localisation, so we're sure to get a standard and consistent ls output, and any sequence of bytes (as filenames are) form valid text.

    ls -n lists the entries of the manu directory in long format which includes the type and size of the files, -q guarantees one line per file even if the file names (or symlink targets) contain newline characters and -A includes all entries, even hidden ones (not . nor .. which are of no use here).

    Then awk can just check the file type (first character being - means it's a regular file) and 5th field is the size.

    Add a -L option to ls for the type and size of file to be considered after symlink resolution. With GNU ls, you can add -U to disable sorting which we don't care for here.

    Or you can use zsh and its advanced globs:

    empty=(manu/*(NDoN.L0)) non_empty=(manu/*(NDoN.L+0)) print "$#empty empty files\n$#non_empty non-empty files" 

    Where the . glob qualifier selects regular files, L0 those of length 0 and L+0 those of length greater than 0 (and N for nullglob (don't complain if there's no match), D for dotglob (includes hidden files), oN do Not order the result).

      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.