Подстановка вашей команды захватывает stdout
; сообщение о завершении работы, вероятно, записывается в stderr
.
Чтобы перенаправить stderr
на stdout
, чтобы он также был захвачен, добавьте 2>&1
, т.е.
$(df -h /; sudo shutdown -r +1 2>&1)
Я бы написал эту программу таким образом, чтобы она создавала жесткую -связанную резервную копию дерева. Затем, если операция не удалась, он восстанавливает уже удаленные файлы из резервной копии. Если операция завершается успешно, резервная копия с жесткой ссылкой -удаляется.
Конечно, это только логически атомарно, а не в том смысле, что «мы можем выдернуть шнур питания в любое время» атомарно; хотя это тоже можно устроить, с дополнительной логикой и неким хуком, который запускается при загрузке -up.
Сделать два прохода (один проход для проверки разрешений, а другой проход для удаления )сложно. Логика должна быть исчерпывающей для проверки всех разрешений, включая расширенные атрибуты. Например, если мы выполним sudo chattr +i file
, то file
станет неудаляемым, даже если обычные разрешения Unix выглядят хорошо :, каталог доступен для записи. Лучшая лакмусовая бумажка на вопрос «можем ли мы удалить этот файл» — это попробовать.
Вот несколько проверенный прототип концепции, в качестве доказательства концепции, с использованием rsync
для резервного копирования и восстановления на основе жесткой -ссылки -. Этот скрипт называетсяatomic-rm.sh
:
#!/bin/sh
set -eu
if [ $# -ne 1 ] ; then
echo "specify directory to remove"
exit 1
fi
ar_src=$(realpath "$1")
ar_tmp=$(mktemp -d "$(dirname "$ar_src")/tmp-XXXXXX")
if [ $? -ne 0 ] ; then
echo "unable to create temporary directory"
exit 1
fi
cleanup()
{
find "$ar_tmp" -type d -print0 | xargs -0 chmod +w
if ! rm -rf "$ar_tmp" ; then
echo "removal of temporary directory $ar_tmp failed"
exit 2 # 2 indicates dirty failure
fi
}
trap cleanup EXIT
if ! rsync -ar --link-dest="$ar_src" "$ar_src"/ "$ar_tmp"/ ; then
echo "unable to create hard-linked backup of $ar_src in $ar_tmp"
exit 1
fi
if ! rm -rf "$ar_src" ; then
if ! rsync -ar --link-dest="$ar_tmp" "$ar_tmp"/ "$ar_src"/ ; then
echo "removal of $ar_src failed; unfortunately, so did the rollback"
exit 2 # 2 indicates dirty failure
fi
exit 1
fi
exit 0
С некоторыми дополнительными усилиями он мог бы хранить некоторую информацию где-то, которую сценарий восстановления во время загрузки -мог бы использовать для очистки незакрепленных временных каталогов. Временный каталог создается как одноуровневый для -и -удаленного каталога, чтобы гарантировать, что они находятся в одной и той же файловой системе; мы не можем использовать /tmp
.
Если удаление завершилось неудачей и был выполнен откат, это не восстановит абсолютно точное состояние дерева, потому что каталоги были перепутаны, и поэтому их временные метки модификации изменены.
Обратите внимание, что в cleanup
мы должны сделать find
проход по каталогам, чтобы сделать их доступными для записи. Причина в том, что если удаление завершается ошибкой из-за того, что каталог недоступен для записи, то точно так же произойдет сбой в резервной копии -, потому что rsync
будет реплицировать эти разрешения для каталога.
Модифицированная версия ответа Каза. Сделайте копию(cp -a
)исходного дерева, попробуйте удалить эту копию и, если удаление прошло успешно, удалите исходное дерево.
#!/bin/sh
if [ $# -ne 1 ] ; then
echo "specify directory to remove"
exit 1
fi
ar_src=$(realpath "$1")
ar_tmp=$(mktemp -d "$(dirname "$ar_src")/tmp-XXXXXX")
if [ $? -ne 0 ] ; then
echo "unable to create temporary directory"
exit 1
fi
if ! cp -a "$1" "$ar_tmp"; then
echo "unable to copy"
exit 1
fi
if rm -rf "$ar_tmp"; then
rm -rf "$ar_src";
fi
exit 0
Исполнение:
$./atomic-rm2.sh d1/
rm: cannot remove '/tmp/testRMDIR/tmp-4sOBNS/d1/d2/f2': Permission denied
$ tree d1
d1
├── d2
│ └── f2
└── f1