Вы ошибаетесь, архиваторы оболочки существовали не позднее 1980 года.
Они были написаны для исходных архивов usenet, чтобы позволить архивам быть появляются в письме. Tar является двоичным и не может быть легко помещен в исходный архив usenet.
С GNUfind
:
find. -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
-regextype egrep
используйте регулярное выражение в стиле egrep. .*/
совпадают с главными родительскими директориями. ([^/]+)/
соответствует родительскому каталогу в группе. \1\.pdf
используйте backreference
для сопоставления имени файла с родительским каталогом. обновление
Один (сам за одного )может подумать, что .*
достаточно жадный, нет необходимости исключать /
из сопоставления родителей:
find. -regextype egrep -regex '.*/(.+)/\1\.pdf'
Вышеупомянутая команда не будет работать, потому что она соответствует./a/b/a/b.pdf
:
.*/
соответствует./
(.+)/
соответствуетa/b/
\1.pdf
соответствуетa/b.pdf
Традиционный вариант цикла find.. -exec sh -c ''
для использования конструкций оболочки для сопоставления базового имени и непосредственного пути, указанного выше, будет выполняться ниже.
find foo/ -name '*.pdf' -exec sh -c '
for file; do
base="${file##*/}"
path="${file%/*}"
if [ "${path##*/}" = "${base%.*}" ]; then
printf "%s\n" "$file"
fi
done' sh {} +
Разбивка расширений отдельных параметров
file
содержит полный путь к файлу .pdf
, возвращенному командой find
"${file##*/}"
содержит только часть после последнего /
, т.е. только базовое имя файла "${file%/*}"
содержит путь до конечного /
, т.е. за исключением части базового имени результата "${path##*/}"
содержит часть после последнего /
из переменной path
, т.е. непосредственный путь к папке над базовым именем файла "${base%.*}"
содержит часть базового имени с удаленным расширением .pdf
Таким образом, если базовое имя без расширения совпадает с именем непосредственной папки выше, мы печатаем путь.
сzsh
:
printf '%s\n' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
Имейте в виду, что хотя **/
не будет переходить по символическим ссылкам, */
будет.
Обратное ответу Иниана , т. е. искать каталоги, а затем смотреть, содержат ли они файл с определенным именем.
Далее выводятся пути к найденным файлам относительно каталогаfoo
:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/${dirpath##*/}.pdf"
if [ -f "$pathname" ]; then
printf "%s\n" "$pathname"
fi
done' sh {} +
${dirpath##*/}
будет заменен частью имени файла в пути к каталогу и может быть заменен на $(basename "$dirpath")
.
Для тех, кто любит короткий -синтаксис схемы:
find foo -type d -exec sh -c '
for dirpath do
pathname="$dirpath/${dirpath##*/}.pdf"
[ -f "$pathname" ] && printf "%s\n" "$pathname"
done' sh {} +
Преимущество такого способа заключается в том, что у вас может быть больше файлов PDF, чем каталогов. Количество задействованных тестов уменьшается, если ограничить запрос меньшим числом (числом каталогов ).
Например, если один каталог содержит 100 PDF-файлов, будет предпринята попытка обнаружить только один из них, а не сравнивать имена всех 100 файлов с именами каталога.
Не указано, но вот решение без регулярных выражений, если кому интересно.
Мы можем использовать find. -type f
, чтобы просто получить файлы, а затем использовать dirname
и basename
для записи условного выражения. Утилиты ведут себя следующим образом:
$ find. -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt
basename
возвращает только имя файла после последнего/
:
$ for file in $(find. -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt
dirname
дает весь путь до финала/
:
$ for file in $(find. -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1
Следовательно, basename $(dirname $file)
указывает родительский каталог файла.
$ for file in $(find. -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1
Объедините вышеуказанное, чтобы сформировать условное выражение "$(basename $file)" = "$(basename $(dirname $file))".pdf
, а затем выведите каждый результат из find
, только если это условное выражение возвращает истину.
$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find. -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf
В приведенном выше примере мы добавили каталог/файл с пробелами в имени для обработки этого случая (благодаря @Kusalananda в комментариях)
В любой день я использую bash-глобирование, простой цикл по строковым тестам в программе Find . Назовите меня иррациональным, и хотя он вполне может быть неоптимальным, такой простой код делает свое дело для меня :читабельным и повторно используемым, даже удовлетворяющим!. Поэтому позвольте мне предложить комбинацию:
• Баш Глобстар:for f in ** ; do...
**перебирает все файлы в текущем каталоге и всех подпапках... для проверки статуса globstar в текущем сеансе:shopt -p globstar
. Чтобы активировать глобус:shopt -s globstar
.
• «файловая» утилита:if [[ $(file "$f") =~ pdf ]]; then...
проверить фактический формат файла для pdf-более надежно, чем проверка только для расширения файла
• базовое имя, имя каталога:чтобы сравнить имя файла с именем каталога непосредственно над ним. basename
возвращает имя файла-dirname
возвращает полный путь к каталогу -объединяет две функции, чтобы вернуть только один каталог, содержащий соответствующий файл. Я поместил каждый в переменную(_mydir и_myf ), чтобы затем выполнить простой тест, используя =~для сопоставления строк.
Одна подпрограмма :удалить любую «точку» в имени файла, чтобы избежать сопоставления имени файла с текущим каталогом, ярлык которого также «.» -Я использовал прямую замену строки в переменной_myf:${_myf//./}
-не очень элегантно, но работает. Положительные совпадения вернут путь к каждому файлу -вместе с полным путем к текущей папке, предварив вывод:$(pwd)/
.
Код
for f in ** ; do
if [[ $(file "$f") =~ PDF ]]; then
_mydir="$(basename $(dirname $f))" ;
_myf="$(basename $f)" ;
[[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ;
fi ;
done