Выбрать главу

··done

··shift $(($OPTIND — 1))

··# НАЧАЛО ОСНОВНОГО СЦЕНАРИЯ

··# =================

··# Гарантировать наличие каталога $archivedir.

··if [! -d $archivedir]; then

····if [! -w $HOME]; then

······echo "$0 failed: can't create $archivedir in $HOME" >&2

······exit 1

····fi

····mkdir $archivedir

····chmod 700 $archivedir # Ограничить доступ к каталогу.

··fi

··for arg

··do

····newname="$archivedir/$(date "+%S.%M.%H.%d.%m").$(basename "$arg")"

····if [-f "$arg" −o — d "$arg"]; then

······$copy "$arg" "$newname"

····fi

··done

··exec $realrm $flags "$@" # Текущий сценарий будет вытеснен командой realrm.

Как это работает

В этом сценарии есть много интересных аспектов, в основном связанных с необходимостью скрыть его работу от пользователя. Например, сценарий не генерирует сообщений об ошибках в ситуациях, когда обнаруживает, что не может продолжить работу; он просто позволяет команде realrm самой сгенерировать такое сообщение, вызывая (обычно) /bin/rm с иногда ошибочными параметрами. Вызов realrm производится с помощью команды exec, которая замещает текущий процесс новым, выполняющим указанную команду. Сразу после вызова команды exec realrm текущий сценарий фактически прекращает работу, и в вызывающую командную оболочку передается код возврата, генерируемый процессом realrm.

Поскольку сценарий втайне создает в домашнем каталоге пользователя новый каталог , он должен гарантировать, что хранимые в нем файлы не окажутся доступны для других только из-за неправильно настроенного значения umask. (Значение umask определяет привилегии доступа по умолчанию для создаваемых файлов и каталогов.) Чтобы избежать непреднамеренного открытия доступа к резервируемым файлам, сценарий вызывает в строке команду chmod, дающую право на доступ к каталогу только для текущего пользователя.

Наконец, в строке сценарий использует basename для удаления любой информации о каталоге из пути к файлу и добавляет в имя файла дату и время удаления в формате: секунды. минуты. часы. день. месяц. имя_файла:

newname="$archivedir/$(date "+"%S.%M.%H.%d.%m").$(basename "$arg")"

Обратите внимание на использование нескольких элементов $() для формирования нового имени файла. Хотя это немного усложняет сценарий, тем не менее такое решение эффективно. Напомним, что содержимое, заключенное между $(и) выполняется в подоболочке, а результат замещает выражение в скобках.

Но зачем усложнять реализацию добавлением даты и времени в имя резервируемого файла? Чтобы дать возможность сохранять несколько копий удаляемого файла с одним и тем же именем. После архивирования файла сценарием нельзя будет отличить /home/oops.txt от /home/subdir/oops.txt иначе как по времени удаления. Если стирание одноименных файлов произойдет одновременно (или в течение одной секунды), резервные копии файлов, удаленных первыми, будут затерты. Для решения этой проблемы можно организовать добавление абсолютных путей к оригинальным файлам в имена резервных копий.

Запуск сценария

Чтобы установить сценарий, добавьте псевдоним — тогда при вводе команды rm действительно будет вызываться этот сценарий, а не команда /bin/rm. В командных оболочках bash и ksh псевдонимы определяются так:

alias rm=yourpath/newrm

Результаты

Результаты работы этого сценария преднамеренно скрыты (как показывает листинг 2.6), так что обратим все внимание на каталог .deleted-files.

Листинг 2.6. Тестирование сценария newrm

$ ls ~/.deleted-files

ls: /Users/taylor/.deleted-files/: No such file or directory

$ newrm file-to-keep-forever

$ ls ~/.deleted-files/

51.36.16.25.03.file-to-keep-forever

Что и требовалось получить. Файл был удален из локального каталога и скрытно перемещен в каталог .deleted-files. Добавление префикса с временем удаления позволяет сохранять в каталоге одноименные файлы, удаленные в разное время, не затирая их.

Усовершенствование сценария

Как одно из усовершенствований можно предложить изменить префикс со временем, чтобы упростить вывод списка копий удаленных файлов командой ls в обратном хронологическом порядке. Ниже показана строка из сценария, подлежащая изменению:

newname="$archivedir/$(date "+"%S.%M.%H.%d.%m").$(basename "$arg")"

Можно изменить порядок следования компонентов в новом имени на противоположный, чтобы исходное имя файла следовало первым, а за ним — дата удаления в секундах. Далее, поскольку время измеряется с точностью до секунды, может так получиться, что при одновременном удалении одноименных файлов из разных каталогов (например, rm test testdir/test) произойдет затирание одной копии удаленного файла другой. Поэтому, как еще одно полезное усовершенствование, можно добавить в имя архивируемого файла его прежнее местоположение, чтобы в результате получить, например, файлы timestamp.test и timestamp.testdir.test, явно отличающиеся друг от друга.