Atomiklan
Atomiklan

Reputation: 5444

Match a filename extension against a set of allowed extensions in an array

In this function, a directory of files are processed. The directory should only contain what is allowed as specified/configured in the EXT array. The purpose of the function is to check to see if an extension is present in the directory that is not allowed ie if you have configured only .mp3 and .png in the EXT array, when the function runs, if it detects a .gif file, it throws an error. My code technically works as is right now, but the problem is it always fails when looping through the extensions.

# USER VARIABLES
INPUT=/home/administrator/music/v1.1/input
OUTPUT=/home/administrator/music/v1.1/output
declare -a EXT
EXT[0]=".png"
EXT[1]=".mp3"

function CHECK_EXT()
{
        for FULLPATH in $INPUT/*; do
                FULLNAME=$(basename -- "$FULLPATH")
                i=0
                for i in "${!EXT[@]}"; do
                        EXT="${EXT[$i]}"
                        if [[ $FULLNAME != *"$EXT" ]]; then
                                ERROR="Unsupported file type detected!"
                                return 0
                        fi
                done
        done
        return 1
}

Upvotes: 2

Views: 296

Answers (3)

Ed Morton
Ed Morton

Reputation: 203807

ONLY addressing the test you asked about, this is all you need to do:

# USER VARIABLES
INPUT=/home/administrator/music/v1.1/input
OUTPUT=/home/administrator/music/v1.1/output
declare -A EXT
EXT[".png"]=1
EXT[".mp3"]=1

function CHECK_EXT()
{
        for FULLPATH in $INPUT/*; do
            if (( "${EXT[${FULLPATH##*.}]}" != 1 )); then
                ERROR="Unsupported file type detected!"
                return 0
            fi
        done
        return 1
}

There are also other ways the script could be improved, e.g. lower case variables, local variable declarations, quoted variables, standard success/fail return values, and a shebang. This, untested, is closer to how you should really write it:

#!/usr/bin/env bash
# USER VARIABLES
input_dir='/home/administrator/music/v1.1/input'
output_dir='/home/administrator/music/v1.1/output'
declare -A exts
exts[".png"]=1
exts[".mp3"]=1

function check_exts() {
    local input_dir="$1" full_path ext
    for full_path in "$input_dir"/*; do
        ext="${full_path##*.}"
        if (( "${exts[$ext]}" != 1 )); then
            error='Unsupported file extension "'"$ext"'" detected!'
            return 1
        fi
    done
    return 0
}

check_exts "$input_dir" || { printf 'Failed\n' >&2; exit 1; }

idk how the variable error really works into your real code.

Upvotes: 0

Inian
Inian

Reputation: 85710

You can re-write the logic to check the extension part as below. The idea is generate a string for alternate match with all the possible extensions supported i.e.

declare -A ext
ext[0]="png"
ext[1]="mp3"

now generate the string containing the possible extension matches and store it in a variable using the printf -v syntax which stores the formatted string in a variable.

printf -v globStr '!(%s)' "$(IFS="|"; printf '%s' "${ext[*]}")"

Now with thr !(opt1|opt2) pattern of the bash extglob syntax we match the filename extension as below. Notice that we removed the . character from the array extension. The ${filename##*.} syntax removes parts of filename leaving only the extension part.

if [[ ${filename##*.} == $globStr ]]; then
    printf '%s\n' "Only ${ext[*]} files are supported"
fi

Putting everything together

#!/usr/bin/env bash
#              ^^^^ needed for [[ operator to work, not POSIX compliant

input=/home/administrator/music/v1.1/input
output=/home/administrator/music/v1.1/output

declare -A ext
ext[0]="png"
ext[1]="mp3"

# The array expansion of type "${arr[*]}" generates a single string
# with the array contents based on the IFS value set. For alternate
# match we define a custom IFS value inside "$(..)" as the value 
# defined inside it is not reflected outside.

printf -v globStr '!(%s)' "$(IFS="|"; printf '%s' "${ext[*]}")"

for file in "$input"/*; do
    [ -f "$file" ] || continue
    filename=$(basename -- "$file")

    # The '==' inside the '[[..]]' enables the extglob option
    # using which we match the set of not allowed filename extensions

    if [[ ${filename##*.} == $globStr ]]; then
        printf '%s\n' "Only ${ext[*]} files are supported - got $filename"
    fi
done

Notice the use of lower-case variable names for user-defined variables. It is always a good practice to do this as the uppercase names are reserved to be used by the shell.

Upvotes: 2

user6576367
user6576367

Reputation:

Your problem is here:

EXT="${EXT[$i]}"

You overwrite your EXT array content in each run. Also instead of looping over array indices, You could write that loop block like this:

for FULLPATH in $INPUT/*; do
  # check whether the file extension is NOT in the allowed EXT array
  if [[ ! " ${EXT[@]} " =~ "$(basename -- "$FULLPATH")" ]]; then
    ERROR="Unsupported file type detected!"
    return 1
  fi
done

Upvotes: 0

Related Questions