Bash(Linux)で filedescriptor が指すファイルが削除されたことを確認したいと思います。
全部読んだファイル記述子が有効かどうかをテストするそしてファイル記述子が有効かどうかをテスト(入力用)。ただし、これらの回答は少し異なる質問には役立ちません。
次のテストケースを使用してください。
# create file
echo hello > /tmp/test.txt
# open read-only fd
exec 3< /tmp/test.txt
# delete file
rm /tmp/test.txt
# special zero-timeout to check if data available for reading
if read -u 3 -t 0
then
echo "data available for reading"
else
echo "no data available"
fi
# close fd (clean up)
exec 3<&-
スクリプトは驚くべきことに「データを読むことができます」と言います。ただし、ファイルはもう存在しません。したがって、いくつかのキャッシュ/バッファリングを実行する必要があります。たぶん別の方法がありますか、それともバッファ/キャッシュを避けますか?
別の考えられるアプローチはls -l /proc/$$/fd/3
次のとおりです-> '/tmp/test.txt (deleted)'
。しかし、私は純粋なBashソリューションを好みます(あまりにも多くの新しいプロセスを作成したり、標準出力を解析したりしません)。
他の場合は、もちろん[ -e /tmp/test.txt ]
小切手を使用することもできます。ただし、元のファイルが削除されたかどうかを知る必要があります。まったく同じファイル名を持つ新しいファイルを同時に作成できます。。
なぜ誰かにこの特定の結果が必要なのか疑問に思った人のために(XYの問題)、これはリサイクルPIDとの競合を防ぐ&
ために追加のfdを開いて、親スクリプトがまだ実行されているかどうかをサブシェルで(使用して)安全にチェックするために使用できます。/proc/$$/cmdline
答え1
ファイル記述子がファイルシステム内のすべてのディレクトリに残っているリンクを持たない一般ファイルを参照しているかどうかをテストするには、そのファイルに対してシステムコールを実行し、返された構造内のリンクfstat()
(フィールド)の数を確認できます。st_nlink
thatを使用すると、組み込み関数をzsh
使用してstat
これを行うことができます。
zmodload zsh/stat
fd=3
if
stat -s -H st -f $fd && # can be fstat'ed (is an opened fd)
[[ $st[mode] = -* ]] && # is a regular file
((st[nlink] == 0)) # has no link on the filesystem
then
print fd $fd is open on a regular file that has no link in the filessystem
fi
bash
(GNUシェル)これに対応するものはありませんが、GNUシステムを使用している場合はおそらくGNUがあります。stat
この場合、次のことができます。
fd=3
if [ "$(LC_ALL=C stat -c %F:%h - <&"$fd")" = 'regular file:0' ]; then
printf '%s\n' "fd $fd is open on a regular file that has no link in the filessystem"
fi
zsh
OSカーネルがLinuxの場合、procファイルシステムがマウントされていると仮定し、より移植可能なアプローチ(カーネルがなく、コアユーティリティがGNUで提供されていない場合)は、次のものを使用する/proc
ことです。ls
/proc/self/fd/$fd
if
LC_ALL=C TZ=UTC0 ls -nLd /proc/self/fd/0 <&"$fd" |
LC_ALL=C awk -v ret=1 '
NF {if ($1 ~ /^-/ && $2 == 0) ret=0; exit}
END {exit(ret)}'
then
printf '%s\n' "fd $fd is open on a regular file that has no link in the filessystem"
fi
これは以前の解決策のようにfdを0にコピーするので、fdにclose-on-execフラグがあっても機能します(fdが最初に0ではないと仮定しますが、fd 0には通常close-on-execはありません)。旗)バナー)。
このメソッドは、オープンfdが/proc/<some-pid>/cmdline
リアルタイムプロセスを参照していることを確認するために、Linuxのprocfs偽のファイルシステムでは機能しません。
$ zsh -c 'zmodload zsh/stat; (sleep 1; stat -f0 +nlink; cat) < /proc/$$/cmdline &'
$ 1
cat: -: No such process
fstat().st_nlink
上記のコマンドは1(ファイルにまだディレクトリへのリンクがあることを意味します)を返しますが、fdのcat
sはread()
エラーを返す方法を確認します。これは一般的なファイルシステムの意味ではありません。
いずれの場合でも、親プロセスがまだ実行されているかどうかを確認するには、それを呼び出すことができます。getppid()
親プロセスが終了すると、1または子子子子リッパーのpidを返します。 (モジュール内)zsh
を使用できます。$sysparams[ppid]
zsh/system
$ sh -c 'zsh -c '\''zmodload zsh/system
print $PPID $sysparams[ppid]
sleep 2; print $PPID $sysparams[ppid]
'\'' & sleep 1'
14585 14585
$ 14585 1
でbash
使用できますps -o ppid= -p "$BASHPID"
。
別の方法は、親と子の間にパイプを作成し、select
/ poll
(またはread -t0
in bash
)を使用してパイプがまだ実行されていることを確認することです。
coproc
bash
代わりに(最近追加された)を使用してこれを実行できます&
。
background_with_pipe() {
coproc "$@" {PARENT_FD}<&0 <&3 3<&- >&4 4>&-
} 3<&0 4>&1
parent_gone() {
local ignore
read -t0 -u "$PARENT_FD" ignore
}
background_with_pipe eval '
parent_gone || echo parent still there
sleep 2
parent_gone && echo parent gone
'
sleep 1
exit
以下を提供します。
$ bash ./that-script
parent still there
$ parent gone
想定したアプローチに基づいて、Linuxカーネルがprocfs
コンピュータにインストールされていると仮定すると、/proc
次のこともできます。
exec {PARENT_CANARY}< /proc/self/cmdline; PARENT_PID=$BASHPID
parent_gone() {
! [[ /proc/$PARENT_PID/cmdline -ef /proc/self/fd/$PARENT_CANARY ]]
}
(
parent_gone || echo parent still there
sleep 2
parent_gone && echo parent gone
) &
sleep 1
[[ file1 -ef file2 ]]
ファイルのdev番号とinode番号が同じであることを確認してst_dev
返すst_ino
には、それを使用しますstat()
。
これは5.6.0で動作しているように見えますが、上記のように一般的な/proc
ファイルシステムの意味に従わず、競合がないか(PIDとinode番号が再利用された可能性がある)、使用できることを保証することはできません。今後のLinuxバージョンでは。
答え2
元のファイルは完全に変更されませんでした。
ファイルが名前で開くと、プロセスによって保存されたファイル記述子はファイルへのリンクと見なされます。システムは、すべてのリンクが削除されるまでファイルまたはそのスペースを解放しません。これは、ファイル記述を開いたプロセス数やハードリンク数に関係なく可能です。
ファイルが開いている間は、ファイル数を数え、現在のファイルを名前で数えます。 inodeが異なる場合、または変更日が異なる場合は、削除されたファイルと新しいファイルがあります。または、削除されたファイルが存在するが新しいファイルが存在しない場合があります。
答え3
ファイル記述子が削除されたファイルusrを参照しているかどうかをbashでテストします/proc/pid/fd
。以下の例
$ ps -fp 52
UID PID PPID C STIME TTY TIME CMD
steve 52 7 0 18:07 tty1 00:00:00 tail -f x1.pdf
$ ls -l /proc/52/fd
total 0
lrwx------ 1 steve steve 0 Jun 13 18:07 0 -> /dev/tty1
lrwx------ 1 steve steve 0 Jun 13 18:07 1 -> /dev/tty1
lrwx------ 1 steve steve 0 Jun 13 18:07 2 -> /dev/tty1
lr-x------ 1 steve steve 0 Jun 13 18:07 3 -> /mnt/c/temp/x1.pdf (deleted)
$