Временная папка, которая автоматически уничтожается после завершения процесса

Ответы:

12

В случае временного файла ваш пример в вопросе создаст его, затем отсоединит его от каталога (заставит его «исчезнуть»), а когда скрипт закроет дескриптор файла (возможно, по завершении), пространство, занимаемое файлом будет исправимо системой. Это распространенный способ работы с временными файлами на таких языках, как C.

Насколько я знаю, невозможно открыть каталог таким же образом, по крайней мере, никоим образом, который бы сделал каталог пригодным для использования.

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

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT

# The rest of the script goes here.

Или вы можете вызвать функцию очистки:

cleanup () {
    rm -f "$tmpfile"
    rm -rf "$tmpdir"
}

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap cleanup EXIT

# The rest of the script goes here.

EXITЛовушка не будет выполняться после приема KILLсигнала (который не может быть в ловушке), что означает , что не будет никакого очистки не выполняется тогда. Однако он будет выполняться при завершении из-за сигнала INTили TERM(при запуске с bashили ksh, в других оболочках вы можете добавить эти сигналы после EXITв trapкомандной строке) или при нормальном выходе из-за прибытия в конец сценария или выполнения exitвызов.

Кусалананда
источник
5
Это не просто оболочка, которая не может использовать уже несвязанные временные каталоги - также не могут C-программы. Проблема в том, что несвязанные каталоги не могут содержать файлы. В качестве рабочего каталога вы можете использовать несвязанный пустой каталог, но любая попытка создать файл приведет к ошибке.
Дероберт
1
@derobert И такой несвязанными каталог даже не имеет .и ..запись. (Протестировано на Linux, я не знаю, совместимо ли это на разных платформах.)
kasperd
1
Обратите внимание, что ловушка EXIT также не выполняется, если скрипт вызывает exec another-commandявно.
Стефан Шазелас
1
Смотрите также: выходная ловушка в тире против ksh и bash
Стефан Шазелас
6

Напишите shell-функцию, которая будет выполнена, когда ваш скрипт завершит работу. В приведенном ниже примере я называю это «очистка» и устанавливаю ловушку для выполнения на уровнях выхода, например: 0 1 2 3 6

trap cleanup 0 1 2 3 6

cleanup()
{
  [ -d $TMP ] && rm -rf $TMP
}

Смотрите этот пост для получения дополнительной информации.

Дирк Кригсман
источник
Это не «уровни выхода», а номера сигналов, и ответ на вопрос, на который вы ссылаетесь, объясняет именно это. Ловушка будет работать cleanupперед чистым выходом (0) и при получении SIGHUP (1), SIGINT (2), SIGQUIT (3) и SIGABRT (6). он не будет работать cleanupпри выходе из сценария из-за SIGTERM, SIGSEGV, SIGKILL, SIGPIPE и т. д. Это явно недостаточно.
мосви
6

Вы можете зайти в него, а затем удалить его, при условии, что впоследствии вы не будете пытаться использовать пути внутри него:

#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"

echo yes >&4    # OK
cat <&3         # OK

cat file        # FAIL
echo yes > file # FAIL

Я не проверял, но это, скорее всего, та же проблема при использовании openat (2) в C с каталогом, который больше не существует в файловой системе.

Если вы root и в Linux, вы можете играть как с отдельным пространством имен, так и mount -t tmpfs tmpfs /dirвнутри него.

Канонические ответы (ставить ловушку на EXIT) не работают, если ваш скрипт принудительно завершает нечистый выход (например, с помощью SIGKILL); это может привести к зависанию конфиденциальных данных.

Обновить:

Вот небольшая утилита, которая реализует подход пространства имен. Это должно быть скомпилировано с

cc -Wall -Os -s chtmp.c -o chtmp

и данные CAP_SYS_ADMINвозможности файла (как root) с

setcap CAP_SYS_ADMIN+ep chtmp

Когда запускается (как обычный) пользователь как

./chtmp command args ...

он откроет пространство имен своей файловой системы, смонтирует файловую систему tmpfs /proc/sysvipc, вставит в нее chdir и запустит commandс заданными аргументами. неcommand будет наследовать возможности.CAP_SYS_ADMIN

Эта файловая система не будет доступна из другого процесса, из которого commandона не запущена , и она волшебным образом исчезнет (со всеми файлами, которые были созданы внутри нее), когда commandи ее дочерние элементы умрут, независимо от того, как это произойдет. Обратите внимание, что это просто отключение пространства имен монтирования - нет жестких барьеров между commandдругими процессами, выполняемыми одним и тем же пользователем; они все еще могут проникнуть в его пространство имен либо с помощью ptrace(2), /proc/PID/cwdлибо другими способами.

Похищение «бесполезных» /proc/sysvipc, конечно, глупо, но альтернативой было бы спам /tmpс пустыми каталогами, которые пришлось бы удалить, или сильно усложнило бы эту маленькую программу с форками и ожиданиями. В качестве альтернативы dirможно изменить, например, на. /mnt/chtmpи создать его с правами root при установке; не делайте его настраиваемым пользователем и не устанавливайте его по пути, принадлежащему пользователю, поскольку это может подвергнуть вас ловушкам символической ссылки и другим опасным вещам, на которые не стоит тратить время.

chtmp.c

#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
        char *dir = "/proc/sysvipc";    /* LOL */
        if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
        argv++;
        if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
        /* "modern" systemd remounts all mount points MS_SHARED
           see the NOTES in mount_namespaces(7); YUCK */
        if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
                err(1, "mount(/, MS_REC|MS_PRIVATE)");
        if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
        if(chdir(dir)) err(1, "chdir %s", dir);
        execvp(*argv, argv);
        err(1, "execvp %s", *argv);
}
qubert
источник
1
Даже если вы не root, вы можете сделать это с пространствами имен, создав новое пространство имен пользователя и установив внутри него tmpfs. Контрабандный доступ к новому каталогу во внешний мир немного сложен, но должен быть возможен.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
Это все еще требует CAP_SYS_ADMIN. У меня есть идея маленькой утилиты с поддержкой setcap, которая будет делать это, я обновлю ответ с ней.
Квеберт
1
Если ядро ​​не заблокировано, чтобы запретить его, создание пользовательских пространств имен не является привилегированной операцией. Базовый дизайн таков, что он должен быть безопасным, чтобы позволить обычным пользователям обходиться без каких-либо специальных возможностей. Однако существует достаточная поверхность атаки / риск, что многие дистрибутивы отключают ее, я думаю.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ЛЬДУ
Я попробовал в терминале. В каком-то временном каталоге, rm $PWDработа, оболочка еще в этом каталоге. Но никакие новые файлы не могут быть помещены в эту «папку». Только вы можете читать / писать с файлами & 3, & 4. Так что это все еще «временный файл», а не «временная папка».
Боб Джонсон
@BobJohnson Это ничем не отличается от того, что я уже говорил в своем ответе ;-)
Qubert
0

Вам требуется определенная оболочка?

Если Zsh вариант, пожалуйста, прочитайте zshexpn(1):

Если вместо <(...) используется = (...), то файл, переданный в качестве аргумента, будет именем временного файла, содержащего выходные данные процесса списка. Это может использоваться вместо формы <для программы, которая ожидает lseek(см. lseek(2)) Во входном файле.

[...]

Другая проблема возникает всякий раз, когда работа с заменой, для которой требуется временный файл, закрывается оболочкой, включая случай, когда &!или &|появляется в конце команды, содержащей подстановку. В этом случае временный файл не будет очищен, поскольку у оболочки больше нет памяти для задания. Обходной путь должен использовать подоболочку, например,

(mycmd =(myoutput)) &!

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

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

() {
   print File $1:
   cat $1
} =(print This be the verse)

выводит что-то похожее на следующее

File /tmp/zsh6nU0kS:
This be the verse

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

# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")
Барт
источник