Найти папки, содержащие несколько совпадений с регулярным выражением / grep - PullRequest
0 голосов
/ 01 декабря 2018

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

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

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

Если возможно, я бы хотел сопоставить «FILE.JPG» и «file.jpg» как совпадающие с запросами «file» или «jpg».

То, что я делал, просто find . -iname "*file*" и проходя его вручную.

папки содержат папки, иногда глубиной 3 или 4 уровня

first/
  second/
     README.txt
     readme.TXT
     readme.txt
     foo.txt
   third/
     info.txt
   third/fourth/
     raksljdfa.txt

Должен возвращаться

first/second/README.txt
first/second/readme.TXT
first/second/readme.txt
first/secondfoo.txt```

при поиске "txt"

и

first/second/README.txt
first/second/readme.TXT
first/second/readme.txt

при поиске "readme"

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Этот чистый код Bash должен это делать (с оговорками, см. Ниже):

#! /bin/bash

fileglob=$1             # E.g. '*.txt' or '*readme*'

shopt -s nullglob       # Expand to nothing if nothing matches
shopt -s dotglob        # Match files whose names start with '.'
shopt -s globstar       # '**' matches multiple directory levels
shopt -s nocaseglob     # Ignore case when matching

IFS=                    # Disable word splitting

for dir in **/ ; do
    matching_files=( "$dir"$fileglob )
    (( ${#matching_files[*]} > 1 )) && printf '%s\n' "${matching_files[@]}"
done

Предоставить шаблон для сопоставления в качестве аргумента программы при его запуске.Например,

myprog '*.txt'
myprog '*readme*'

(кавычки на шаблонах необходимы для того, чтобы они не совпадали с файлами в текущем каталоге.)

Предостережения относительно кода:

  1. globstar был представлен в Bash 4.0.Код не будет работать со старым Bash.
  2. До Bash 4.3, globstar соответствует следующим символическим ссылкам.Это может привести к дублированию выходных данных или даже к сбоям из-за циклических ссылок.
  3. Шаблон **/ расширяется до списка всех каталогов в иерархии.Это может занять слишком много времени или использовать слишком много памяти, если число каталогов велико (скажем, больше десяти тысяч).

Если ваш Bash старше 4.3 или у васбольшое количество каталогов, этот код является лучшим вариантом:

#! /bin/bash

fileglob=$1             # E.g. '*.txt' or '*readme*'

shopt -s nullglob       # Expand to nothing if nothing matches
shopt -s dotglob        # Match files whose names start with '.'
shopt -s nocaseglob     # Ignore case when matching

IFS=                    # Disable word splitting

find . -type d -print0 \
    |   while read -r -d '' dir ; do
            matching_files=( "$dir"/$fileglob )
            (( ${#matching_files[*]} > 1 )) \
                && printf '%s\n' "${matching_files[@]}"
        done
0 голосов
/ 02 декабря 2018

Что-то вроде этого звучит так, как вы хотите:

find . -type f -print0 |
awk -v re='[.]txt$' '
BEGIN {
    RS = "\0"
    IGNORECASE = 1
}
{
    dir  = gensub("/[^/]+$","",1,$0)
    file = gensub("^.*/","",1,$0)
}
file ~ re {
    dir2files[dir][file]
}
END {
    for (dir in dir2files) {
        if ( length(dir2files[dir]) > 1 ) {
            for (file in dir2files[dir]) {
                print dir "/" file
            }
        }
    }
}'

Это не проверено, но должно быть близко.Он использует GNU awk для gensub (), IGNORECASE, истинных многомерных массивов и длины (массива).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...