Команда mktemp в Linux создаёт временные файлы и каталоги с уникальными, непредсказуемыми именами, что является самым безопасным способом работы с временными данными в скриптах оболочки и системных задачах.
Большинство скриптов оболочки, которые вы найдете в Интернете, создают временные файлы с именами типа /tmp/mydata.txt или /tmp/backup-$$.log, что работает до тех пор, пока не запустятся две копии скрипта одновременно, или пока злонамеренный пользователь не угадает имя файла и не создаст символьную ссылку в /tmp, указывающую на /etc/passwd. Внезапно ваш безобидный скрипт начинает перезаписывать системные файлы, и у вас появляется реальная проблема.
Решением является mktemp — небольшая утилита, которая создает файл или каталог с гарантированно уникальным именем и безопасными правами доступа, а затем возвращает вам путь к нему. Каждый серьезный скрипт системного администратора использует её, и как только вы поймёте, как она работает, вы никогда больше не вернётесь к ручному созданию имен временных файлов.
Почему системные администраторы полагаются на mktemp
Каталог /tmp является общим для всех пользователей системы, а это означает, что любой скрипт, записывающий туда данные, должен предполагать, что другие процессы одновременно читают и записывают в него.
Если два человека запускают один и тот же скрипт резервного копирования и оба пытаются создать /tmp/backup.log, один из них незаметно перезапишет файл другого. Подобные условия гонки являются одним из самых давних источников ошибок в скриптах Unix.
mktemp решает сразу две проблемы. Во-первых, он гарантирует уникальность имени файла, добавляя случайные символы, так что параллельные запуски никогда не конфликтуют. Во-вторых, он создает файл атомарно с правами доступа, установленными на 600, что означает, что только владелец может читать или записывать в него, что блокирует целую категорию атак через символьные ссылки.
Вы найдете mktemp предустановленным в каждом современном дистрибутиве Linux, поскольку он поставляется как часть пакета GNU coreutils. Устанавливать ничего не нужно, и команда работает одинаково в Ubuntu, Debian, RHEL, Rocky Linux и Fedora.
Объяснение базового синтаксиса mktemp
Самая простая форма команды не принимает никаких аргументов. Просто введите mktemp, и она выведет путь к новому файлу, который только что создала для вас. Файл уже существует на диске до того, как команда завершит работу, что и делает операцию безопасной.
mktemp
Вывод:
/tmp/tmp.kJ9mN2xQ8F
Случайное расширение kJ9mN2xQ8F меняется при каждом запуске команды, что и гарантирует уникальность. Теперь вы можете перенаправить вывод скрипта в этот путь, будучи уверенными, что ничто другое в системе не затронет его. Права доступа к новому файлу автоматически устанавливаются на чтение-запись только для вашего пользователя.
Распространенные ошибки новичков
Новые пользователи часто запускают mktemp, а затем забывают сохранить возвращенный путь, что означает, что они теряют след только что созданного файла. Всегда присваивайте вывод переменной, как показано в TMPFILE=$(mktemp), чтобы ваш скрипт мог обратиться к нему позже.
1. Создание временного файла внутри скрипта
Стандартный шаблон в боевых скриптах заключается в том, чтобы сохранить путь в переменной и настроить механизм очистки, чтобы файл удалялся при завершении работы скрипта, даже если он завершится с ошибкой. Этот шаблон из трех строк — самое полезное, что нужно запомнить о mktemp.
TMPFILE=$(mktemp) trap "rm -f $TMPFILE" EXIT echo "some data" > $TMPFILE cat $TMPFILE
Вывод:
some data
Строка trap указывает оболочке запускать rm -f $TMPFILE при выходе из скрипта по любой причине, чтобы в /tmp никогда не оставались заброшенные файлы.
Даже если ваш скрипт завершится досрочно из-за ошибки, trap все равно сработает и очистит файлы. Именно это делает этот шаблон безопасным для долго работающих скриптов.
Распространенная ошибка новичков: Забыть строку trap — это ошибка номер один, и за несколько недель ваш /tmp заполнится брошенными файлами. Всегда сочетайте mktemp с очисткой в следующей же строке.
Распространенные ошибки новичков
Забыть строку trap — это ошибка номер один, и за несколько недель ваш /tmp заполнится брошенными файлами. Всегда сочетайте mktemp с очисткой в следующей строке.
2. Создание временного каталога с помощью -d
Когда вашему скрипту нужно работать с несколькими файлами, например, извлекать архив tar или подготавливать сборку, вам нужен целый каталог, а не один файл. Флаг -d обрабатывает этот случай и возвращает путь к только что созданному частному каталогу.
TMPDIR=$(mktemp -d) echo $TMPDIR ls -ld $TMPDIR
Вывод:
/tmp/tmp.8vX2mNqL4K drwx------ 2 ravi ravi 4096 Apr 20 10:42 /tmp/tmp.8vX2mNqL4K
Права доступа drwx------ означают, что только ваш пользователь может войти в каталог или просматривать его содержимое, что как раз и нужно для частного рабочего пространства. Очищайте его с помощью rm -rf $TMPDIR в вашем trap вместо простого rm -f, иначе удаление не удастся, поскольку rm без -r отказывается удалять каталоги.
Распространенные ошибки новичков
Использование rm -f на каталоге завершается без ошибок и оставляет каталог на месте. Всегда используйте rm -rf при очистке каталога, созданного с помощью mktemp -d.
3. Используйте пользовательский шаблон для понятных имен
Когда вы отлаживаете и вам нужно найти временные файлы ваших скриптов в /tmp, пользовательский шаблон упрощает их поиск. Шаблон должен заканчиваться как минимум тремя символами X, которые mktemp заменяет случайными данными, чтобы гарантировать уникальность.
mktemp backup-log-XXXXXX
Вывод:
backup-log-a8K2mQ
Обратите внимание, что файл был создан в текущем каталоге, а не в /tmp, поскольку шаблон не содержал полного пути. Это удивляет многих при первом знакомстве. Чтобы заставить файл создаваться в /tmp независимо от места выполнения скрипта, объедините шаблон с флагом -t, показанным в следующем примере.
Распространенные ошибки новичков
Использование менее трех символов X приводит к ошибке, подобной mktemp: too few X's in template, поэтому придерживайтесь шести символов X в качестве безопасного значения по умолчанию.
4. Принудительное размещение файлов в /tmp с помощью -t
Флаг -t указывает mktemp разместить файл в каталоге, указанном переменной среды TMPDIR, с переходом на /tmp, если эта переменная не задана. Этот шаблон следует использовать в скриптах, которые вы планируете распространять.
mktemp -t backup-XXXXXX.log
Вывод:
/tmp/backup-a8K2mQ.log
Теперь ваш скрипт учитывает настройку TMPDIR пользователя, что важно в системах, где /tmp имеет небольшой размер или где администраторы перенесли временное хранилище на диск большего объема. Скрипт работает корректно везде без изменений. Именно благодаря этой переносимости -t используется в большинстве инструментов оболочки с открытым исходным кодом.
Распространенные ошибки новичков
Жесткое кодирование /tmp/ в шаблоне сводит на нет смысл использования -t. Пусть mktemp выбирает каталог, чтобы ваш скрипт учитывал системную конфигурацию.
5. Выполните пробный запуск с помощью –dry-run
Иногда вам просто нужно имя, не создавая файл, например, при генерации пути для передачи другой команде, которая сама создаст файл. Флаг --dry-run возвращает безопасное уникальное имя, не затрагивая файловую систему вообще.
mktemp --dry-run -t staging-XXXXXX
Вывод:
/tmp/staging-pL9mK3
Файл /tmp/staging-pL9mK3 ещё не существует, поэтому последующая команда, ожидающая, что файл уже будет на месте, завершится с ошибкой. Используйте --dry-run только в том случае, если следующая команда в вашем конвейере сама создаст путь, например, при распаковке tar в каталог или ssh-keygen записи нового файла ключа.
Распространенная ошибка новичков
Если рассматривать вывод --dry-run как реальный путь к файлу и записывать в него, не создав сначала файл, то получится No such file or directory error. Либо пропустите –-dry-run, либо добавьте после него touch или mkdir.
Наиболее полезные флаги mktemp
Нескольких флагов достаточно для почти всех реальных скриптов, которые вы будете писать с помощью команды mktemp. В повседневной работе вам редко понадобится больше пяти из них.
-d– создает каталог вместо файла.-t– помещает файл внутрь$TMPDIRили/tmp.-p DIR– помещает файл в указанный вами каталог.-uили--dry-run– возвращает имя, не создавая ничего.-q– подавляет сообщения об ошибках, что полезно, когда вы сами обрабатываете ошибки.
О флаге -p стоит знать, потому что он позволяет выбрать любой каталог, которым вы управляете, например mktemp -p /var/cache/myapp. Это удобно, когда /tmp смонтирован с помощью noexec и вам нужно записать туда скрипт, или когда вы хотите, чтобы временные данные хранились на конкретном диске с большим объёмом свободного места.
Заключение
Теперь вы знаете, почему вручную сгенерированные имена временных файлов — плохая идея, как mktemp решает проблему с атомарным созданием и безопасными правами доступа, а также пять шаблонов, которые охватывают практически каждый скрипт, который вы когда-либо напишете.
Сочетание mktemp и строки очистки trap — это самая полезная привычка, которую вы можете приобрести для написания надёжных скриптов bash, и она избавит вас от целого класса ошибок, которые очень сложно отследить в производственной среде.
Откройте терминал прямо сейчас и попробуйте применить трёхстрочный шаблон из примера, скопируйте:
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT; echo test > $TMPFILE; cat $TMPFILE
в скрипт, запустите его, а затем выполните ls /tmp, чтобы убедиться, что файл исчез после завершения работы скрипта. Это одно упражнение научит вас больше, чем чтение десяти справочных страниц по данной теме.
Комментарии (2)