Bash — Как остановить поиск глубже после того, как файл найден?

#bash

#bash

Вопрос:

Я пытаюсь выполнить это, используя встроенную функциональность bash без поиска и т. Д.

В настоящее время я:

  1. Получение списка всех имен файлов и помещение их в массив.
  2. Анализируем их по регулярному выражению, чтобы найти конкретное имя файла
  3. Затем добавьте эти совпадения в другой массив, который я использую позже в скрипте

Я хотел бы немного изменить это, чтобы прекратить поиск глубже в дереве, если я найду совпадение. т.е. допустим, файл, который я ищу, ‘test.txt ‘

И у меня есть следующие файлы:

 ./stuff/test.txt
./stuff/things/test.txt
./foo/bar/test.txt
 

Я хотел бы только соответствовать. /stuff/test.txt , игнорировать. /stuff/things/test.txt (поскольку у него есть родительское совпадение) и все равно найти. /foo/bar/test.txt поскольку он находится в другом дереве, чем первое совпадение.

Не слишком уверен, какой наилучший подход — использовать собственный bash без внешних приложений? Оценил бы некоторую мудрость в отношении наилучшего подхода.

Вот мой текущий код:

 shopt -s globstar

    for files in **/*; do
        if [[ -f "${files}" ]]; then
            files_array =(./"${files}")
        fi
    done

shopt -u globstar

shopt -s nocasematch

    for such_files in "${files_array[@]}"; do
        if [[ "${such_files}" =~ ${my_custom_regex} ]]; then
            full_projects =("${such_files}")
        fi
    done

shopt -u nocasematch`
 

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

1. Как есть, вы ничего не можете сделать, чтобы избежать перехода в каталог, потому **/* что он расширяется до запуска любой вашей логики. Если вы хотите сделать это, как указано, вам придется вручную написать рекурсивную функцию, которая условно пропускает переход в каталоги, которые вам не нужны.

2. @that-other-guy Спасибо, что взглянули! Да, я знаю, что мне нужно провести рефакторинг, просто не слишком уверен в лучшем подходе? Я подумал, может быть, передать совпадения регулярных выражений через фильтр другого типа, чтобы удалить все дублирующиеся подпапки, а затем сохранить их снова. Просто любопытно, есть ли идиоматический способ сделать что-то подобное.

3. Нет, нет. Даже с find этим нелегко реализовать, если имя файла не известно, например test.txt .

Ответ №1:

Мне удалось сделать это, используя несколько функций и массивов. Это самый эффективный способ? Не слишком уверен, но пока работает.

Предполагая, что скрипт может быть запущен из любого места, а затем будет погружаться в дерево каталогов, пока не найдет совпадение регулярных выражений, мы:

  1. Проверьте все файлы в текущем каталоге, из которых выполняется скрипт. Если мы найдем совпадение, у нас все хорошо, и мы можем выйти.
  2. Если совпадений нет, мы перечисляем каталоги в текущем каталоге. Затем выполните итерацию в них, проверяя совпадения.
  3. Если мы находим совпадение, мы прекращаем погружение. Если мы этого не сделаем, мы продолжаем погружаться в это дерево, пока не останется больше каталогов.

Больше примеров с некоторыми рабочими сценариями здесь: https://github.com/octopusnz/scripts

Вот мой код:

 #!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

magic_reg="my_fancy_regex"
reg_matches=()
tmp_array=()
success=0

main(){

    shopt -s nocasematch

    for files in *; do
        if [[ -f "${files}" ]]; then
            if [[ "${files}" =~ ${magic_reg} ]]; then
                reg_matches =("${PWD}")
                success=$((success 1))
                #This will only find the first one
                break
            fi
        fi
    done

    shopt -u nocasematch

    if [[ "${success}" -gt 0 ]]; then
        cleanup
    fi
}

deeper_dirs(){

  set  o nounset

  if [[ "${#tmp_array[@]}" -gt 0 ]]; then
      for deeper_files in "${tmp_array[@]}"; do
          cd "${deeper_files}"
          cust_get_files
      done
  fi

  set -o nounset

}

cust_get_files() {

    shopt -s nocasematch
    success=0

    for files in *; do
        if [[ -f "${files}" ]]; then
            if [[ "${files}" =~ ${magic_reg} ]]; then
                reg_matches =("${deeper_files}")
                success=$((success 1))
                #This will only find the first one
                break
            fi
        fi
    done

    shopt -u nocasematch

    if [[ "${success}" -eq 0 ]]; then
        unset tmp_array
        for dirs in *; do
            if [[ -d "${dirs}" ]]; then
                tmp_array =("${PWD}"/"${dirs}")
            fi
        done
        deeper_dirs
    fi
}

dir_check() {

    for dirs in *; do
        if [[ -d "${dirs}" ]]; then
            dirs_array =("${PWD}"/"${dirs}")
        fi
    done

    for deeper_files in "${dirs_array[@]}"; do
        cd "${deeper_files}"
        cust_get_files
    done
}

cleanup() {

    if [[ "${#reg_matches[@]}" -lt 1 ]]; then
        printf "We didn't find any matching files. Nothing to do.n"
        exit 0
    fi

    echo "${reg_matches[@]}"
    # We can do more neat stuff with this data here.

    exit 0
}

main
dir_check
cleanup