Я написал следующий скрипт для интерактивного и рекурсивного удаления потерянных файлов резервных копий, т. Е. Удаления каждого file.txt~
, который не имеет соответствующего file.txt
.
#!/bin/sh -x
set -o errexit
unalias -a
backups=$(find . -name "*~")
orphans=""
while read -r file
do
[ ! -e "${file%~}" ] && orphans=$(echo "$file\n$orphans");
done << EOF
$backups
EOF
if [ -z "$orphans" ]; then
echo "No orphans."
else
echo "orphans:\n$orphans"
echo "$orphans" | xargs --interactive -d '\n' rm
fi
Этот сценарий делает очень странные вещи случайным образом. Иногда он ведет себя правильно, иногда он игнорирует параметры -x, переданные sh, иногда он выполняет закомментированный код, иногда тесты дают просто неправильные результаты.
Кажется, что проблема связана со скриптом here, так как при перенаправлении вывода find во временный файл все проблемы исчезают. Но почему, где ошибка?
Решение (благодаря Dennis Williamson): Побег ~
характер. Неэкранированный ~
в расширении параметра ${file%~}
каким-то образом создавал непредсказуемое поведение.
Более читаемое и детерминированное решение, с небольшим количеством жира, может быть (благодаря предложениям Микеля):
#!/bin/sh
IFS='
'
for backup in $(find . -type f -name "*~"); do
if [ ! -e "${backup%\~}" ]; then
rm -i "$backup"
fi
done
Если вы являетесь поклонником цикла while read
, все становится менее изящным, потому что интерактивная команда rm -i
не может использоваться (она будет конфликтовать с командой read
). В любом случае, решение может быть:
#!/bin/sh
orphans=""
while read -r backup; do
if [ ! -e "${backup%\~}" ]; then
orphans=$(echo "$backup\n$orphans");
fi
done << EOF
$(find . -type f -name "*~")
EOF
if [ ! -z "$orphans" ]; then
echo "$orphans" | xargs --interactive -d '\n' rm
fi
Или Деннис Уильямсон также предлагает более сложный способ.