Чи можемо ми використовувати тимчасові папки, як тимчасові файли
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
який буде автоматично знищений після цього виходу з оболонки?
Чи можемо ми використовувати тимчасові папки, як тимчасові файли
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
який буде автоматично знищений після цього виходу з оболонки?
Відповіді:
У випадку тимчасового файлу ваш приклад у запитанні створить його, потім від’єднайте його від каталогу (змусивши його "зникнути"), а коли скрипт закриє fileescriptor (можливо, після припинення), простір, який займає файл буде відшкодовано системою. Це поширений спосіб поводження з тимчасовими файлами такими мовами, як 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
дзвінок.
.
і ..
записів. (Тестовано на Linux, я не знаю, чи це відповідає всім платформам.)
exec another-command
очевидно.
Напишіть функцію оболонки, яка буде виконуватися, коли ваш сценарій буде завершено. У наведеному нижче прикладі я називаю це «очищенням» і встановлюю пастку, яку потрібно виконати на рівнях виходу, наприклад: 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 тощо. Це явно недостатньо.
Ви можете chdir до нього, а потім видалити, за умови, що після цього не намагатиметеся використовувати шляхи всередині нього:
#! /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
і створити його коренем при встановленні; не робіть його налаштованим користувачем і не встановлюйте його на дорогу, що належить користувачеві, оскільки це може піддавати вас посиланням на пастки та інші волохаті речі, на які не варто витрачати часу.
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);
}
rm $PWD
робота, шкаралупа все ще знаходиться в тому ресі. Але жодні нові файли не можуть бути поміщені в цю "папку". Ви можете лише читати / писати з файлу & 3, & 4. Отже, це все ще "тимчасовий файл", а не "тимчасова папка".
Вам потрібна конкретна оболонка?
Якщо 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")