Вопрос или проблема
Вот пример содержимого директории 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
получает h
ead пути (имя директории), как в 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
, обеспечит возможность быстро и точно извлекать пути к файлам, даже если файл находится глубоко в структуре каталогов. Знание базовых команд позволяет строить сложные цепочки операций для автоматизации задач поиска файлов в реальных производственных условиях.