linux bash shell io-redirection proc-filesystem

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 ):

  1. Acceda a /dev/fd/2 , abriendo el descriptor de archivo 3 ... y cree /dev/fd/3 , luego
  2. 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
  3. Acceda /dev/fd/4 (enlazado a /dev/null por línea de comando) y cree /dev/fd/6 , luego
  4. Acceda /dev/fd/5 , que existe ahora, (enlazado a 3, enlazado a 2 ...), luego
  5. 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