¿Cuál es la diferencia entre "Redirección" y "Tubería"?

205

Esta pregunta puede sonar un poco estúpida, pero realmente no puedo ver la diferencia entre la redirección y las tuberías.

La redirección se utiliza para redirigir stdout / stdin / stderr, por ejemplo ls > log.txt.

Las tuberías se utilizan para dar la salida de un comando como entrada a otro comando, por ejemplo ls | grep file.txt.

Pero, ¿por qué hay dos operadores para la misma cosa?

¿Por qué no simplemente escribir ls > greppara pasar la salida, no es esto solo un tipo de redirección también? ¿Qué me estoy perdiendo?

John Threepwood
fuente

Respuestas:

224

Pipe se utiliza para pasar la salida a otro programa o utilidad .

Redirect se utiliza para pasar la salida a un archivo o secuencia .

Ejemplo: thing1 > thing2vsthing1 | thing2

thing1 > thing2

  1. Su shell ejecutará el programa llamado thing1
  2. Todo lo que thing1salga se colocará en un archivo llamado thing2. (Nota: si thing2existe, se sobrescribirá)

Si desea pasar la salida del programa thing1a un programa llamado thing2, puede hacer lo siguiente:

thing1 > temp_file && thing2 < temp_file

Cuál debería

  1. ejecutar programa llamado thing1
  2. guardar la salida en un archivo llamado temp_file
  3. ejecute el programa llamado thing2, pretendiendo que la persona en el teclado escribió el contenido temp_filecomo entrada.

Sin embargo, eso es torpe, por lo que hicieron tuberías como una forma más simple de hacerlo. thing1 | thing2hace lo mismo quething1 > temp_file && thing2 < temp_file

EDITAR para proporcionar más detalles a la pregunta en el comentario:

Si se >trata de ser "pasar al programa" y "escribir en el archivo", podría causar problemas en ambas direcciones.

Primer ejemplo: está intentando escribir en un archivo. Ya existe un archivo con ese nombre que desea sobrescribir. Sin embargo, el archivo es ejecutable. Presumiblemente, intentaría ejecutar este archivo, pasando la entrada. Tendría que hacer algo como escribir la salida en un nuevo nombre de archivo, luego cambiar el nombre del archivo.

Segundo ejemplo: como señaló Florian Diesch, ¿qué pasa si hay otro comando en otra parte del sistema con el mismo nombre (que está en la ruta de ejecución). Si pretendía hacer un archivo con ese nombre en su carpeta actual, estaría atascado.

En tercer lugar: si escribe incorrectamente un comando, no le advertirá que el comando no existe. En este momento, si escribe, ls | gerp log.txtse lo dirá bash: gerp: command not found. Si >significaba ambos, simplemente crearía un nuevo archivo para usted (luego advierta que no sabe qué hacer log.txt).

David Oneill
fuente
Gracias. Usted mencionó thing1 > temp_file && thing2 < temp_filehacer más fácil con las tuberías. Pero, ¿por qué no reutilizar el >operador para hacer esto, por ejemplo, thing1 > thing2para comandos thing1y thing2? ¿Por qué un operador extra |?
John Threepwood
1
"Tomar la salida y escribirla en un archivo" es una acción diferente a "Tomar la salida y pasarla a un programa diferente". Editaré más pensamientos en mi respuesta ...
David Oneill
1
@ JohnThreepwood Tienen diferentes significados. ¿Qué sucede si quisiera redirigir algo a un archivo llamado less, por ejemplo? thing | lessy thing > lessson perfectamente diferentes, ya que hacen cosas diferentes. Lo que propones crearía una ambigüedad.
Darkhogg
¿Es correcto decir que "thing1> temp_file" es simplemente azúcar sintáctico para "thing1 | tee temp_file"? Desde que descubrí el tee casi nunca uso redirecciones.
Sridhar Sarnobat
2
@ Sridhar-Sarnobat no, el teecomando hace algo diferente. teeescribe la salida tanto en la pantalla ( stdout) como en el archivo. Redirect solo hace el archivo.
David Oneill
22

Si el significado de foo > bardepende de si hay un comando llamado barque haría que el uso de la redirección sea mucho más difícil y más propenso a errores: cada vez que quiero redirigir a un archivo, primero tuve que verificar si hay un comando llamado como mi archivo de destino.

Florian Diesch
fuente
Esto sería un problema solo si está escribiendo baren un directorio que es parte de su $PATHvariable env. Si estás en algo como / bin, entonces ot podría ser un problema. Pero incluso entonces, bartendría que tener un conjunto de permisos ejecutable, de modo que el shell no solo busque un ejecutable, barsino que realmente pueda ejecutarlo. Y si la preocupación es sobrescribir el archivo existente, la nocloberopción de shell debería evitar sobrescribir los archivos existentes en las redirecciones.
Sergiy Kolodyazhnyy
13

Del Manual de administración del sistema Unix y Linux:

Redireccionamiento

El shell interpreta los símbolos <,> y >> como instrucciones para redirigir la entrada o salida de un comando hacia o desde un archivo .

Tubería

Para conectar el STDOUT de un comando al STDIN de otro, use | símbolo, comúnmente conocido como una tubería.

Entonces mi interpretación es: si es comando a comando, use una tubería. Si sale ao desde un archivo, use la redirección.

Señor lo que sea
fuente
12

Hay una diferencia vital entre los dos operadores:

  1. ls > log.txt -> Este comando envía la salida al archivo log.txt.

  2. ls | grep file.txt-> Este comando envía la salida del comando ls al grep mediante el uso de pipe ( |), y el comando grep busca file.txt en la entrada proporcionada por el comando anterior.

Si tuviera que realizar la misma tarea usando el primer escenario, entonces sería:

ls > log.txt; grep 'file.txt' log.txt

Entonces, una tubería (con |) se usa para enviar la salida a otro comando, mientras que la redirección (con >) se usa para redirigir la salida a algún archivo.

Ankit
fuente
3

Hay una gran diferencia sintáctica entre los dos:

  1. Una redirección es un argumento para un programa.
  2. Una tubería separa dos comandos

Se puede pensar en redirecciones como este: cat [<infile] [>outfile]. Esto implica que el orden no importa: cat <infile >outfilees lo mismo que cat >outfile <infile. Incluso puede mezclar redirecciones con otros argumentos: cat >outfile <infile -by cat <infile -b >outfileambos están perfectamente bien. También se puede encadenar más de una entrada o salida (entradas serán leídos secuencialmente y toda la salida se escribirá en cada archivo de salida): cat >outfile1 >outfile2 <infile1 <infile2. El objetivo o la fuente de una redirección puede ser un nombre de archivo o el nombre de una secuencia (como & 1, al menos en bash).

Pero las tuberías separan totalmente un comando de otro comando, no puede mezclarlos con argumentos:

[command1] | [command2]

La tubería toma todo lo escrito en la salida estándar del comando1 y lo envía a la entrada estándar del comando2.

También puede combinar tuberías y redireccionamiento. Por ejemplo:

cat <infile >outfile | cat <infile2 >outfile2

El primero catleerá líneas desde el archivo, luego simultáneamente escribirá cada línea para archivar y la enviará al segundo cat.

En el segundo cat, la entrada estándar primero lee desde la tubería (el contenido del archivo), luego lee desde infile2, escribiendo cada línea en outfile2. Después de ejecutar esto, outfile será una copia de infile, y outfile2 contendrá infile seguido de infile2.

Finalmente, en realidad haces algo muy similar a tu ejemplo usando la redirección "here string" (solo bash family) y backticks:

grep blah <<<`ls`

dará el mismo resultado que

ls | grep blah

Pero creo que la versión de redirección primero leerá toda la salida de ls en un búfer (en memoria), y luego alimentará ese búfer para agrupar una línea a la vez, mientras que la versión canalizada tomará cada línea de ls a medida que emerge, y pasa esa línea a grep.

usuario319857
fuente
1
Nitpick: el orden es importante en la redirección si redirige un fd a otro: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blahademás, la redirección a un archivo solo usará la última redirección. echo yes >/tmp/blah >/tmp/blah2solo escribirá a /tmp/blah2.
muru
2
Redirect no es realmente un argumento para el programa. El programa no sabrá ni le importará a dónde va su salida (o de dónde proviene la entrada). Es solo una forma de decirle a bash cómo organizar las cosas antes de ejecutar el programa.
Alois Mahdal
3

Nota: La respuesta refleja mi propia comprensión de estos mecanismos actualizados, acumulados durante la investigación y la lectura de las respuestas de los pares en este sitio y en unix.stackexchange.com , y se actualizará a medida que pase el tiempo. No dude en hacer preguntas o sugerir mejoras en los comentarios. También le sugiero que intente ver cómo funcionan las llamadas al sistema en shell con el stracecomando. Además, no se deje intimidar por la noción de elementos internos o syscalls: no tiene que saberlos ni poder usarlos para comprender cómo funciona Shell, pero definitivamente ayudan a comprender.

TL; DR

  • |las tuberías no están asociadas con una entrada en el disco, por lo tanto, no tienen un número de inodo del sistema de archivos de disco (pero sí tienen un inodo en el sistema de archivos virtual de pipefs en el espacio del kernel), pero las redirecciones a menudo involucran archivos, que tienen entradas de disco y, por lo tanto, tienen el correspondiente inodo
  • las tuberías no son lseek()capaces, por lo que los comandos no pueden leer algunos datos y luego retroceder, pero cuando se redirige >o <generalmente es un archivo lseek()capaz de objetos, los comandos pueden navegar como quieran.
  • las redirecciones son manipulaciones en los descriptores de archivos, que pueden ser muchas; las tuberías tienen solo dos descriptores de archivo: uno para el comando izquierdo y otro para el comando derecho
  • La redirección en las corrientes y tuberías estándar se almacenan en búfer.
  • las tuberías casi siempre implican bifurcación y, por lo tanto, intervienen pares de procesos; redirecciones: no siempre, aunque en ambos casos los subprocesos heredan los descriptores de archivo resultantes.
  • las tuberías siempre conectan descriptores de archivo (un par), redirecciones, ya sea que utilicen un nombre de ruta o descriptores de archivo.
  • las tuberías son un método de comunicación entre procesos, mientras que las redirecciones son solo manipulaciones en archivos abiertos u objetos similares
  • ambos emplean dup2()syscalls debajo del capó para proporcionar copias de descriptores de archivos, donde ocurre el flujo real de datos.
  • las redirecciones se pueden aplicar "globalmente" con el execcomando incorporado (vea esto y esto ), por lo que si lo hace, exec > output.txttodos los comandos escribirán a output.txtpartir de ese momento. |las tuberías se aplican solo para el comando actual (lo que significa un comando simple o como seq 5 | (head -n1; head -n2)comandos de subshell o compuestos.
  • Cuando se realiza la redirección en los archivos, cosas como echo "TEST" > filey echo "TEST" >> fileambos usan open()syscall en ese archivo ( ver también ) y obtienen un descriptor de archivo para pasarlo dup2(). Las tuberías |solo usan pipe()y dup2()syscall.

  • En lo que respecta a los comandos que se ejecutan, las canalizaciones y la redirección no son más que descriptores de archivos: objetos en forma de archivo, en los que pueden escribir a ciegas, o manipularlos internamente (lo que puede producir comportamientos inesperados; aptpor ejemplo, tiende a ni siquiera escribir en stdout si sabe que hay redirección).

Introducción

Para entender cómo difieren estos dos mecanismos, es necesario comprender sus propiedades esenciales, la historia detrás de los dos y sus raíces en el lenguaje de programación C. De hecho, saber qué son los descriptores de archivos, y cómo funcionan las llamadas al sistema dup2()y también pipe()es esencial lseek(). Shell pretende ser una forma de hacer que estos mecanismos sean abstractos para el usuario, pero cavar más profundo que la abstracción ayuda a comprender la verdadera naturaleza del comportamiento de shell.

Los orígenes de las redirecciones y tuberías

Según el artículo de Dennis Ritche, Petroglifos proféticos , las tuberías se originaron en un memorando interno de 1964 de Malcolm Douglas McIlroy , cuando trabajaban en el sistema operativo Multics . Citar:

Para poner mis preocupaciones más fuertes en pocas palabras:

  1. Deberíamos tener algunas formas de conectar programas como la manguera de jardín: atornille otro segmento cuando sea necesario para masajear los datos de otra manera. Este es el camino de IO también.

Lo que es evidente es que en ese momento los programas eran capaces de escribir en el disco, sin embargo, eso era ineficiente si la salida era grande. Para citar la explicación de Brian Kernighan en el video de Unix Pipeline :

Primero, no tiene que escribir un gran programa masivo: tiene programas más pequeños existentes que ya pueden hacer parte del trabajo ... Otro es que es posible que la cantidad de datos que está procesando no encajaría si lo almacenó en un archivo ... porque recuerde, estamos de vuelta en los días en que los discos de estas cosas tenían, si tuvo suerte, un Megabyte o dos de datos ... Por lo tanto, la tubería nunca tuvo que instanciar toda la salida .

Así, la diferencia conceptual es evidente: las tuberías son un mecanismo para hacer que los programas se comuniquen entre sí. Redirecciones: son una forma de escribir en el archivo a nivel básico. En ambos casos, Shell hace que estas dos cosas sean fáciles, pero debajo del capó, están sucediendo muchas cosas.

Profundizando: syscalls y funcionamiento interno del shell

Comenzamos con la noción de descriptor de archivo . Los descriptores de archivo describen básicamente un archivo abierto (ya sea un archivo en el disco, en la memoria o un archivo anónimo), que está representado por un número entero. Los dos flujos de datos estándar (stdin, stdout, stderr) son descriptores de archivo 0,1 y 2 respectivamente. De dónde vienen ? Bueno, en los comandos de shell los descriptores de archivo se heredan de su padre - shell. Y es cierto en general para todos los procesos: el proceso hijo hereda los descriptores de archivo de los padres. Para los demonios , es común cerrar todos los descriptores de archivos heredados y / o redirigir a otros lugares.

De vuelta a la redirección. Que es realmente Es un mecanismo que le dice al shell que prepare los descriptores de archivo para el comando (porque el redireccionamiento lo realiza el shell antes de que se ejecute el comando) y los señala donde el usuario sugirió. La definición estándar de redirección de salida es

[n]>word

Que [n]existe el número de descriptor de archivo. Cuando haces echo "Something" > /dev/nullel número 1 está implícito allí, y echo 2> /dev/null.

Debajo del capó, esto se hace duplicando el descriptor de archivo a través de la dup2()llamada al sistema. Vamos a tomar df > /dev/null. El shell creará un proceso secundario donde se dfejecuta, pero antes de eso se abrirá /dev/nullcomo descriptor de archivo # 3, y dup2(3,1)se emitirá, lo que hace una copia del descriptor de archivo 3 y la copia será 1. Usted sabe cómo tiene dos archivos file1.txty file2.txt, y cuando lo haga cp file1.txt file2.txt, tendrá dos mismos archivos, pero puede manipularlos de forma independiente. Eso es lo mismo que sucede aquí. A menudo, puede ver que antes de ejecutar, bashdeberá dup(1,10)hacer una copia del descriptor de archivo # 1 que es stdout(y esa copia será fd # 10) para restaurarla más tarde. Es importante tener en cuenta que cuando se consideran los comandos integrados(que son parte del shell en sí y no tienen ningún archivo en ningún /binotro lado) o comandos simples en un shell no interactivo , el shell no crea un proceso hijo.

Y luego tenemos cosas como [n]>&[m]y [n]&<[m]. Esto es duplicar los descriptores de archivos, que el mismo mecanismo que dup2()solo ahora tiene la sintaxis de shell, convenientemente disponible para el usuario.

Una de las cosas importantes a tener en cuenta sobre la redirección es que su orden no es fijo, pero es importante para la forma en que Shell interpreta lo que el usuario quiere. Compare lo siguiente:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

El uso práctico de estos en scripts de shell puede ser versátil:

y muchos otros.

Fontanería con pipe()ydup2()

Entonces, ¿cómo se crean las tuberías? A través de pipe()syscall , que tomará como entrada una matriz (también conocida como lista) llamada pipefdde dos elementos de tipo int(entero). Esos dos enteros son descriptores de archivo. El pipefd[0]será el fin de leer de la tubería y pipefd[1]será el final de escritura. Entonces df | grep 'foo', grepobtendrá una copia de pipefd[0]y dfobtendrá una copia de pipefd[1]. Pero cómo ? Por supuesto, con la magia de dup2()syscall. En dfnuestro ejemplo, digamos que pipefd[1]tiene el n. ° 4, por lo que el shell creará un hijo, hacer dup2(4,1)(¿recuerda mi cpejemplo?) Y luego hacer execve()para ejecutar realmente df. Naturalmente,dfheredará el descriptor de archivo n. ° 1, pero no se dará cuenta de que ya no está apuntando a la terminal, sino en realidad fd n. ° 4, que en realidad es el extremo de escritura de la tubería. Naturalmente, ocurrirá lo mismo grep 'foo'excepto con diferentes números de descriptores de archivo.

Ahora, una pregunta interesante: ¿podríamos hacer tuberías que redirijan fd # 2 también, no solo fd # 1? Sí, de hecho eso es lo que |&hace en bash. El estándar POSIX requiere un lenguaje de comandos de shell para admitir la df 2>&1 | grep 'foo'sintaxis para ese propósito, pero también lo bashhace |&.

Lo que es importante tener en cuenta es que las tuberías siempre tratan con descriptores de archivo. Existe FIFOo tubería con nombre , que tiene un nombre de archivo en el disco y te permite utilizarlo como un archivo, pero se comporta como un tubo. Pero los |tipos de tuberías son lo que se conoce como tubería anónima: no tienen nombre de archivo, porque en realidad son solo dos objetos conectados entre sí. El hecho de que no estamos tratando con archivos también tiene una implicación importante: las tuberías no son lseek()capaces. Los archivos, ya sea en la memoria o en el disco, son estáticos: los programas pueden usar lseek()syscall para saltar al byte 120, luego regresar al byte 10 y luego avanzar hasta el final. Las tuberías no son estáticas: son secuenciales y, por lo tanto, no puede rebobinar los datos que obtiene de ellas conlseek(). Esto es lo que hace que algunos programas se den cuenta si están leyendo desde un archivo o desde una tubería y, por lo tanto, pueden hacer los ajustes necesarios para un rendimiento eficiente; en otras palabras, a progpuede detectar si lo hago cat file.txt | progo no prog < input.txt. Ejemplo de trabajo real de eso es la cola .

Las otras dos propiedades muy interesantes de las tuberías es que tienen un búfer, que en Linux tiene 4096 bytes , ¡y en realidad tienen un sistema de archivos como se define en el código fuente de Linux ! No son simplemente un objeto para pasar datos, ¡son una estructura de datos ellos mismos! De hecho, debido a que existe un sistema de archivos pipefs, que gestiona tuberías y FIFO, las tuberías tienen un número de inodo en su sistema de archivos respectivo:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

En Linux, las canalizaciones son unidireccionales, al igual que la redirección. En algunas implementaciones tipo Unix, hay tuberías bidireccionales. Aunque con la magia de las secuencias de comandos de shell, también puede crear tuberías bidireccionales en Linux .

Ver también:

Sergiy Kolodyazhnyy
fuente
2

Para agregar a las otras respuestas, también hay diferencias semánticas sutiles, por ejemplo, las tuberías se cierran más fácilmente que las redirecciones:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

En el primer ejemplo, cuando headfinaliza la primera llamada , cierra la tubería y seqfinaliza, por lo que no hay entrada disponible para la segunda head.

En el segundo ejemplo, head consume la primera línea, pero cuando cierra su propia stdin tubería , el archivo permanece abierto para la próxima llamada.

El tercer ejemplo muestra que si usamos readpara evitar cerrar la tubería, todavía está disponible dentro del subproceso.

Entonces, el "flujo" es lo que atravesamos los datos (stdin, etc.), y es el mismo en ambos casos, pero la tubería conecta los flujos de dos procesos, donde una redirección conecta los flujos entre un proceso y un archivo, por lo que Puede ver la fuente de las similitudes y diferencias.

PD: Si tiene tanta curiosidad y / o sorpresa por esos ejemplos como yo, puede profundizar más trappara ver cómo se resuelven los procesos, por ejemplo:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

A veces, el primer proceso se cierra antes de 1imprimirse, a veces después.

También me pareció interesante usar exec <&-para cerrar la secuencia de la redirección para aproximar el comportamiento de la tubería (aunque con un error):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`
Julian de Bhal
fuente
"cuando finaliza la primera llamada a la cabeza, se cierra la tubería" Esto en realidad es inexacto por dos razones. Uno, (head -n1; head -n1) es un subshell con dos comandos, cada uno de los cuales hereda el extremo de lectura de la tubería como descriptor 0, y por lo tanto el subshell AND cada comando tiene abierto ese descriptor de archivo. Segunda razón, puede ver que con strace -f bash -c 'seq 5 | (cabeza -n1; cabeza -n1) '. Así que la primera cabeza cierra solo su copia del descriptor de archivo
Sergiy Kolodyazhnyy
El tercer ejemplo también es inexacto, porque readconsume solo la primera línea (eso es un byte para una 1nueva línea). seqenviado en total 10 bytes (5 números y 5 líneas nuevas). Entonces quedan 8 bytes en el buffer de tubería, y es por eso que el segundo headfunciona: todavía hay datos disponibles en el buffer de tubería. Por cierto, la cabeza sale sólo si hay 0 bytes leídos, un poco como enhead /dev/null
Sergiy Kolodyazhnyy
Gracias por la aclaración. ¿Estoy entendiendo correctamente que en seq 5 | (head -n1; head -n1)la primera llamada se vacía la tubería, por lo que todavía existe en un estado abierto pero sin datos para la segunda llamada head? Entonces, ¿la diferencia de comportamiento entre la tubería y la redirección se debe a que la cabeza extrae todos los datos de la tubería, pero solo las 2 líneas del controlador de archivo?
Julian de Bhal
Eso es correcto. Y es algo que se puede ver con el stracecomando que di en el primer comentario. Con la redirección, el archivo tmp está en el disco, lo que lo hace buscable (porque usan lseek()syscall; los comandos pueden saltar el archivo desde el primer byte hasta el último como quieran). el trabajo es leer todo primero, o si el archivo es grande, asignar parte de él a la RAM a través de una mmap()llamada. Una vez hice el mío tailen Python, y me encontré exactamente con el mismo problema.
Sergiy Kolodyazhnyy
También es importante recordar que el extremo de lectura de la tubería (descriptor de archivo) se da primero a la subshell (...), y la subshell hará una copia de su propio stdin en cada comando dentro (...). Por lo tanto, técnicamente se leen desde el mismo objeto. Primero head piensa que está leyendo de su propio stdin. Segundo headpiensa que tiene su propio stdin. Pero en realidad su fd # 1 (stdin) es solo una copia de la misma fd, que se lee al final de la tubería. Además, he publicado una respuesta, por lo que tal vez ayude a aclarar las cosas.
Sergiy Kolodyazhnyy
1

He encontrado un problema con esto en C hoy. Esencialmente, Pipe también tiene diferentes semánticas para redirigir, incluso cuando se envía a stdin. Realmente creo que dadas las diferencias, las tuberías deberían ir a otro lugar que no sea stdinasí, stdiny llamemos stdpipe(para hacer un diferencial arbitrario) se puede manejar de diferentes maneras.

Considera esto. Cuando se canaliza la salida de un programa a otro, fstatparece que devuelve cero como a st_sizepesar de ls -lha /proc/{PID}/fdmostrar que hay un archivo. Al redirigir un archivo, este no es el caso (al menos en debian wheezy, stretchy jessievanilla y ubuntu 14.04, 16.04vanilla.

Si cat /proc/{PID}/fd/0tiene una redirección, podrá repetir la lectura tantas veces como desee. Si hace esto con una tubería, notará que la segunda vez que ejecuta la tarea consecutivamente, no obtiene el mismo resultado.

MrMesees
fuente