回答:
一時ファイルの場合、問題の例ではそれを作成し、それをディレクトリからリンク解除して(「非表示」にします)、スクリプトがファイル記述子を閉じると(おそらく終了時に)、ファイルが占めるスペースシステムによって再利用可能になります。これは、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
or TERM
信号による終了時(bash
またはksh
で実行している場合は、他のシェルEXIT
ではtrap
コマンドラインでこれらの信号を追加したい場合)、またはスクリプトの最後に到達したか、exit
コール。
.
and ..
エントリさえありません。(Linuxでテストされましたが、プラットフォーム間で一貫しているかどうかはわかりません。)
exec another-command
明らかに呼び出した場合も、EXITトラップは実行されないことに注意してください。
スクリプトが終了したときに実行されるシェル関数を記述します。以下の例では、これを「クリーンアップ」と呼び、次のように終了レベルで実行されるトラップを設定します。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
作業では、シェルはまだそのディレクトリにあります。ただし、この「フォルダ」に新しいファイルを入れることはできません。ファイル&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")