Pour tester si un descripteur de fichier fait référence à un fichier normal qui n'a plus de lien dans aucun répertoire du système de fichiers, vous pouvez créer un fstat()
appel système dessus et vérifier le nombre de liens (st_nlink
champ) dans la structure renvoyée.
Avec zsh
, vous pourriez le faire avec son stat
intégré :
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
(le shell GNU) n'a pas d'équivalent, mais si vous êtes sur un système GNU, vous pourriez avoir GNU stat
dans ce cas, vous devriez pouvoir faire quelque chose comme :
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
Si votre noyau de système d'exploitation est Linux, une approche plus portable (pour les systèmes d'exploitation qui n'ont pas zsh
et où les utilitaires principaux ne proviennent pas de GNU), en supposant que le système de fichiers proc est monté sur /proc
pourrait être d'utiliser ls
le /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
Ici, dupliquer le fd sur 0 comme dans la solution précédente, donc cela fonctionne même si fd a le drapeau close-on-exec (en supposant que fd n'est pas 0 en premier lieu, mais fd 0 n'aurait normalement pas le close-on-exec drapeau).
Ce genre d'approche ne fonctionne pas avec le faux système de fichiers qu'est le procfs de Linux pour vérifier si un fd s'ouvre sur /proc/<some-pid>/cmdline
fait référence à un processus en direct :
$ zsh -c 'zmodload zsh/stat; (sleep 1; stat -f0 +nlink; cat) < /proc/$$/cmdline &'
$ 1
cat: -: No such process
Découvrez comment fstat().st_nlink
renvoyé 1 ci-dessus (ce qui signifierait que le fichier avait toujours un lien vers un répertoire), tandis que cat
est read()
sur le fd a renvoyé une erreur. Ce n'est pas la sémantique habituelle du système de fichiers.
Dans tous les cas, pour vérifier si ton parent court toujours, tu peux appeler le getppid()
qui renverrait 1 ou le pid du sous-moissonneur enfant si le parent mourait. En zsh
, vous utiliseriez $sysparams[ppid]
(dans le zsh/system
module).
$ sh -c 'zsh -c '\''zmodload zsh/system
print $PPID $sysparams[ppid]
sleep 2; print $PPID $sysparams[ppid]
'\'' & sleep 1'
14585 14585
$ 14585 1
En bash
, vous pouvez utiliser ps -o ppid= -p "$BASHPID"
à la place.
Une autre approche serait de créer un tuyau entre le parent et l'enfant et de vérifier avec select
/poll
(ou read -t0
en bash
) qu'il est toujours actif.
Peut être fait en utilisant un coproc
(ajouté récemment à bash
) au lieu de &
.
background_with_pipe() {
coproc "[email protected]" {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
Ce qui donne :
$ bash ./that-script
parent still there
$ parent gone
Construire sur votre approche envisagée, et encore une fois en supposant un noyau Linux avec procfs
monté sur /proc
, vous pouvez également faire :
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
Utilisation de [[ file1 -ef file2 ]]
qui vérifient si les fichiers too ont le même numéro dev et inode (st_dev
et st_ino
renvoyé par stat()
).
Cela semble fonctionner avec 5.6.0 mais comme nous l'avons vu ci-dessus, /proc
ne respecte pas la sémantique habituelle du système de fichiers, je ne peux pas garantir qu'il est sans course (le PID et le numéro d'inode auraient pu être réutilisés) ou qu'il fonctionnerait dans les futures versions de Linux.
Votre fichier d'origine existe complètement inchangé.
Une fois qu'un fichier a été ouvert par son nom, le descripteur de fichier que contient votre processus compte comme un lien vers le fichier. Le système ne libère pas le fichier ou son espace tant que tous les liens n'ont pas été supprimés :il peut s'agir de n'importe quel nombre de processus pour lesquels une description de fichier est ouverte, plus n'importe quel nombre de liens physiques.
Vous pouvez stat le fichier au moment où il a été ouvert, et stat le fichier actuel par son nom. S'il s'agit d'inodes différents ou d'une date de modification différente, vous avez un fichier supprimé et il y a un nouveau fichier. Ou vous constaterez peut-être que vous avez un fichier supprimé mais qu'il n'en existe pas de nouveau.