Tengo un cierto script bash, que quiere preservar la /dev/stdout
ubicación original antes de reemplazar el descriptor del primer archivo con otra ubicación.
Entonces, naturalmente, escribí algo como
old_stdout=$(readlink -f /dev/stdout)
Y no funcionó. Muy rápidamente entiendo cuál era el problema:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Obviamente, se $()
ejecuta en un subshell, que se canaliza al shell principal.
Entonces, la pregunta es: ¿hay una manera confiable (con un alcance de portabilidad entre distribuciones de Linux) para guardar la /dev/stdout
ubicación como una cadena en un script bash?
bash
io-redirection
io
alexey.e.egorov
fuente
fuente
/dev/stdout
resolvería el problema con la impresión de mensajes en modo silencioso. La alternativa es redirigir cualquier otra acción que produzca resultados, y hay bastantes. Aproximadamente 100 veces más que los mensajes de interacción del usuario.stderr
. Esto es, por ejemplo, por qué los avisos iránstderr
por defecto.stderr
también debe ser redirigido y guardado, ya que el script llama a una serie de programas externos, y todos los posibles mensajes de error deben ser recopilados y registrados.Respuestas:
Para guardar un descriptor de archivo, lo duplica en otro fd. Guardar una ruta al archivo correspondiente no es suficiente, necesitará guardar el modo de apertura, los indicadores de apertura, la posición actual dentro del archivo, etc. Y, por supuesto, para tuberías o tomas anónimas, eso no funcionaría, ya que no tienen camino. Lo que desea guardar es la descripción del archivo abierto al que se refiere el fd, y duplicar un fd en realidad está devolviendo un nuevo fd a la misma descripción del archivo abierto .
Para duplicar un descriptor de archivo en otro, con un shell tipo Bourne, la sintaxis es:
Arriba, fd 1 está duplicado en fd 3.
Cualquier cosa que fd 3 ya estuviera abierta antes estaría cerrada, pero tenga en cuenta que los fds 3 a 9 (generalmente más, hasta 99 con
yash
) están reservados para ese propósito (y no tienen un significado especial contrario a 0, 1 o 2), el Shell sabe que no debe usarlos para su propio negocio interno. La única razón por la que fd 3 habría estado abierto de antemano es porque lo hizo en el script 1 , o la persona que llamó lo filtró.Luego, puede cambiar stdout a otra cosa:
Y más tarde, para restaurar stdout:
(
3>&-
para cerrar el descriptor de archivo que ya no necesitamos).Ahora, el problema con eso es que, excepto en ksh, cada comando que ejecute después
exec 3>&1
heredará ese fd 3. Esa es una fuga de fd. Generalmente no es un gran problema, pero eso puede causar problemas.ksh
establece el indicador close-on-exec en esos fds (para fds mayores de 2), pero no otros shells y otros shells no tienen forma de establecer ese indicador manualmente.La solución para otro shell es cerrar el fd 3 para cada comando, como:
Incómodo. Aquí, la mejor manera sería no usar
exec
nada, sino redirigir los grupos de comandos:Allí, es el shell el que se encarga de guardar stdout y restaurarlo después (y lo hace internamente duplicándolo en un fd (por encima de 9, por encima de 99 para
yash
) con el conjunto de indicadores close-on-exec ).Nota 1
Ahora, la administración de esos fds 3 a 9 puede ser engorrosa y problemática si los usa extensamente o en funciones, especialmente si su script usa algún código de terceros que a su vez puede usar esos fds.
Algunas conchas (
zsh
,bash
,ksh93
, todo ello sumado la característica ( sugerido por Oliver Kiddle dezsh
) alrededor del mismo tiempo en 2005 después de haber sido discutido entre sus desarrolladores) tiene una sintaxis alternativa para asignar la primera fd libre por encima de 10 en vez que ayuda en este caso:fuente
rc.local
servicio, por ejemplo, realmente debería haber usado algo comoexec {FD}>&1
o algo. Pero esto solo se admite en bash 4, lo cual es realmente triste. Entonces esto no es realmente portátil.eval "exec $i>&1"
es algo que me gustaría evitar, debido a lo engorroso. ¿ Realmente puedo confiar en que fds por encima de 9 sería gratis entonces?bash
te permitirá dispararte en el pie.Como puede ver, las secuencias de comandos bash no son como un lenguaje de programación normal donde puede asignar descriptores de archivo.
La solución más simple es usar un subconjunto para ejecutar lo que desea redirigir, de modo que el procesamiento pueda revertirse al caparazón superior que tiene su E / S estándar intacta.
Una solución alternativa sería utilizar
tty
para identificar el dispositivo TTY y controlar la E / S en su secuencia de comandos. Por ejemplo:y luego puedes ...
fuente
$$
obtendría el PID del proceso actual, en caso de shell interactivo o script el PID de shell relevante.Entonces puedes usar:
Ejemplo:
fuente
/proc
estructura específica causa problemas de portabilidad, al igual que el uso/dev/stdout
como se menciona en la pregunta./proc
estructura específica ? Que funcionaría en cualquier Linux que tieneprocfs
..procfs
casi siempre está presente, pero a menudo vemos preguntas de portabilidad y una buena metodología de desarrollo incluye considerar la portabilidad a otros sistemas.bash
puede ejecutarse en una multitud de sistemas operativos.