#bash #shell #unix #recursion
#bash #оболочка #unix #рекурсия
Вопрос:
Мне нужно создать сценарий оболочки, который «перечисляет все идентичные подкаталоги (рекурсивно) в текущем рабочем каталоге».
Я новичок в сценариях оболочки. Как мне подойти к этому?
Для меня это означает:
- для каждого каталога, начинающегося с некоторого начального каталога, сравните его с любым другим каталогом, который он разделяет по имени.
- если другой каталог имеет то же имя, проверьте размер.
- если размер также одинаков, рекурсивно сравните содержимое каждого элемента каталога по элементам, возможно, по
md5sum
(?) и продолжайте делать это для каждого подкаталога в каталогах (рекурсивно?)
- если размер также одинаков, рекурсивно сравните содержимое каждого элемента каталога по элементам, возможно, по
- если другой каталог имеет то же имя, проверьте размер.
- затем продолжайте, рекурсивно вызывая это для каждого обнаруженного подкаталога
- затем повторите для каждого другого каталога в структуре каталогов
Это была бы самая сложная программа, которую я когда-либо писал, поэтому я предполагаю, что я просто не знаю какой-либо команды оболочки, чтобы сделать большую часть этого за меня?
То есть, как я должен был подойти к этому? Все остальные части были посвящены поиску в Google, пока я не обнаружил команду оболочки, которая сделала это на 90% за меня.
(Для предыдущего задания, которое я не смог выполнить, в этой части был ноль, нужно знать, как подойти к нему в будущем.)
Комментарии:
1. Пример ввода и вывода?
2. не указано, я предполагаю, что ввод — это текущий рабочий каталог, а вывод — список каталогов, которые считаются идентичными. Извините, не могу быть более конкретным
3. зависит от того, насколько конкретно вы имеете в виду дубликат. имеет ли
dir1/file{1,2,3}
значение vsdir2/file{1.2,3}
wherefile{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