Saltar al contenido

¿Cómo funcionan los descriptores de archivos?

Te recomendamos que pruebes esta resolución en un ambiente controlado antes de pasarlo a producción, saludos.

Solución:

Los descriptores de archivo 0, 1 y 2 son para stdin, stdout y stderr respectivamente.

Los descriptores de archivo 3, 4, .. 9 son para archivos adicionales. Para usarlos, primero debe abrirlos. Por ejemplo:

exec 3<> /tmp/foo  #open fd 3.
echo "test" >&3
exec 3>&- #close fd 3.

Para obtener más información, consulte la Guía avanzada de secuencias de comandos de Bash: Capítulo 20. Redirección de E/S.

es una vieja pregunta pero una cosa necesita aclaración.

Si bien las respuestas de Carl Norum y dogbane son correctas, la suposición es cambia tu script para que funcione.

Lo que me gustaría señalar es que no necesitas cambiar el guion:

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

Funciona si lo invocas de manera diferente:

./fdtest 3>&1 4>&1

lo que significa redirigir los descriptores de archivo 3 y 4 a 1 (que es la salida estándar).

El caso es que el guión está perfectamente bien al querer escribir en descriptores que no sean solo 1 y 2 (stdout y stderr) si esos descriptores son proporcionados por el proceso padre.

Su ejemplo es bastante interesante porque este script puede escribir en 4 archivos diferentes:

./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

Ahora tiene la salida en 4 archivos separados:

$ for f in file*; do echo $f:; cat $f; done
file1.txt:
This
file2.txt:
is
file3.txt:
a
file4.txt:
test.

Que es más interesante al respecto es que su programa no tiene que tener permisos de escritura para esos archivos, porque en realidad no los abre.

Por ejemplo, cuando corro sudo -s para cambiar el usuario a root, cree un directorio como root e intente ejecutar el siguiente comando como mi usuario habitual (rsp en mi caso) así:

# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt'

me sale un error:

bash: file1.txt: Permission denied

Pero si hago la redirección fuera de su:

# su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

(note la diferencia entre comillas simples) funciona y obtengo:

# ls -alp
total 56
drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./
drwxrwxr-x 3 rsp  rsp  4096 Jun 23 15:01 ../
-rw-r--r-- 1 root root    5 Jun 23 15:05 file1.txt
-rw-r--r-- 1 root root   39 Jun 23 15:05 file2.txt
-rw-r--r-- 1 root root    2 Jun 23 15:05 file3.txt
-rw-r--r-- 1 root root    6 Jun 23 15:05 file4.txt

que son 4 archivos propiedad de root en un directorio propiedad de root – a pesar de que el script no tenía permisos para crear esos archivos.

Otro ejemplo sería usar chroot jail o un contenedor y ejecutar un programa en el interior donde no tendría acceso a esos archivos incluso si se ejecutara como root y aún redirigir esos descriptores externamente donde los necesite, sin realmente dar acceso a todo el archivo sistema o cualquier otra cosa a este script.

El caso es que has descubierto un mecanismo muy interesante y útil. No tiene que abrir todos los archivos dentro de su secuencia de comandos como se sugirió en otras respuestas. A veces es útil redirigirlos durante la invocación del script.

En resumeneste:

echo "This"

en realidad es equivalente a:

echo "This" >&1

y ejecutando el programa como:

./program >file.txt

es lo mismo que:

./program 1>file.txt

El número 1 es solo un número predeterminado y es stdout.

Pero incluso este programa:

#!/bin/bash
echo "This"

puede producir un error de “descriptor incorrecto”. ¿Cómo? Cuando se ejecuta como:

./fdtest2 >&-

La salida será:

./fdtest2: line 2: echo: write error: Bad file descriptor

agregando >&- (que es lo mismo que 1>&-) significa cerrar la salida estándar. agregando 2>&- significaría cerrar el stderr.

Incluso puedes hacer una cosa mas complicada. Su guión original:

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

cuando se ejecuta con solo:

./fdtest

huellas dactilares:

This
is
./fdtest: line 4: 3: Bad file descriptor
./fdtest: line 5: 4: Bad file descriptor

Pero puede hacer que los descriptores 3 y 4 funcionen, pero el número 1 falla al ejecutar:

./fdtest 3>&1 4>&1 1>&-

Produce:

./fdtest: line 2: echo: write error: Bad file descriptor
is
a
test.

Si desea que los descriptores 1 y 2 fallen, ejecútelo así:

./fdtest 3>&1 4>&1 1>&- 2>&-

Usted obtiene:

a
test.

¿Por qué? ¿No falló nada? Lo hizo pero sin stderr (descriptor de archivo número 2) ¡No viste los mensajes de error!

Creo que es muy útil experimentar de esta manera para tener una idea de cómo funcionan los descriptores y su redirección.

Su guión es un ejemplo muy interesante, y yo sostengo que no está roto en absoluto, ¡solo lo estabas usando mal! 🙂

¡Está fallando porque esos descriptores de archivo no apuntan a nada! Los descriptores de archivo predeterminados normales son la entrada estándar 0la salida estándar 1y el flujo de error estándar 2. Dado que su secuencia de comandos no abre ningún otro archivo, no hay otros descriptores de archivo válidos. Puede abrir un archivo en bash usando exec. Aquí hay una modificación de su ejemplo:

#!/bin/bash
exec 3> out1     # open file 'out1' for writing, assign to fd 3
exec 4> out2     # open file 'out2' for writing, assign to fd 4

echo "This"      # output to fd 1 (stdout)
echo "is" >&2    # output to fd 2 (stderr)
echo "a" >&3     # output to fd 3
echo "test." >&4 # output to fd 4

Y ahora lo ejecutaremos:

$ ls
script
$ ./script 
This
is
$ ls
out1    out2    script
$ cat out*
a
test.
$

Como puede ver, la salida adicional se envió a los archivos solicitados.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *