Home > OS >  Bash Loop with counter gives a count of 1 when no item found. Why?
Bash Loop with counter gives a count of 1 when no item found. Why?

Time:06-16

In the function below my counter works fine as long as an item is found in $DT_FILES. If the find is empty for that folder the counter gives me a count of 1 instead of 0. I am not sure what I am missing.

What the script does here is 1) makes a variable containing all the parent folders. 2) Loop through each folder, cd inside each one and makes a list of all files that contain the string "-DT-". 3) If it finds a file that doesn't not end with ".tif", it then copy the DT files and put a .tif extension to it. Very simple.

I count the number of times the loop did create a new file with the ".tif" extension.

So I am not sure why I am getting a count of 1 at times.

function create_tifs()
{
    IFS=$'\n'

    # create list of main folders
    LIST=$( find . -maxdepth 1 -mindepth 1 -type d )

    for f in $LIST
    do
        echo -e "\n${OG}>>> Folder processed: ${f} ${NONE}"
        cd ${f}

            DT_FILES=$(find . -type f -name '*-DT-*' | grep -v '.jpg')
            if (( ${#DT_FILES} ))
            then
                count=0
                for b in ${DT_FILES}
                do
                    if  [[ "${b}" != *".tif" ]]
                    then
#                        cp -n "${b}" "${b}.tif"
                        echo -e "TIF created ${b} as ${b}.tif"
                        echo
                       ((count  ))
                    else
                        echo -e "TIF already done ${b}"

                    fi
                done
            fi

            echo -e "\nCount = ${count}"
}

CodePudding user response:

I can't repro your problem, but your code contains several dubious constructs. Here is a refactoring might coincidentally also remove whatever problem you were experiencing.

#!/bin/bash

# Don't use non-portable function definition syntax
create_tifs() {
    # Don't pollute global namespace; don't attempt to parse find output
    # See also https://mywiki.wooledge.org/BashFAQ/020
    local f
    for f in ./*/; do
        # prefer printf over echo -e
        # print diagnostic messages to standard error >&2
        # XXX What are these undeclared global variables?
        printf "\n%s>>> Folder processed: %s %s" "$OG" "$f" "$NONE" >&2

        # Again, avoid parsing find output
        find "$f" -name '*-DT-*' -not -name '*.jpg' -exec sh -c '
            for b; do
                if  [[ "${b}" != *".tif" ]]
                then
#                   cp -n "${b}" "${b}.tif"
                    printf "TIF created %s as %s.tif\n" "$b" "$b" >&2
                    # print one line for wc
                    printf ".\n"
                else
                    # XXX No newline, really??
                    printf "TIF already done %s" "$b" >&2
                fi
            done
        fi' _ {}  
    # Missing done!
    done |
    # Count lines produced by printf inside tif creation
    wc -l |
    sed 's/.*/Count = &/'
}

This could be further simplified by using find ./*/ instead of looping over f but then you don't (easily) get to emit a diagnostic message for each folder separately. Similarly, you could add -not -name '*.tif' but then you don't get to print "tif already done" for those.

Tangentially perhaps see also Correct Bash and shell script variable capitalization; use lower case for your private variables.

Printing a newline before your actual message (like in the first printf) is a weird antipattern, especially when you don't do that consequently. The usual arrangement would be to put a newline at the end of each emitted message.

CodePudding user response:

If you've got Bash 4.0 or later you can use globstar instead of (the error-prone) find. Try this Shellcheck-clean code:

#! /bin/bash -p

shopt -s dotglob extglob nullglob globstar

function create_tifs
{
    local dir dtfile
    local -i count
    for dir in */; do
        printf '\nFolder processed: %s\n' "$dir" >&2

        count=0
        for dtfile in "$dir"**/*-DT-!(*.jpg); do
            if [[ $dtfile == *.tif ]]; then
                printf 'TIF already done %s\n' "$dtfile" >&2
            else
                cp -v -n -- "$dtfile" "$dtfile".tif
                count =1
            fi
        done
        printf 'Count = %d\n' "$count" >&2
    done

    return 0
}
  • shopt -s ... enables some Bash settings that are required by the code:
    • dotglob enables globs to match files and directories that begin with .. find shows such files by default.
    • extglob enables "extended globbing" (including patterns like !(*.jpg)). See the extglob section in glob - Greg's Wiki.
    • nullglob makes globs expand to nothing when nothing matches (otherwise they expand to the glob pattern itself, which is almost never useful in programs).
    • globstar enables the use of ** to match paths recursively through directory trees.
  • Note that globstar is potentially dangerous in versions of Bash prior to 4.3 because it follows symlinks, possibly leading to processing the same file or directory multiple times, or getting stuck in a cycle.
  • The -v option with cp causes it to print details of what it does. You might prefer to drop the option and print a different format of message instead.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used printf instead of echo.
  • I didn't use cd because it often leads to problems in programs.
  • Related