GNU/Linux >> Linux Esercitazione >  >> Linux

Controlla se un filedescriptor si riferisce a un file cancellato (in Bash)

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.


Linux
  1. Come controllare la sintassi di sudoers

  2. Script Bash:controlla se un file è un file di testo?

  3. Come controllare lo stato di avanzamento dell'esecuzione di Cp?

  4. Come controllare la sottostringa in Shell Script Bash?

  5. Come verificare se un file è vuoto in Bash?

Comando sorgente Bash

Bash:aggiungi al file

Sostituzione di String in Bash

35 Esempi di script Bash

Scripting Bash(III)

Come verificare se esiste un file o una directory in Bash Shell