Я хочу рекурсивно искать каждый *.pdf
файл в каталоге ~/foo
, базовое имя которого совпадает с именем родительского каталога файла.
Например, предположим, что структура каталогов ~/foo
выглядит следующим образом
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Запуск моей желаемой команды вернется
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Возможно ли это с помощью find
какой-либо другой основной утилиты? Я предполагаю, что это выполнимо, используя -regex
опцию, find
но я не уверен, как написать правильный шаблон.
Ответы:
С GNU
find
:-regextype egrep
используйте регулярное выражение в стиле egrep..*/
соответствует директориям прародителя.([^/]+)/
соответствует родительский каталог в группе.\1\.pdf
использоватьbackreference
для сопоставления имени файла в качестве родительского каталога.Обновить
Один (я для одного) может подумать, что
.*
это достаточно жадный, его не нужно исключать/
из родительского соответствия:Выше команда не будет работать хорошо, потому что это выглядит так
./a/b/a/b.pdf
:.*/
Матчи./
(.+)/
Матчиa/b/
\1.pdf
Матчиa/b.pdf
источник
find . -regex '.*/\([^/]*\)/\1\.pdf'
тогда это будет даже работать с BSDfind
.Традиционный вариант цикла
find .. -exec sh -c ''
использования конструкций оболочки для соответствия базовому имени и непосредственному пути выше должен быть сделан ниже.Для разбивки отдельных расширений параметров
file
содержит полный путь к.pdf
файлу, возвращенному изfind
команды"${file##*/}"
содержит только часть после последней,/
т.е. только базовое имя файла"${file%/*}"
содержит путь до финала,/
т.е. за исключением части базового имени результата"${path##*/}"
содержит часть после последней/
изpath
переменной, то есть непосредственный путь к папке над базовым именем файла"${base%.*}"
содержит часть базового имени с.pdf
удаленным расширениемПоэтому, если базовое имя без расширения совпадает с именем непосредственной папки выше, мы печатаем путь.
источник
Обратный ответ Inian , то есть поиск каталогов, а затем посмотреть, содержат ли они файл с определенным именем.
Далее выводятся пути к найденным файлам относительно каталога
foo
:${dirpath##*/}
будет заменен частью имени файла в пути к каталогу и может быть заменен на$(basename "$dirpath")
.Для людей, которым нравится синтаксис короткого замыкания:
Преимущество этого состоит в том, что у вас может быть больше файлов PDF, чем каталогов. Количество задействованных тестов уменьшается, если ограничить запрос меньшим числом (количеством каталогов).
Например, если один каталог содержит 100 файлов PDF, он будет пытаться обнаружить только один из них, а не проверять имена всех 100 файлов по сравнению с именем каталога.
источник
с
zsh
:Остерегайтесь того, что пока
**/
не будете следовать символическим ссылкам,*/
будете.источник
Это не было указано, но вот решение без регулярных выражений, если кто-то заинтересован.
Мы можем использовать,
find . -type f
чтобы просто получить файлы, затем использоватьdirname
иbasename
написать условное. Утилиты имеют следующее поведение:basename
возвращает только имя файла после последнего/
:dirname
дает весь путь до финала/
:Следовательно,
basename $(dirname $file)
дает родительский каталог файла.Решение
Объедините вышеперечисленное, чтобы сформировать условное выражение
"$(basename $file)" = "$(basename $(dirname $file))".pdf
, а затем выводите каждый результат только в томfind
случае, если это условное выражение возвращает значение true.В приведенном выше примере мы добавили каталог / файл с пробелами в имени, чтобы обработать этот случай (спасибо @Kusalananda в комментариях)
источник
Final Thesis.pdf
(с пробелом).Я использую bash globbing, простые циклические тесты строк в любой день в программе Find . Назовите меня иррациональным, и, хотя он может быть неоптимальным, такой простой код делает свое дело для меня: читаемый и многократно используемый, удовлетворяющий даже! Поэтому позвольте мне предложить комбинацию из:
• Баш globstar :
for f in ** ; do ...
** перебирает каждые файлы в текущем каталоге и во всех вложенных папках .. проверить состояние globstar в текущем сеансе:shopt -p globstar
. Чтобы активировать globstar:shopt -s globstar
.• «file» utlity :
if [[ $(file "$f") =~ pdf ]]; then ...
проверить фактический формат файла для pdf - более надежный, чем тестирование только на расширение файла• basename, dirname : сравнить имя файла с именем каталога непосредственно над ним.
basename
возвращает имя файла -dirname
возвращает полный путь к каталогу - объединяет две функции, чтобы вернуть только один каталог, содержащий соответствующий файл. Я помещаю каждый из них в переменную ( _mydir и _myf ), чтобы затем выполнить простой тест, используя = ~ для сопоставления строк.Одна подпрограмма: удалите любую «точку» в имени файла, чтобы избежать совпадения имени файла с текущим каталогом, ярлык которого также «.» - Я использовал прямую подстановку строк для переменной _myf :
${_myf//./}
- не очень элегантно, но это работает. Положительные матчи будут возвращать путь каждого файла - вместе с полным путем к текущей папке, предваряя выход с:$(pwd)/
.Код
источник