linux - ¿3> & 1 implica 4> & 3 5> & 3 etc.?
bash shell (2)
Un error de
tee
?
La respuesta de John Kugelman está bien, pero como el problema es complejo, iré un poco más allá:
bash -c ''exec 2> >(exec sed -ue "s/^/StdErr: /");
exec 1> >(exec sed -ue "s/^/StdOut: /");
tee /dev/fd/{2..6} <<<foo''
StdErr: foo
StdOut: foo
StdErr: foo
StdErr: foo
StdErr: foo
StdErr: foo
Mostrando input (
foo
) multiplicado 5x en *
STDERR
*
Y
1x en
STDOUT
.
Entonces todos
/dev/fd/{3..6}
están vinculados a
/dev/fd/2
.
strace -o >(grep dev/fd) -e openat tee /dev/fd/{2..6} <<<foo 4>/dev/null
foo
foo
foo
foo
openat(AT_FDCWD, "/dev/fd/2", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
openat(AT_FDCWD, "/dev/fd/3", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5
openat(AT_FDCWD, "/dev/fd/4", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 6
openat(AT_FDCWD, "/dev/fd/5", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7
openat(AT_FDCWD, "/dev/fd/6", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 8
Entonces
tee
produce 4x de entrada en
tty
lugar de 5 (
2..6
+ STDOUT =
6
, - 1x / dev / null =>
5
atendidos
foo
):
-
Acceda a
/dev/fd/2
, abriendo el descriptor de archivo 3 ... y cree/dev/fd/3
, luego -
Acceda a
/dev/fd/3
, que existe ahora, desde la operación anterior, abriendo el descriptor de archivo 5 (porque/dev/fd/4
está enlazado a/dev/null
por la línea de comando ... y así crea/dev/fd/5
, luego -
Acceda
/dev/fd/4
(enlazado a/dev/null
por línea de comando) y cree/dev/fd/6
, luego -
Acceda
/dev/fd/5
, que existe ahora, (enlazado a 3, enlazado a 2 ...), luego -
Acceso
/dev/fd/6
, que existe ahora, pero enlazado a/dev/fd/4
que está enlazado a/dev/null
.
El resultado esperado podría ser:
tee /dev/fd/{2..6} <<<foo
foo
tee: /dev/fd/3: No such file or directory
tee: /dev/fd/4: No such file or directory
tee: /dev/fd/5: No such file or directory
tee: /dev/fd/6: No such file or directory
foo
Con salida 1 vez en STDERR y 1 vez en STDOUT, y 4 líneas de error.
Por lo tanto, no verifique la existencia de objetivos antes de comenzar. ¿Pero esto es un error?
Como abrir un archivo en
write mode
podría estar bien incluso si el archivo no existe (creando un nuevo archivo).
Lectura Tiempo de verificación Tiempo de uso (TOCTOU) Condición de carrera señalada por el comentario de John Kugelman, ayuda a entender por qué hacer una verificación previa es una mala idea .
Entonces, si hay un error, creo que
referir
/dev/fd/
directamente por
tee
es EL error aquí (en la línea de comando).
... No es un error de
tee
, solo un usuario con errores ;-)
Yo esperaría
echo foo | tee /proc/self/fd/{3..6} 3>&1
fallar con errores como / proc / self / fd / 4: No existe tal archivo o directorio , etc., pero para mi sorpresa, genera
foo
foo
foo
foo
foo
Es como
3>&1
hace que todos los descriptores siguientes se redirijan a stdout, excepto que no funciona si cambio
3
a otra cosa, como
$ echo foo | tee /proc/self/fd/{3..6} 4>&1
tee: /proc/self/fd/3: No such file or directory
tee: /proc/self/fd/5: No such file or directory
tee: /proc/self/fd/6: No such file or directory
foo
foo
$ echo foo | tee /proc/self/fd/{4..6} 4>&1
tee: /proc/self/fd/5: No such file or directory
tee: /proc/self/fd/6: No such file or directory
foo
foo
¿Hay alguna explicación para este comportamiento?
strace
muestra esta secuencia de llamadas al sistema:
$ strace -o strace.log tee /proc/self/fd/{3..6} 3>&1
...
$ cat strace.log
...
openat(AT_FDCWD, "/proc/self/fd/3", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
openat(AT_FDCWD, "/proc/self/fd/4", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5
openat(AT_FDCWD, "/proc/self/fd/5", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 6
openat(AT_FDCWD, "/proc/self/fd/6", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7
...
La primera línea abre
/proc/self/fd/3
y le asigna el siguiente número fd disponible, 4.
/proc/self/fd/3
es una ruta especial.
Abrirlo tiene un efecto similar a
dup
fd 3: fd 4 apunta al mismo lugar que fd 3, el tty.
Lo mismo sucede con cada
openat()
sucesiva de
openat()
.
Cuando el polvo se asienta fds 4, 5, 6 y 7 son todos duplicados de fd 3.
- 1 → tty
- 3 → tty
- 4 → tty
- 5 → tty
- 6 → tty
- 7 → tty
Tenga en cuenta que la redirección
3>&1
no es importante.
Lo importante es que estamos pidiendo tee para abrir
/proc/self/fd/N
donde
N
ya está en uso.
Deberíamos obtener el mismo resultado si nos deshacemos de
3>&1
y tenemos un tee start en
/proc/self/fd/2
.
Veamos:
$ echo foo | tee /proc/self/fd/{2..6}
foo
foo
foo
foo
foo
foo
¡Confirmado! Mismo resultado.
También podemos repetir el mismo número fd una y otra vez. Obtenemos el mismo resultado cuando presionamos fd 6. Para cuando llega al último, ha abierto suficientes descriptores para hacer posible el salto a 6.
$ echo foo | tee /proc/self/fd/{2,2,2,2,6}
foo
foo
foo
foo
foo
foo