Как я могу получить абсолютный путь к файлу, находящемуся глубоко в каталоге?

Вопрос или проблема

Вот пример содержимого директории level0 в формате дерева. Однако предположим, что она огромна и содержит много файлов, которых я не показываю здесь:

$ tree level0
level0
└── level1
    └── level2
        └── sample.txt

Я попытался использовать grep для проверки некоторых файлов через grep, но вот что я получил в выводе:

$ tree /tmp/level0  | grep sample
        └── sample.txt

Однако я ожидал, что вывод будет содержать абсолютный путь /tmp/level0/level1/level2/sample.txt.

Как я могу получить абсолютный путь файла глубоко внутри директории с помощью команды?

Что бы я сделал:

find /tmp/level0 -type f -name 'sample.txt'

Если вы хотите получить реальный путь для потенциальных символических ссылок:

find /tmp/level0 -type f -name 'sample.txt' -exec realpath {} +

shopt -s globstar
echo /tmp/level0/**/sample.txt

Установка опции globstar в Bash позволяет использовать оператор **, который позволяет сопоставлять несколько компонентов директории в путях.

Это позволяет создать массив из совпавших путей:

samples=( /tmp/level0/**/sample.txt )

echo 'Это файл(ы) sample.txt:'
printf '\t%s\n' "${samples[@]}"

Также установите опцию оболочки dotglob, чтобы учитывать скрытые имена и имена в поддиректориях со скрытыми именами, а также либо nullglob, либо failglob в зависимости от того, как вы хотите обрабатывать несовпадающие шаблоны. Установка nullglob заставит несовпадающий шаблон расширяться до пустоты, а установка failglob вызовет ошибку “нет совпадений” (текущая подпроцесс будет завершена, если это произойдет). Использование обоих опций даст failglob приоритет, и использование ни одной из них оставит нерасширенный шаблон как есть.

Вы можете использовать "sample.txt" или "*sample*"

Чтобы получить только путь :

find /tmp/level0 -type f -name "sample.txt" -exec dirname {} \;

С tree:

tree -fi /tmp/level0 | grep "sample.txt" | xargs dirname

Путь и имя файла:

find /tmp/level0 -type f -name "sample.txt"

С tree:

tree -fi /tmp/level0 | grep "sample.txt"

Для отсортированного списка нескрытых директорий, содержащих файл под названием sample.txt, в zsh вы можете сделать:

dirs=( **/sample.txt(N:h) )

Где N квалификатор glob включает nullglob, чтобы не было ошибок при отсутствии совпадений, а модификатор :h получает head пути (имя директории), как в csh или vim.

Чтобы учитывать скрытые директории, добавьте квалификатор D (включает dotglob для этого одного расширения glob).

Чтобы остановиться на первом совпадении, вы можете добавить квалификатор Y1 или использовать [1]/[-1] для первого/последнего в отсортированном списке (или использовать $dirs[1] для первого или $dirs[-1] для последнего).

any_dir=( **/sample.txt(NY1:h) )
first_dir_in_sorted_list=( **/sample.txt(NY[1]:h) )
last_dir_in_sorted_list=( **/sample.txt(NY[-1]:h) )

Эквивалент с bash (GNU оболочка) и GNU find и GNU sort:

readarray -td '' dirs < <(
  LC_ALL=C find . -name '.?*' -prune -o \
    -name sample.txt -printf '%h\0' | sort -z
)

Чтобы также учитывать скрытые директории, просто удалите -name '.?*' -prune -o и затем вы также можете удалить LC_ALL=C, которое больше не нужно.

Или с bash 5.0+:

shopt -s globstar nullglob
shopt -u failglob
dirs=( ./**/sample.txt )
dirs=( "${dirs%/*}" )
dirs=( "${dirs#./}" )

Добавьте shopt -s dotglob, чтобы также рассматривать скрытые директории.

Имейте в виду, что в bash вам нужно "${dirs[@]}" для всех элементов массива, а не просто $dirs, как в zsh ($dirs в bash, как и в ksh, это ${dirs[0]} и если не использовать кавычки, то будет выполняться разделение и расширение).


Теперь учтите, что grep sample.txt возвращает вам строки (а имена файлов могут состоять из любого числа строк, так как новая строка – такой же допустимый символ, как любой другой в имени файла), которые соответствуют регулярному выражению sample.txt. В синтаксисе регулярных выражений . – оператор, который соответствует любому символу.

Таким образом, grep sample.txt возвращает строки, которые содержат sample, за которым следует любой одиночный символ, затем txt, такие как sample.txt или foosample-txts/bar.

.

Ответ или решение

Получение абсолютного пути к файлу, глубоко расположенного в директории, является распространенной задачей в ИТ-сфере, особенно когда мы имеем дело с большими и сложными деревьями каталогов. В этой ситуации очень важно знать, какие средства и подходы можно использовать, чтобы достичь своей цели эффективно и быстро.

Теория

Когда мы говорим об абсолютном пути, мы подразумеваем полный путь к файлу, начиная от корня файловой системы. В UNIX-подобных системах это можно сделать несколькими способами. Основной подход — использовать команды, которые позволяют рекурсивно переходить по каталогам и находить нужные файлы.

Ключевыми утилитами, которые мы можем здесь применить, являются команды find, realpath, и возможности шелла, такие как расширенное шаблонное соответствие (globstar) в Bash. Самое важное — выбрать инструмент, который соответствует особенностям вашей задачи, например, если у вас может быть дело с символическими ссылками или скрытыми файлами.

Пример

Имея дерево директорий, как в вашем примере, мы можем использовать несколько подходов:

Использование команды find:

Команда find является одной из самых мощных и широкоиспользуемых для поиска файлов в UNIX-системах. Вот основной пример использования:

find /tmp/level0 -type f -name 'sample.txt'

Эта команда ищет все файлы с именем "sample.txt" в каталоге /tmp/level0 и его подкаталогах. Ключ -type f указывает на поиск именно файлов (в отличие от директорий или других типов объектов).

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

find /tmp/level0 -type f -name 'sample.txt' -exec realpath {} +

Использование Bash с globstar:

Bash предоставляет возможность использования шаблонов для поиска файлов с включением расширенной возможности **, которая позволяет обращаться к подкаталогам. Для этого необходимо включить соответствующий параметр:

shopt -s globstar
echo /tmp/level0/**/sample.txt

После выполнения команды выше все файлы с именем "sample.txt" в иерархии каталогов начиная с /tmp/level0 будут выведены вместе с их путями.

Использование tree:

Хотя tree основной только для визуализации структуры каталогов, она также может быть использована в сочетании с grep для фильтрации файлов по имени:

tree -fi /tmp/level0 | grep "sample.txt"

Этот подход менее точен по сравнению с find, так как он просто ищет вахождение строки "sample.txt" в выведенном списке файлов, но может быть полезен для быстрой проверки среди небольшого числа файлов.

Применение

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

Также при работе с файлами, узнайте о скрытых файлах и директорях (dotfiles) и учтите их существование. Для этой цели можно настроить параметры dotglob в Bash или соответствующие флаги в find.

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

Заключение

Владение командами, такими как find и realpath, обеспечит возможность быстро и точно извлекать пути к файлам, даже если файл находится глубоко в структуре каталогов. Знание базовых команд позволяет строить сложные цепочки операций для автоматизации задач поиска файлов в реальных производственных условиях.

Оцените материал
Добавить комментарий

Капча загружается...