#bash #unix
#bash #unix
Вопрос:
У меня есть двойной цикл, который открывает файлы и использует awk для получения первого раздела и второго раздела каждой строки. Первый раздел — это md5sum файла, а второй фрагмент — это имя файла. Однако, когда я запускаю скрипт, чтобы проверить, есть ли у меня дубликаты файлов, file1 штрафует file1 и поэтому считает их дубликатами, даже если это один и тот же файл. Вот мой код:
echo start
for i in $(<dump.txt) ; do
md=$(echo $i|awk -F'|' '{print $1}')
file=$(echo $i|awk -F'|' '{print $2}')
for j in $(<dump.txt) ; do
m=$(echo $j|awk -F'|' '{print $1}')
f=$(echo $j|awk -F'|' '{print $2}')
if [ "$md" == "$m" ]; then
echo $file and $f are duplicates
fi
done
done
echo end
Файл дампа выглядит следующим образом:
404460c24654e3d64024851dd0562ff1 *./extest.sh
7a900fdfa67739adcb1b764e240be05f *./test.txt
7a900fdfa67739adcb1b764e240be05f *./test2.txt
88f5a6b83182ce5c34c4cf3b17f21af2 *./dump.txt
c8709e009da4cce3ee2675f2a1ae9d4f *./test3.txt
d41d8cd98f00b204e9800998ecf8427e *./checksums.txt
Весь код:
#!/bin/sh
func ()
{
if [ "$1" == "" ]; then
echo "Default";
for i in `find` ;
do
#if [ -d $i ]; then
#echo $i "is a directory";
#fi
if [ -f $i ]; then
if [ "$i" != "./ex.sh" ]; then
#echo $i "is a file";
md5sum $i >> checksums.txt;
sort --output=dump.txt checksums.txt;
fi
fi
done
fi
if [ "$1" == "--long" ]; then
echo "--long";
for i in `find` ;
do
#if [ -d $i ]; then
#echo $i "is a directory";
#fi
if [ -f $i ]; then
echo $i "is a file";
fi
done
fi
if [ "$1" == "--rm" ]; then
echo "--rm";
for i in `find` ;
do
#if [ -d $i ]; then
#echo $i "is a directory";
#fi
if [ -f $i ]; then
echo $i "is a file";
fi
done
fi
}
parse () {
echo start
for i in $(<dump.txt) ; do
md=$(echo $i|awk -F'|' '{print $1}')
file=$(echo $i|awk -F'|' '{print $2}')
for j in $(<dump.txt) ; do
m=$(echo $j|awk -F'|' '{print $1}')
f=$(echo $j|awk -F'|' '{print $2}')
#echo $md
#echo $m
if [ "$file" != "$f" ] amp;amp; [ "$md" == "$m" ]; then
echo Files $file and $f are duplicates.
fi
done
done
echo end
}
getArgs () {
if [ "$1" == "--long" ]; then
echo "got the first param $1";
else
if [ "$1" == "--rm" ]; then
echo "got the second param $1";
else
if [ "$1" == "" ]; then
echo "got default param";
else
echo "script.sh: unknown option $1";
exit;
fi
fi
fi
}
#start script
cat /dev/null > checksums.txt;
cat /dev/null > dump.txt;
getArgs $1;
func $1;
parse;
#end script
Комментарии:
1. ИМХО, на данный момент было бы очень полезно использовать язык сценариев более высокого уровня, например, Ruby или Python — это упростило бы задачу
Ответ №1:
Это довольно просто:
if [ "$file" != "$f" ] amp;amp; [ "$md" = "$m" ]; then
echo "Files $file and $f are duplicates."
fi
Обратите внимание, что я изменил оператор сравнения с ==
на =
, который является обычной формой. Я также заключил сообщение в двойные кавычки, чтобы было ясно, что это одна строка и что я не хочу, чтобы расширение слова происходило для двух переменных file
и f
.
[Обновление:]
Другой способ поиска дубликатов, который намного быстрее, — использовать awk для обработки строк:
awk -F'|' '
NF == 2 {
if (fname[$1] != "") {
print("Files " fname[$1] " and " $2 " are duplicates.");
}
fname[$1] = $2;
}
' dump.txt
Комментарии:
1. Мне очень нравится этот оператор if. Однако я не получаю никаких дубликатов файлов, я точно знаю, что есть 2 идентичных файла. Похоже, что переменные md и m иногда получают второй фрагмент инструкции awk вместо только первого
2. Тогда, может быть, вам нужно показать нам, как вы
dump.txt
выглядите. Из кода я догадался, что он содержит два поля в строке, разделенных|
символом .
Ответ №2:
на самом деле вам не нужен цикл или два цикла, если вы решите решить его с помощью awk. Это что-то вроде nuclear head при обработке текста.
awk -F'|' '{if($1 in a)print "duplicate found:" $0 " AND "a[$1];else a[$1]=$0 }' yourfile
принесет то, что вам нужно. конечно, текстовую информацию вы могли бы настроить.
смотрите тест ниже
kent$ cat md5chk.txt
abcdefg|/foo/bar/a.txt
bbcdefg|/foo/bar2/ax.txt
cbcdefg|/foo/bar3/ay.txt
abcdefg|/foo/bar4/a.txt
1234567|/seven/7.txt
1234568|/seven/8.txt
1234567|/seven2/7.txt
kent$ awk -F'|' '{if($1 in a)print "duplicate found:" $0 " AND "a[$1];else a[$1]=$0 }' md5chk.txt
duplicate found:abcdefg|/foo/bar4/a.txt AND abcdefg|/foo/bar/a.txt
duplicate found:1234567|/seven2/7.txt AND 1234567|/seven/7.txt
обновлено
awk # the name of the tool/command
-F'|' # declare delimiter is "|"
'{if($1 in a) # if the first column was already saved
print "duplicate found:" $0 " AND "a[$1]; # print the info
else # else
a[$1]=$0 }' # save in an array named a, index=the 1st column (md5), value is the whole line.
yourfile # your input file
Комментарии:
1. это отлично работает, но что именно это делает? Это выглядит сложным. Не могли бы вы разбить это для меня?
2. На самом деле это то же самое, что и мое второе предложение.
a
в программе awk есть карта , словарь , ассоциативный массив , как бы вы это ни называли. Просматривая список файлов, в качестве ключа к карте используется хэш-код, и если запись уже есть, вы знаете, что нашли дубликат.3. Есть ли способ, при котором я печатаю только имя файла, а не md5sum?
4. Мне также понадобится возможность удалять файлы, которые являются дубликатами. Как мне получить доступ к каждому файлу по отдельности?
5. @EricAnderson awk тоже может это сделать, либо с помощью system («rm ….»), либо с помощью pipe sh. но это уже не относится к тому же вопросу. 🙂