3>&4-
es una extensión ksh93 que también es compatible con bash y que es la abreviatura de 3>&4 4>&-
3 ahora apunta a donde solía 4 y 4 ahora está cerrado, por lo que lo que señaló 4 ahora se ha movido a 3.
El uso típico sería en casos donde ha duplicado stdin
o stdout
para guardar una copia y desea restaurarlo, como en:
Suponga que desea capturar el stderr de un comando (y solo stderr) mientras deja stdout solo en una variable.
Sustitución de comandos var=$(cmd)
, crea una tubería. El final de escritura de la tubería se convierte cmd
en stdout (descriptor de archivo 1) y el otro extremo es leído por el shell para completar la variable.
Ahora, si quieres stderr
ir a la variable, se podría hacer: var=$(cmd 2>&1)
. Ahora tanto fd 1 (stdout) como 2 (stderr) van a la tubería (y eventualmente a la variable), que es solo la mitad de lo que queremos.
Si lo hacemos var=$(cmd 2>&1-)
(abreviatura de var=$(cmd 2>&1 >&-
), ahora solo cmd
stderr va a la tubería, pero fd 1 está cerrado. Si cmd
intenta escribir alguna salida, eso devolvería un EBADF
error, si abre un archivo, obtendrá el primer fd libre y se le asignará el archivo abierto a stdout
menos que el comando lo proteja. No es lo que queremos tampoco.
Si queremos que el stdout de cmd
se quede solo, es decir, apuntar al mismo recurso que apuntó fuera de la sustitución del comando, entonces necesitamos de alguna manera llevar ese recurso dentro de la sustitución del comando. Para eso podemos hacer una copia de stdout
fuera de la sustitución del comando para llevarlo dentro.
{
var=$(cmd)
} 3>&1
Cuál es una forma más limpia de escribir:
exec 3>&1
var=$(cmd)
exec 3>&-
(que también tiene el beneficio de restaurar fd 3 en lugar de cerrarlo al final).
Luego, sobre {
(o el exec 3>&1
) y hasta el }
, tanto fd 1 como 3 apuntan al mismo recurso al que fd 1 apuntó inicialmente. fd 3 también apuntará a ese recurso dentro de la sustitución del comando (la sustitución del comando solo redirige el fd 1, stdout). Así que arriba, para cmd
, tenemos para fds 1, 2, 3:
- la pipa a var
- intacto
- igual a lo que apunta 1 fuera de la sustitución del comando
Si lo cambiamos a:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Entonces se convierte en:
- igual a lo que apunta 1 fuera de la sustitución del comando
- la pipa a var
- igual a lo que apunta 1 fuera de la sustitución del comando
Ahora, tenemos lo que queríamos: stderr va a la tubería y stdout se deja intacto. Sin embargo, estamos filtrando ese fd 3 a cmd
.
Mientras que los comandos (por convención) asumen que los fds 0 a 2 están abiertos y son entrada, salida y error estándar, no asumen nada de otros fds. Lo más probable es que dejen ese fd 3 intacto. Si necesitan otro descriptor de archivo, simplemente harán uno open()/dup()/socket()...
que devolverá el primer descriptor de archivo disponible. Si (como un script de shell que lo hace exec 3>&1
) necesitan usarlo fd
específicamente, primero lo asignarán a algo (y en ese proceso, el recurso en poder de nuestro fd 3 será liberado por ese proceso).
Es una buena práctica cerrar ese fd 3 ya cmd
que no lo utiliza, pero no es gran cosa si lo dejamos asignado antes de llamar cmd
. Los problemas pueden ser: eso cmd
(y potencialmente otros procesos que genera) tiene un fd menos disponible. Un problema potencialmente más serio es si el recurso al que apunta fd puede terminar retenido por un proceso generado por eso cmd
en segundo plano. Puede ser una preocupación si ese recurso es una tubería u otro canal de comunicación entre procesos (como cuando su script se ejecuta como script_output=$(your-script)
), ya que eso significará que la lectura del proceso desde el otro extremo nunca verá el final del archivo hasta que el proceso en segundo plano finaliza.
Entonces, aquí, es mejor escribir:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Que, con bash
se puede acortar a:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Para resumir las razones por las que rara vez se usa:
- no es estándar y solo azúcar sintáctico. Debe equilibrar el ahorro de unas pocas pulsaciones de teclas para que su script sea menos portátil y menos obvio para las personas que no están acostumbradas a esa característica poco común.
- La necesidad de cerrar el fd original después de duplicarlo a menudo se pasa por alto porque la mayoría de las veces, no sufrimos las consecuencias, por lo que simplemente lo hacemos en
>&3
lugar de >&3-
o >&3 3>&-
.
La prueba de que rara vez se usa, como descubrió es que es falso en bash . En bash compound-command 3>&4-
o any-builtin 3>&4-
deja fd 4 cerrado incluso después compound-command
o any-builtin
ha regresado. Ya está disponible un parche para solucionar el problema (2013-02-19).
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
¿No es un error tipográfico en el cierre 1?$(...)
).{...}
, fd 3 apunta a lo que fd 1 solía señalar y fd 1 está cerrado, luego al ingresar$(...)
, fd 1 se establece en la tubería que se alimenta$var
, luego tambiéncmd
2 a eso, y luego 1 a qué 3 puntos a, ese es el 1. externo. El hecho de que 1 permanezca cerrado después es un error en bash, lo reportaré. ksh93 de donde proviene esa característica no tiene ese error.Significa hacer que apunte al mismo lugar que el otro descriptor de archivo. Es necesario hacer esto muy rara vez, aparte de la obvia manipulación separada del descriptor de errores estándar (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Puede ser útil en algunos casos complejos.La guía Advanced Bash Scripting tiene este ejemplo de nivel de registro más largo y este fragmento:
En Source Mage's Sorcery, por ejemplo, lo usamos para discernir diferentes resultados del mismo bloque de código:
Tiene una sustitución de proceso adicional añadida por razones de registro (VOYEUR decide si los datos deben mostrarse en la pantalla o simplemente registrar), pero algunos mensajes deben presentarse siempre . Para lograr eso, los imprimimos en el descriptor de archivo 3 y luego lo manejamos especialmente.
fuente
En Unix, los archivos se manejan mediante descriptores de archivo (enteros pequeños, por ejemplo, la entrada estándar es 0, la salida estándar es 1, el error estándar es 2; a medida que abre otros archivos, normalmente se les asigna el descriptor no utilizado más pequeño). Por lo tanto, si conoce las entradas del programa y desea enviar la salida que va al descriptor de archivo 5 a la salida estándar, movería el descriptor 5 a 1. De ahí es de donde
2> errors
proviene, y construcciones como2>&1
duplicar errores en El flujo de salida.Por lo tanto, casi nunca lo uso (recuerdo vagamente haberlo usado una o dos veces con ira en mis más de 25 años de uso casi exclusivo de Unix), pero cuando lo necesito es absolutamente esencial.
fuente
5>&1
envía 5 a donde va 1, ¿qué hace1>&5-
, además de cerrar 5?