Сопоставление расширения имени файла с набором разрешенных расширений в массиве

#bash #shell

#bash #оболочка

Вопрос:

В этой функции обрабатывается каталог файлов. Каталог должен содержать только то, что разрешено, как указано / настроено в массиве EXT. Цель функции — проверить, присутствует ли в каталоге расширение, которое не разрешено, т.Е. Если вы настроили только .mp3 и .png в массиве EXT, при запуске функции, если она обнаруживает .gif файл, она выдает ошибку. Технически мой код работает так, как есть сейчас, но проблема в том, что он всегда дает сбой при циклическом просмотре расширений.

 # 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
}
  

Комментарии:

1. Очевидно, что я опустил другой код, который вызывает функцию, и такой, поскольку он не нужен.

Ответ №1:

Вы можете переписать логику для проверки части расширения, как показано ниже. Идея заключается в том, чтобы сгенерировать строку для альтернативного сопоставления со всеми возможными поддерживаемыми расширениями, т. Е.

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

теперь сгенерируйте строку, содержащую возможные совпадения расширений, и сохраните ее в переменной, используя printf -v синтаксис, который сохраняет форматированную строку в переменной.

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

Теперь с помощью !(opt1|opt2) шаблона синтаксиса bash extglob мы сопоставляем расширение имени файла, как показано ниже. Обратите внимание, что мы удалили . символ из расширения массива. ${filename##*.} Синтаксис удаляет части имени файла, оставляя только часть расширения.

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

Объединение всего вместе

 #!/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 '%sn' "Only ${ext[*]} files are supported - got $filename"
    fi
done
  

Обратите внимание на использование имен переменных в нижнем регистре для пользовательских переменных. Это всегда является хорошей практикой, поскольку имена в верхнем регистре зарезервированы для использования командной оболочкой.

Ответ №2:

Ваша проблема здесь:

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

Вы перезаписываете EXT содержимое массива при каждом запуске. Также вместо зацикливания индексов массива вы могли бы написать этот блок цикла следующим образом:

 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
  

Комментарии:

1. Ах, теперь это имеет смысл! Я уже переключился на решение выше, но также благодарю вас за вашу помощь.

Ответ №3:

ЭТО все, что вам нужно сделать, обращаясь только к тесту, о котором вы просили:

 # 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
}
  

Существуют также другие способы улучшения скрипта, например, переменные в нижнем регистре, объявления локальных переменных, переменные в кавычках, стандартные возвращаемые значения success / fail и shebang. Это, непроверенное, ближе к тому, как вы действительно должны это написать:

 #!/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 'Failedn' >amp;2; exit 1; }
  

узнайте, как переменная error действительно работает в вашем реальном коде.