Verifica se un descrittore di file è valido


12

Vorrei fare in modo che uno script bash fornisca informazioni aggiuntive ai descrittori di file (FD) maggiori o uguali a 3, quando sono aperti. Per verificare se un FD è aperto, ho escogitato il seguente trucco:

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

Questo è sufficiente per le mie esigenze, ma sono curioso di sapere se esiste un modo più idiomatico di testare se un FD è valido. Sono particolarmente interessato al fatto che esista una mappatura della fcntl(1)syscall su un comando shell, che consentirebbe il recupero dei flag FD ( O_WRONLYe O_RDWRper verificare se l'FD è scrivibile O_RDONLYe O_RDWRper verificare se l'FD è leggibile).

Risposte:


12

In ksh(entrambe le varianti AT&T e pdksh) o zsh, puoi fare:

if print -nu3; then
  echo fd 3 is writeable
fi

Non scriveranno nulla su quel fd, ma controllano comunque se il fd è scrivibile (usando fcntl(3, F_GETFL)) e segnalano un errore altrimenti:

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(a cui puoi reindirizzare /dev/null).

Con bash, penso che la tua unica opzione sia quella di verificare se un dup()successo ha successo nel tuo approccio, anche se ciò non garantisce che il fd sia scrivibile (o chiama un'utilità esterna ( zsh/ perl...) per fare fcntl()).

Si noti che in bash(come la maggior parte delle shell), se si utilizza al (...)posto di {...;}quello, verrà eseguito un fork di un processo aggiuntivo. Puoi usare:

if { true >&3; } 2<> /dev/null

invece per evitare il fork (tranne nella shell Bourne in cui il reindirizzamento di comandi composti provoca sempre una subshell). Non usare al :posto di truequello che è un builtin speciale , quindi la shell si chiuderebbe quando bash è in modalità di conformità POSIX.

Puoi tuttavia accorciarlo a:

if { >&3; } 2<> /dev/null

@mikeserve, re: la tua modifica, con che cosa <>? La shell non leggerà dal suo standard, perché dovresti aprirla in read + write? Cosa intendi con cosa è successo a intrinseco? ?
Stéphane Chazelas,

7

Nella descrizione dell'utilizzo dell'applicazione POSIX troverai quanto segue:command

Ci sono alcuni vantaggi nel sopprimere occasionalmente le caratteristiche speciali degli incorporamenti speciali. Per esempio:

command exec > unwritable-file

non provoca l'interruzione di uno script non interattivo, in modo che lo stato dell'output possa essere verificato dallo script.

Ecco perché puoi semplicemente fare:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

O...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

Che scriverà una stringa seguita da una \newline su stdout o 3 e passerà comunque a uno stato di uscita diverso da zero quando 3 non è aperto perché la matematica fatta su $?finisce non riuscendo a convertire l'ottale 08 in % decimale ma non si tronca affatto l'ottale 00 .

O...

command exec >&3 || handle_it

Ma se stai usando ksh93, puoi semplicemente fare:

fds

Per un elenco di descrittori di file aperti. Aggiungi -lper vedere dove vanno.


3

I descrittori di file aperti sono disponibili in /proc/<pid>/fd. Per elencare, ad esempio, i descrittori di file aperti della shell corrente che puoi emettere ls -l /proc/$$/fdche dovrebbero darti qualcosa del tipo:

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

Quando apri un file usando:

touch /tmp/myfile
exec 7</tmp/myfile

Dovrebbe essere elencato da un nuovo ls -l /proc/$$/fd:

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

Se chiudi di nuovo il descrittore di file usando exec 7>&-anche questo non è più elencato /proc/$$/fd.


2
Tutto questo è abbastanza specifico per Linux. FWIW.
lcd047

1
Testato su Linux e su Solaris (10 e 11). La differenza è che devi usare pfiles <pid>per vedere quale descrittore di file è collegato a quale file mentre ls -lvisualizza la connessione su Linux.
Lambert,

Mi piace la compattezza di [ -e /proc/$$/fd/3 ], ma preferisco non fare affidamento su procfs, poiché è deprecato in FreeBSD e forse anche in altri * ces.
Witiko,

1
Mi porta all'alternativa dell'utilizzo pfiles <pid>o lsof -p <pid>per vedere quali descrittori di file sono aperti.
Lambert,

1
/procnon esiste affatto su OpenBSD. Su FreeBSD e NetBSD deve essere mountesplicitamente modificato e /proc/<PID>non avere una sottodirectory fd.
lcd047,

3

Il tuo trucco sembra carino; ma per un modo idiomatico mi chiedo perché non hai usato:

if ( exec 1>&3 ) 2>&-

Questo è, in effetti, un modo più pulito.
Witiko,

5
Ciò crea una subshell, sebbene la maggior parte delle shell significhi creare un processo. Ciò non garantisce che il fd sia scrivibile. Puoi usare { true >&3; } 2> /dev/nullper evitare la forchetta. O { command exec >&3; } 2> /dev/nullse vuoi reindirizzare stdout su di esso.
Stéphane Chazelas,

@Stephane; Il trucco della subshell inventato da @Witiko non influiva sui descrittori di file dell'ambiente corrente quando si utilizza un reindirizzamento per ottenere un reindirizzamento. - Potresti approfondire la "fd scrivibile" di cui parli?
Janis,

2
{ true >&3; } 2> /dev/nullnon avrà alcun effetto sull'ambiente corrente e non verrà fork (tranne che nella shell Bourne). Voglio dire che (exec 1>&3) 2>&-tornerà vero per un fd aperto in modalità di sola lettura.
Stéphane Chazelas,

1
execessere un builtin speciale uscirà dalla shell se fallisce (per bash, solo quando in modalità di conformità POSIX). command execlo impedisce. truenon è un builtin speciale. Si noti che exece command execinfluenzano l'ambiente corrente (è per questo che ho detto se si desidera reindirizzare stdout ad esso ).
Stéphane Chazelas,

-1

Se sei interessato a una soluzione a basso fork per usarla ripetutamente, suggerirei questa funzione:

checkfd () {
    exec 2> / dev / null
    se exec> & 3; poi
        exec 1> / dev / tty
        echo "fd3 OK"
    altro
        echo "fd3 KO"
    fi
    exec 2> / dev / tty
}

Ed ecco cosa produce con un zsh:

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 OK
$

Nella maggior parte delle shell exec >&3ucciderà la shell quando 3 non è aperto.
Mikeserv,

Almeno sta lavorando su zshe bash. Potresti fornire la shell su cui il fallimento ha execcausato un exit?
dan

Si. Nel bashfare set -o posixe riprovare. In zsh... penso che sia una questione di impostare env var POSIX_BUILTINSsu un valore non nullo - ma dimentico di mano. In ogni caso, zshnon è una shell che tenta la conformità POSIX, e quindi è definitivamente non standard. Entrambe queste shell evitano la compatibilità per ciò che alcuni credono sia convenienza.
Mikeserv,

Sta anche lavorando su una semplice shell Bourne.
dan

In bash, con set -o posixun tentativo ha successo.
dan

-1

Sembra semplicissimo (vedi commenti):

[ -r /proc/$$/fd/$FD ] && echo "File descriptor $FD is readable"
[ -w /proc/$$/fd/$FD ] && echo "File descriptor $FD is writable"

Come extra ... Il test [-r file] non indica se alcuni dati sono effettivamente in attesa di essere letti (/ dev / null supera questo test (vedi commenti)).

[ -r /proc/$$/fd/4 ] \
  && [ read -t 0.0001 -N 0 <&4 ] \
  && echo "Data is waiting to be read from file descriptor 4"

È richiesto un piccolo numero per l'argomento timeout (read -t) o si possono perdere i dati che richiedono un calcolo. È richiesto il test leggibile ([-r file]) o il comando di lettura bombarda se il file non è leggibile. Questo in realtà non leggerà alcun dato perché il conteggio dei byte è zero (leggi -N 0).


se hai intenzione di assumere un sistema Linux, puoi anche dare un'occhiata /proc/<pid>/fdinfo/<fd>, che elenca tutte le modalità di file aperti in flags:- vedi qui . Per quale motivo la tua seconda parte (anche dopo aver corretto l'errore eclatante): read -t .1 -N0 <&4non dirà se ci sono dati da leggere su fd 4: prova con 4</dev/null.
mosvy

E, naturalmente, [ -r /proc/$$/fd/$FD ]non ti dice se il descrittore di file $FDè leggibile, ma se il file da cui era aperto potrebbe essere nuovamente aperto , con un altro descrittore di file, per la lettura:exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

La domanda è piuttosto vecchia, ma comunque: perché non usare i builtin?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

Produzione:

0 is a valid FD
1 is a valid FD
2 is a valid FD
3 is INVALID FD
4 is INVALID FD
5 is INVALID FD

Quindi, per rispondere alla domanda, suggerirei:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

-tnon verifica se un descrittore di file è valido, ma se è collegato a un tty. Prepara un echo yup |tuo script e dirai che 0 is INVALID FD, mentre in realtà è molto valido, una pipe.
mosvy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.