Answers:
对于临时文件,问题中的示例将创建它,然后从目录取消链接(使其“消失”),并且当脚本关闭filedescriptor时(可能在终止时),文件占用的空间将被系统回收。这是处理诸如C之类的临时文件的常用方法。
据我所知,不可能以相同的方式打开目录,至少不能以任何使目录可用的方式打开。
在脚本终止时删除临时文件和目录的常用方法是安装清除EXIT
陷阱。下面给出的代码示例避免了完全摆弄filedescriptor。
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
命令行中添加这些信号,或者在其他shell中由于在脚本末尾或执行一个exit
呼叫。
.
和..
条目。(在Linux上测试过,我不知道这在各个平台上是否一致。)
exec another-command
明显调用,则也不执行EXIT陷阱。
编写一个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)时运行。当脚本由于SIGTERM,SIGSEGV,SIGKILL,SIGPIPE等而退出时,它将无法运行cleanup
。这显然是不足的。
您可以将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
我没有检查过,但是在C中使用openat(2)并在文件系统中不再存在目录的情况下,这可能是相同的问题。
如果您是root用户并且在Linux上,则可以mount -t tmpfs tmpfs /dir
在其内部使用单独的名称空间。
如果您的脚本被迫进入不干净的出口(例如,使用SIGKILL),则规范的答案(在EXIT上设置陷阱)将不起作用。可能会使敏感数据四处徘徊。
更新:
这是一个实现命名空间方法的小实用程序。应该用
cc -Wall -Os -s chtmp.c -o chtmp
和给定的CAP_SYS_ADMIN
文件功能(作为根)
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);
}
rm $PWD
shell仍在该目录中。但是不能将新文件放入此“文件夹”。您唯一可以做的就是读写文件&3,&4。因此,这仍然是“临时文件”,而不是“临时文件夹”。
您需要特定的外壳吗?
如果选择zsh,请阅读zshexpn(1)
:
如果使用=(...)而不是<(...),则作为参数传递的文件将是包含列表进程输出的临时文件的名称。这可以代替<形式的一个程序,希望
lseek
(参见lseek(2)
上输入文件)。[...]
每当shell放弃具有需要临时文件的替换作业时,都会出现另一个问题,包括在包含替换的命令末尾出现
&!
或&|
出现的情况。在这种情况下,临时文件将不会被清理,因为外壳不再具有该作业的任何内存。一种解决方法是使用子外壳,例如,(mycmd =(myoutput)) &!
因为分叉的子shell将等待命令完成,然后删除临时文件。
确保进程替换持续一段适当时间的一般解决方法是将其作为参数传递给匿名Shell函数(使用功能范围立即运行的一部分Shell代码)。例如,此代码:
() { 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")