ファイル記述子が削除されたファイルを参照していることを確認してください(Bashから)

ファイル記述子が削除されたファイルを参照していることを確認してください(Bashから)

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

zshOSカーネルが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のcatsは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 -t0in bash)を使用してパイプがまだ実行されていることを確認することです。

coprocbash代わりに(最近追加された)を使用してこれを実行できます&

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バージョンでは。

おすすめ記事