Probar si un descriptor de archivo es válido

12

Me gustaría hacer que un script bash genere información adicional para los descriptores de archivo (FD) mayores o iguales a 3, cuando están abiertos. Para probar si un FD está abierto, ideé el siguiente truco:

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

Esto es suficiente para mis necesidades, pero tengo curiosidad por saber si hay una forma más idiomática de probar si un FD es válido. Estoy especialmente interesado en saber si existe un mapeo de fcntl(1)syscall a un comando de shell, lo que permitiría la recuperación de banderas FD ( O_WRONLYy O_RDWRpara probar si el FD es editable, O_RDONLYy O_RDWRpara probar si el FD es legible).

Witiko
fuente

Respuestas:

12

En ksh(ambas variantes AT&T y pdksh) o zsh, puedes hacer:

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

No escribirán nada en ese fd, pero aún así verificarán si el fd se puede escribir (usando fcntl(3, F_GETFL)) e informarán un error de lo contrario:

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

(que puede redirigir a /dev/null).

Con bash, creo que su única opción es verificar si dup()tiene éxito como en su enfoque, aunque eso no garantizará que el fd sea grabable (o llame a una utilidad externa ( zsh/ perl...) para hacer el fcntl()).

Tenga en cuenta que en bash(como la mayoría de los shells), si usa en (...)lugar de {...;}, eso bifurcará un proceso adicional. Puedes usar:

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

en su lugar, para evitar la bifurcación (excepto en el shell Bourne donde la redirección de comandos compuestos siempre causa un subshell). No lo use en :lugar de hacerlo, trueya que es un componente incorporado especial , por lo que el shell se cerrará cuando bash esté en modo de cumplimiento POSIX.

Sin embargo, podría acortarlo a:

if { >&3; } 2<> /dev/null
Stéphane Chazelas
fuente
@mikeserve, re: tu edición, ¿qué es eso <>? El shell no va a leer desde su stderr, ¿por qué querrías abrirlo en lectura + escritura? ¿Qué quieres decir con lo que pasó con intrínseco? ?
Stéphane Chazelas
7

En la descripción del uso de la aplicación POSIX encontrará lo siguiente:command

Hay algunas ventajas en suprimir las características especiales de las incorporaciones especiales en ocasiones. Por ejemplo:

command exec > unwritable-file

no hace que un script no interactivo se anule, de modo que el script pueda verificar el estado de salida.

Es por eso que solo puedes hacer:

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

Que escribirá una cadena seguida de una línea \nelectrónica ya sea stdout o 3 y aún así pasará un estado de salida distinto de cero cuando 3 no esté abierto porque la matemática realizada al $?terminar no logra convertir el octal 08 a % decimal pero se trunca a nada el octal 00 .

O...

command exec >&3 || handle_it

Pero si está usando ksh93, puede hacer lo siguiente:

fds

Para obtener una lista de descriptores de archivos abiertos. Agregue -lpara ver a dónde van.

mikeserv
fuente
3

Los descriptores de archivos abiertos se pueden encontrar en /proc/<pid>/fd. Para enumerar, por ejemplo, los descriptores de archivos abiertos del shell actual que puede emitir, ls -l /proc/$$/fdque deberían proporcionarle algo como:

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

Cuando abres un archivo usando:

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

Debería estar listado por un nuevo ls -l /proc/$$/fd:

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

Si vuelve a cerrar el descriptor de archivo con el uso exec 7>&-, tampoco aparecerá en la lista /proc/$$/fd.

Lambert
fuente
2
Todo esto es bastante específico para Linux. FWIW
lcd047
1
Probado en Linux y en Solaris (10 y 11). La diferencia es que debe usar pfiles <pid>para ver qué descriptor de archivo está conectado a qué archivo mientras ls -lmuestra la conexión en Linux.
Lambert
Me gusta la compacidad de [ -e /proc/$$/fd/3 ], pero prefiero no confiar en procfs, ya que está en desuso en FreeBSD y posiblemente también en otras unidades *.
Witiko
1
Me lleva a la alternativa de usar pfiles <pid>o lsof -p <pid>ver qué descriptores de archivo están abiertos.
Lambert
1
/procno existe en absoluto en OpenBSD. En FreeBSD y NetBSD tiene que ser mounteditado explícitamente y /proc/<PID>no tiene un subdirectorio fd.
lcd047
3

Tu truco se ve lindo; pero por una forma idiomática me pregunto por qué no usaste:

if ( exec 1>&3 ) 2>&-
Janis
fuente
Esta es, de hecho, una forma más limpia.
Witiko
55
Sin embargo, eso crea un subshell, que es la mayoría de los shells, significa bifurcar un proceso. Eso no garantiza que el fd sea grabable. Puedes usar { true >&3; } 2> /dev/nullpara evitar el tenedor. O { command exec >&3; } 2> /dev/nullsi desea redirigir stdout a él.
Stéphane Chazelas 01 de
@Stephane; El truco del subshell que inventó @Witiko era no afectar los descriptores de archivo del entorno actual cuando se usa una redirección para obtener una redirección. - ¿Podrías dar más detalles sobre el "fd grabable" que mencionas?
Janis
2
{ true >&3; } 2> /dev/nulltampoco afectará el entorno actual y no se bifurcará (excepto en el shell Bourne). Quiero decir que (exec 1>&3) 2>&-volverá verdadero para un fd abierto en modo de solo lectura.
Stéphane Chazelas 01 de
1
execAl ser un generador especial, saldrá del shell si falla (para bash, solo cuando está en modo de cumplimiento POSIX). command execevita eso. trueNo es una construcción especial. Tenga en cuenta que execy command execafecta el entorno actual (es por eso que dije si desea redirigir stdout a él ).
Stéphane Chazelas 01 de
-1

Si está interesado en una solución de baja bifurcación para usarla repetidamente, le sugiero esta función:

checkfd () {
    exec 2> / dev / null
    si exec> & 3; entonces
        exec 1> / dev / tty
        echo "fd3 OK"
    más
        echo "fd3 KO"
    fi
    exec 2> / dev / tty
}

Y esto es lo que produce con un zsh:

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 OK
PS
dan
fuente
En la mayoría de los proyectiles exec >&3matará al proyectil cuando 3 no esté abierto.
mikeserv 01 de
Al menos está trabajando en zshy bash. ¿Podría proporcionar el caparazón en el que execcausó la falla exit?
dan
Si. En bashhacer set -o posixe intentar de nuevo. En zsh... creo que es una cuestión de configurar la var var POSIX_BUILTINSa un valor no nulo, pero lo olvido de improviso. En cualquier caso, zshno es un shell que intenta cumplir con POSIX, por lo que definitivamente no es estándar. Ambas cáscaras evitan la compatibilidad para lo que algunos creen que es conveniencia.
mikeserv 01 de
También está trabajando en el shell Bourne simple.
dan
En bash, con set -o posixun intento es exitoso.
dan
-1

Esto parece súper fácil (ver comentarios):

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

Como extra ... La prueba [-r file] no indica si algún dato está esperando ser leído (/ dev / null pasa esta prueba (ver comentarios)).

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

Se requiere algún número pequeño para el argumento de tiempo de espera (read -t) o se pueden perder los datos que necesitan algún cálculo. Se requiere la prueba legible ([-r archivo]) o el comando de lectura bombardeará si el archivo no es legible. En realidad, esto no leerá ningún dato porque el recuento de bytes es cero (leer -N 0).

Pablo
fuente
si va a asumir un sistema Linux, es mejor que eche un vistazo /proc/<pid>/fdinfo/<fd>, que enumera todos los modos de archivos abiertos en flags:- vea aquí . Por qué su segunda parte (incluso después de corregir el error evidente): read -t .1 -N0 <&4no dirá si hay datos para leer en fd 4: solo intente 4</dev/null.
mosvy
Y, por supuesto, [ -r /proc/$$/fd/$FD ]no le dice si el descriptor de archivo $FDes legible, pero si el archivo desde el que estaba abierto podría abrirse nuevamente , con otro descriptor de archivo, para leer:exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy
-1

La pregunta es bastante antigua, pero de todos modos, ¿por qué simplemente no usar builtins?

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

Salida:

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

Entonces, para responder la pregunta, sugeriría:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi
Dimas
fuente
-tno prueba si un descriptor de archivo es válido, pero si está conectado a un tty. Anteponga un echo yup |a su secuencia de comandos, y dirá que 0 is INVALID FD, aunque en realidad es muy válido fd, una tubería.
mosvy