Найти все повторяющиеся подкаталоги в каталоге

#bash #shell #unix #recursion

#bash #оболочка #unix #рекурсия

Вопрос:

Мне нужно создать сценарий оболочки, который «перечисляет все идентичные подкаталоги (рекурсивно) в текущем рабочем каталоге».

Я новичок в сценариях оболочки. Как мне подойти к этому?

Для меня это означает:

  • для каждого каталога, начинающегося с некоторого начального каталога, сравните его с любым другим каталогом, который он разделяет по имени.
    • если другой каталог имеет то же имя, проверьте размер.
      • если размер также одинаков, рекурсивно сравните содержимое каждого элемента каталога по элементам, возможно, по md5sum (?) и продолжайте делать это для каждого подкаталога в каталогах (рекурсивно?)
  • затем продолжайте, рекурсивно вызывая это для каждого обнаруженного подкаталога
  • затем повторите для каждого другого каталога в структуре каталогов

Это была бы самая сложная программа, которую я когда-либо писал, поэтому я предполагаю, что я просто не знаю какой-либо команды оболочки, чтобы сделать большую часть этого за меня?

То есть, как я должен был подойти к этому? Все остальные части были посвящены поиску в Google, пока я не обнаружил команду оболочки, которая сделала это на 90% за меня.

(Для предыдущего задания, которое я не смог выполнить, в этой части был ноль, нужно знать, как подойти к нему в будущем.)

Комментарии:

1. Пример ввода и вывода?

2. не указано, я предполагаю, что ввод — это текущий рабочий каталог, а вывод — список каталогов, которые считаются идентичными. Извините, не могу быть более конкретным

3. зависит от того, насколько конкретно вы имеете в виду дубликат. имеет ли dir1/file{1,2,3} значение vs dir2/file{1.2,3} where file{1,2,3} то же значение, что и дубликат? Обратите внимание, что dir1 / dir2 разные. А как насчет, если dir2/file{1,2,3} на 3 уровня ниже, считается ли это совпадением с теми же файлами в оригинале dir1 ? Здесь много возможностей, и возможность обучения может заключаться в том, «как вы определяете правильную спецификацию того, что должно считаться обманом (и почему)?». Удачи.

Ответ №1:

Я был бы удивлен, узнав, что существует специальный инструмент Unix или специальное использование стандартного инструмента Unix для выполнения именно того, что вы описываете. Возможно, ваше понимание задачи сложнее, чем предполагал создатель задачи. Возможно, под «идентичным» подразумевалось что-то, касающееся ссылок. Обычно жесткие ссылки на каталоги запрещены, так что, вероятно, это тоже не подразумевается.

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

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

После создания контрольных сумм для всех элементов выполните поиск дубликатов (путем сортировки списка всех и поиска последовательных строк).

Быстрое решение может быть таким:

 #!/bin/bash

dirchecksum() {
  if [ -f "$1" ]
  then
    checksum=$(md5sum < "$1")
  elif [ -d "$1" ]
  then
    checksum=$(
      find "$1" -maxdepth 1 -printf "%P " ( ! -path "$1" ) 
                -exec bash -c "dirchecksum {}" ; |
        md5sum
    )
  fi
  echo "$checksum"
  echo "$checksum $1" 1>amp;3
}
export -f dirchecksum

list=$(dirchecksum "$1" 3>amp;1 1>/dev/null)

lastChecksum=''
while read checksum _ path
do
  if [ "$checksum" = "$lastChecksum" ]
  then
    echo "duplicate found: $path = $lastPath"
  fi
  lastChecksum=$checksum
  lastPath=$path
done < <(sort <<< "$list")
  

Этот скрипт использует два трюка, которые могут быть непонятны, поэтому я упоминаю о них:

  • Чтобы передать функцию оболочки find -exec , можно export -f использовать ее (сделано ниже), а затем вызвать bash -c ... для ее выполнения.
  • Функция оболочки имеет два выходных потока, один для возврата контрольной суммы результата (это через стандартный вывод, т. Е. fd 1), и один для выдачи каждой контрольной суммы, найденной на пути к этому (это через fd 3).

При сортировке в конце в качестве входных данных используется список, выданный через fd 3.

Ответ №2:

Может быть, что-то вроде этого:

 $ find -type d -exec sh -c "echo -n {}  ; sh -c "ls -s {}; basename {}"|md5sum " ; | awk '$2 in a {print "Match:"; print a[$2], $1; next} a[$2]=$1{next}'
Match:
./bar/foo ./foo
  

find все каталоги: find -type d , вывод:

 .
./bar
./bar/foo
./foo
  

ls -s {}; basename {} выведет упрощенный список каталогов и базовое имя указанного каталога, например, для directory foo : ls -s foo; basename foo

 total 0
0 test
foo
  

Они будут охватывать файлы в каждом каталоге, их размеры и имя каталога. Этот вывод будет отправлен в md5sum каталог и по каталогу:

 . 674e2573b49826d4e32dfe81d9680369  -
./bar 4c2d588c5fa9781ad63ad8e86e575e01  -
./bar/foo ff8d1569685be86366f18ea89851db35  -
./foo ff8d1569685be86366f18ea89851db35  -
  

будет отправлено в awk :

 $2 in a {            # hash as array key
    print "Match:"   # separate hits in output
    print a[$2], $1  # print matching dirscompared to
    next             # next record
} 
a[$2]=$1 {next}      # only first match is stored and 
  

Протестируйте структуру каталога:

 $ mkdir -p test/foo; mkdir -p test/bar/foo; touch test/foo/test; touch test/bar/foo/test
$ find test/
test/
test/bar
test/bar/foo
test/bar/foo/test  # touch test
test/foo
test/foo/test      # touch test