Создание временных файлов в bash

150

Существуют ли объективно лучшие способы создания временных файлов в скриптах bash?

Обычно я просто называю их так, как мне кажется, например, tempfile-123, так как они будут удалены после завершения скрипта. Есть ли какой-либо недостаток в этом, кроме перезаписи возможного файла tempfile-123 в текущей папке? Или есть ли преимущество в создании временного файла более осторожным способом?

Strapakowsky
источник
1
Не используйте временные файлы. Вместо этого используйте временные каталоги. И не используйте mktemp. Вот почему: codeproject.com/Articles/15956/…
Ceving
17
@ceving Эта статья просто неверна, по крайней мере, применительно к команде оболочки mktemp (в отличие от вызова библиотеки mktemp). Поскольку mktemp создает сам файл с ограничительным umask, данная атака работает только в том случае, если атакующий работает под тем же аккаунтом, что и атакующий ... и в этом случае игра уже проиграна. Лучшие практики в мире сценариев оболочки см. На сайте mywiki.wooledge.org/BashFAQ/062
Чарльз Даффи,
Вы также можете использовать tempfile(1)в системах, которые имеют его.
Приостановлено до дальнейшего уведомления.

Ответы:

179

mktemp(1)Страница людей объясняет это довольно хорошо:

Традиционно многие сценарии оболочки принимают имя программы с суффиксом pid и используют его как временное имя файла. Такая схема именования является предсказуемой, и создаваемое ею состояние гонки легко победит злоумышленник. Более безопасный, но все же неполноценный подход заключается в создании временного каталога с использованием той же схемы именования. Хотя это позволяет гарантировать, что временный файл не будет разрушен, он все же допускает простую атаку отказа в обслуживании. По этим причинам предлагается использовать вместо этого mktemp.

В сценарии я вызываю mktemp что-то вроде

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

который создает временный каталог, в котором я могу работать, и в котором я могу смело назвать реальные файлы чем-то читаемым и полезным.

mktempне является стандартным, но он существует на многих платформах. «X», как правило, преобразуются в некоторую случайность, и больше, вероятно, будет более случайным; однако некоторые системы (например, busybox ash) ограничивают эту случайность более значительно, чем другие


Кстати, безопасное создание временных файлов важно не только для сценариев оболочки. Вот почему в python есть tempfile , в perl есть File :: Temp , в ruby ​​есть Tempfile и т. Д.

Кодзиро
источник
1
Опция -t устарела: gnu.org/software/coreutils/manual/html_node/…
Тибор Васс
7
Кажется, самый безопасный и самый кроссплатформенный способ использования mktempв сочетании с basename, вот так mktemp -dt "$(basename $0). XXXXXXXXXX". При использовании без basenameвас может появиться ошибка, такая как шаблон mktemp: неверный, `/tmp/MOB-SAN-JOB1-183-ScriptBuildTask-7300464891856663368.sh.XXXXXXXXXX ', содержит разделитель каталогов .
i4niac
7
Не обращайте внимания на опечатку (дополнительное пространство). mktemp -dt "$(basename $0).XXXXXXXXXX"это правильный путь.
i4niac
3
@ i4niac: вам нужно процитировать это $0, в стране OS X много mktemp -dt "$(basename "$0").XXXXXX"
пробелов.
11
Также может быть неплохо удалить tempdir в конце выполнения скрипта:trap "rm -rf $mydir" EXIT
KumZ
43

Да, используйте mktemp .

Он создаст временный файл в папке, предназначенной для хранения временных файлов, и гарантирует вам уникальное имя. Он выводит имя этого файла:

> mktemp
/tmp/tmp.xx4mM3ePQY
>
Павел
источник
19

Вы можете посмотреть на mktemp

Утилита mktemp берет заданный шаблон имени файла и перезаписывает его часть, чтобы создать уникальное имя файла. К шаблону может относиться любое имя файла с добавленным к нему числом «X», например /tmp/tfile.XXXXXXXXXX. Конечные «X» заменяются комбинацией текущего номера процесса и случайных букв.

Для более подробной информации: man mktemp

Джон Лоуренс
источник
11

Есть ли преимущество в создании временного файла более осторожным способом?

Временные файлы обычно создаются во временном каталоге (например, /tmp), где все другие пользователи и процессы имеют права на чтение и запись (любой другой сценарий может создавать новые файлы там). Поэтому сценарий должен быть осторожен при создании файлов, таких как использование с правильными разрешениями (например, чтение только для владельца, см . help umask:), а имя файла не должно быть легко угадано (в идеале случайное). В противном случае, если имена файлов не являются уникальными, это может привести к конфликту с одним и тем же сценарием, запущенным несколько раз (например, условие гонки).) или злоумышленник может либо перехватить некоторую конфиденциальную информацию (например, когда права доступа слишком открыты, а имя файла легко угадать), либо создать / заменить файл собственной версией кода (например, заменить команды или запросы SQL в зависимости от того, что происходит сохранены).


Вы можете использовать следующий подход для создания временного каталога:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

или временный файл:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

Однако это все еще предсказуемо и не считается безопасным.

Согласно man mktemp, мы можем прочитать:

Традиционно многие сценарии оболочки принимают имя программы с суффиксом pid и используют его как временное имя файла. Такая схема именования является предсказуемой, и создаваемое ею состояние гонки легко победит злоумышленник.

Поэтому, чтобы быть в безопасности, рекомендуется использовать mktempкоманду для создания уникального временного файла или каталога ( -d).

kenorb
источник
2
не совсем то, что спросили. Тем не менее, это может быть идеальным решением.
jpbochi
1
@jpbochi Я улучшил ответ, чтобы ответить на вопрос. Дайте мне знать, если это поможет.
Кенорб
2
Это действительно улучшает ответ. Мой голос был уже твоим. Больше не могу голосовать. Одно из предложений, которое у меня есть, - объяснить, что ${0##*/}и $$расширить, или дать ссылку на некоторую документацию по этому поводу.
Jpbochi
0

mktemp является, вероятно, наиболее универсальным, особенно если вы планируете поработать с файлом некоторое время.

Вы также можете использовать оператор подстановки процесса, <() если вам нужен только временный файл для ввода в другую команду, например:

$ diff <(echo hello world) <(echo foo bar)
Барри Джонс
источник