bash - sustituir - ¿Cómo funcionan los descriptores de archivos?
sed ubuntu (3)
¡Está fallando porque esos descriptores de archivos no apuntan a nada! Los descriptores de archivo predeterminados normales son la entrada estándar 0
, la salida estándar 1
y la secuencia de error estándar 2
. Como su script 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 tu 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, el resultado extra se envió a los archivos solicitados.
¿Puede alguien decirme por qué esto no funciona? Estoy jugando con las descripciones de los archivos, pero me siento un poco perdido.
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
Las tres primeras líneas funcionan bien, pero los dos últimos errores salieron. ¿Por qué?
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 cambiar el script para que funcione .
Lo que me gustaría señalar es que no es necesario cambiar el script :
#!/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 archivos 3 y 4 a 1 (que es salida estándar).
El punto es que el script está perfectamente bien para 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.
Lo que es más interesante es que su programa no tiene que tener permisos de escritura para esos archivos, porque en realidad no los abre.
Por ejemplo, cuando ejecuto sudo -s
para cambiar de usuario a raíz, creo un directorio como raíz y trato de ejecutar el siguiente comando como mi usuario habitual (rsp en mi caso) de esta manera:
# 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
(observe 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, aunque el script no tenía permisos para crear esos archivos.
Otro ejemplo sería usar chroot jail o un contenedor y ejecutar un programa dentro del cual no tendría acceso a esos archivos, incluso si se ejecutó como root y aún así redirigir esos descriptores externamente donde lo necesita, sin dar realmente acceso al archivo completo sistema o cualquier otra cosa a este script.
El punto es que has descubierto un mecanismo muy interesante y útil . No tiene que abrir todos los archivos dentro de su script como se sugirió en otras respuestas. Algunas veces es útil redirigirlos durante la invocación del script.
Para resumir , esto:
echo "This"
es en realidad 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 >&-
El resultado será:
./fdtest2: line 2: echo: write error: Bad file descriptor
Agregar >&-
(que es lo mismo que 1>&-
) significa cerrar la salida estándar. Agregar 2>&-
significaría cerrar el stderr.
Incluso puedes hacer algo más complicado . Tu script 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 ejecutando:
./fdtest 3>&1 4>&1 1>&-
Emite:
./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 vio 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.
Tu guión es, de hecho, un ejemplo muy interesante, y yo sostengo que no está roto en absoluto, ¡solo lo estabas usando mal! :)
Los descriptores de archivo 0, 1 y 2 son para stdin, stdout y stderr, respectivamente.
Los descriptores de archivos 3, 4, .. 9 son para archivos adicionales. Para usarlos, primero debes 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 Bash-Scripting Guide: Capítulo 20. Redirección de E / S.