Per verificare se un descrittore di file fa riferimento a un file normale che non ha collegamenti rimanenti in nessuna directory del filesystem, puoi creare un fstat()
system call su di esso e controlla il numero di link (st_nlink
campo) nella struttura restituita.
Con zsh
, potresti farlo con il suo stat
incorporato:
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
(la shell GNU) non ha equivalenti, ma se sei su un sistema GNU, potresti avere GNU stat
in tal caso dovresti essere in grado di fare qualcosa come:
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
Se il kernel del tuo sistema operativo è Linux, un approccio più portabile (per quei sistemi operativi che non hanno zsh
e dove le utilità principali non provengono da GNU), supponendo che il filesystem proc sia montato su /proc
potrebbe essere usare ls
su /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
Qui duplicando fd su 0 come nella soluzione precedente, quindi funziona anche se fd ha il flag close-on-exec (supponendo che fd non sia 0 in primo luogo, ma fd 0 normalmente non avrebbe il close-on-exec bandiera).
Questo tipo di approccio non funziona con il filesystem falso che è procfs di Linux per verificare se un fd si apre su /proc/<some-pid>/cmdline
si riferisce a un processo attivo:
$ zsh -c 'zmodload zsh/stat; (sleep 1; stat -f0 +nlink; cat) < /proc/$$/cmdline &'
$ 1
cat: -: No such process
Guarda come fstat().st_nlink
ha restituito 1 sopra (il che significherebbe che il file aveva ancora un collegamento a una directory), mentre cat
read()
di sul fd ha restituito un errore. Non è la solita semantica del filesystem.
In ogni caso, per verificare se il tuo genitore è ancora in esecuzione, puoi chiamare getppid()
che restituirebbe 1 o il pid del subreaper figlio se il genitore morisse. In zsh
, useresti $sysparams[ppid]
(nel zsh/system
modulo).
$ sh -c 'zsh -c '\''zmodload zsh/system
print $PPID $sysparams[ppid]
sleep 2; print $PPID $sysparams[ppid]
'\'' & sleep 1'
14585 14585
$ 14585 1
In bash
, potresti utilizzare ps -o ppid= -p "$BASHPID"
invece.
Un altro approccio sarebbe quello di creare una pipe tra padre e figlio e verificare con select
/poll
(o read -t0
in bash
) che è ancora attivo.
Potrebbe essere fatto usando un coproc
(aggiunto solo di recente a bash
) invece di &
.
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
Che danno:
$ bash ./that-script
parent still there
$ parent gone
Basandosi sul tuo approccio immaginato e assumendo ancora un kernel Linux con procfs
montato su /proc
, potresti anche fare:
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
Usando [[ file1 -ef file2 ]]
che controlla se i file too hanno lo stesso numero di dev e inode (st_dev
e st_ino
restituito da stat()
).
Sembra funzionare con 5.6.0 ma come abbiamo visto sopra quel /proc
non rispetta la consueta semantica del filesystem, non posso garantire che sia race free (PID e numero di inode potrebbero essere stati riutilizzati) o che funzionerebbe nelle future versioni di Linux.
Il tuo file originale esiste completamente invariato.
Una volta che un file è stato aperto per nome, il descrittore di file contenuto dal processo conta come collegamento al file. Il sistema non rilascia il file o il suo spazio fino a quando tutti i collegamenti non sono stati eliminati:questi possono essere un numero qualsiasi di processi che hanno una descrizione del file aperta per esso, più un numero qualsiasi di collegamenti reali.
È possibile dichiarare il file nel momento in cui è stato aperto e dichiarare il file corrente per nome. Se sono inode diversi o una data di modifica diversa, hai un file eliminato e c'è un nuovo file. Oppure potresti scoprire di avere un file eliminato ma non ne esiste uno nuovo.