Цикл BASH while для проверки строк в файле отображается слишком много раз
Автор вопроса: zacgalfОтветов: 6
Я пишу скрипт, в котором хочу взять каждую строку из файла и проверить совпадение в другом файле. Если я найду совпадение, я хочу сказать, что я нашел совпадение, а если нет, то сказать, что я не нашел совпадения.
2 файла содержат хэши md5. Старый файл является исходным, а новый файл предназначен для проверки наличия каких-либо изменений по сравнению с исходным файлом.
исходный файл: chksum новый файл: chksum1
#!/bin/bash
while read e; do
while read f; do
if [[ $e = $f ]]
then
echo $e "is the same"
else
if [[ $e != $f]]
then
echo $e "has been changed"
fi
fi
done < chksum1
done < chksum
Моя проблема в том, что для файлов, которые были изменены, я получаю эхо каждый раз, когда выполняется проверка в цикле, и я хочу, чтобы он отображал файл только один раз и говорил, что он не найден.
Надеюсь, это понятно.
Можете ли вы опубликовать образец из вашего файла? ИМО awk может сделать это лучше. jaypal singh 14.06.2013
Почему вы не хотите использовать diff? diff chksum chksum1 сделает именно то, что вы хотите. Nikolai Popov 14.06.2013
Небольшое дополнение: В части else не нужно проверять, отличаются ли значения, потому что они точно есть. Просто повторите строку. TrueY 14.06.2013
+1! Спасибо за вопрос! Я снова чему-то научился. Я прочитал man bash (снова) и обнаружил, что ${var:+val} и ${var+val} ведут себя по-разному! TrueY 14.06.2013
Я пытался использовать diff, но мне было трудно, так как я должен сказать, какие файлы были отредактированы, а какие нет. diff показал мне все файлы и после перебора вариантов я решил использовать цикл zacgalf 14.06.2013
@someguy Вы правы. Сохранение простоты не похоже на карты здесь. Я разместил решение с sort и uniq, но diff работает так же хорошо, если не лучше. Samveen 14.06.2013
вы можете использовать тот же сценарий, но поставить напоминание.
#!/bin/bash
while read e; do
rem=0
while read f; do
if [[ $e = $f ]]
then
rem=1
fi
done < chksum1
if [[ rem = 1 ]]
then
echo $e "is the same"
else
echo $e "has been changed"
fi
done < chksum
Это должно работать правильно
Ответ 2 · cforbish
Вы были очень близки. Это будет работать:
while read e; do
while read f; do
found=0
if [[ $e = $f ]]
then
# echo $e "is the same"
found=1
break
fi
done < chksum1
if [ $found -ne 0 ]
then
echo "$e is the the same"
else
echo "$e has been changed"
fi
done < chksum
Ответ 3 · TrueY
Немного упрощенная версия, позволяющая избежать многократного чтения одного и того же файла (bash 4.0 и выше). Я предполагаю, что файлы содержат уникальные имена файлов, а формат файла является результатом md5sum команда.
#!/bin/bash
declare -A hash
while read md5 file; do hash[$file]=$md5; done <chksum
while read md5 file; do
[ -z "${hash[$file]}" ] && echo "$file new file" && continue
[ ${hash[$file]} == $md5 ] && echo "$file is same" && continue
echo "$file has been changed"
done <chksum1
Этот скрипт считывает первый файл в ассоциативный массив с именем hash. Индекс - это имя файла, а значение - контрольная сумма MD5. Второй цикл считывает второй файл контрольной суммы; имени файла нет в hash, он печатает file new file; если он находится в hash и значение равно, то это один и тот же файл; если не равно пишет file has been changed.
./a.txt is same
./b.txt has been changed
./d.txt new file
РАСШИРЕННАЯ ВЕРСИЯ
Также обнаруживать удаленные файлы.
#!/bin/bash
declare -A hash
while read md5 file; do hash[$file]=$md5; done <chksum
while read md5 file; do
[ -z "${hash[$file]}" ] && echo "$file new file" && continue
if [ ${hash[$file]} == $md5 ]; then echo "$file is same"
else echo "$file has been changed"
fi
unset hash[$file]
done <chksum1
for file in ${!hash[*]};{ echo "$file deleted file";}
Выход:
./a.txt is same
./b.txt has been changed
./d.txt new file
./c.txt deleted file
Ответ 4 · Samveen
Я хотел бы предложить альтернативное решение: как насчет того, чтобы вы не читали построчно, а использовали sort и uniq -c, чтобы увидеть, есть ли различия. Нет необходимости в петле, где простая труба может сделать вашу работу.
В этом случае вам нужны все строки, которые изменились в файле chksum1, поэтому
Как насчет использования команды grep. Каждая строка, которую вы читаете из chksum, будет служить шаблоном поиска в chksum1. Если grep находит совпадение, "$?", который содержит возвращаемое значение grep, будет равен 0, в противном случае он будет равен 1.
while read e; do
grep $e checksum1
if[ $? == "0" ];then
echo $e "is the same"
else
echo $e "has been changed"
fi
done < chksum