Tengo estos tres ejemplos de redirección stdin/stdout, solo uno de ellos funciona como debe. Me encantaría que alguien pudiera explicarme eso.
El objetivo es ordenar el contenido del archivo1 y guardar los cambios en el mismo archivo.
ordenar archivo1 | tee file1 > /dev/null --------> Funciona
ordenar archivo1 | tee file1 --------> Se borrará el contenido del file1
ordenar archivo1 | tee file1 > file2 --------> Se borrará el contenido del file1
PD. tee copia la entrada estándar a cada ARCHIVO y también a la salida estándar.
¿Qué hace que el primer ejemplo funcione?
Respuesta1
De acuerdo con mis pruebas en Debian Wheezy, los 3 escenarios pueden llevar a ambos resultados (el archivo1 se ordena y se vuelve a escribir en sí mismo O no se ordena nada y no se escribe nada en el archivo1.
Creo que este es un comportamiento normal y se debe a la forma en que Linux trabaja con los archivos. Piense en el comando: el comando de clasificación comienza a leer el archivo1 e inmediatamente envía su salida al tee. Tee lee el resultado, lo vuelve a escribir en el archivo 1 y lo imprime en /dev/null. En caso de que la clasificación sea lo suficientemente rápida como para leer el archivo completo1, tee obtiene una salida ordenada. Pero en caso de que tee bloquee el archivo, lo borra (tee siempre borra el archivo de salida, excepto cuando se usa la opción de agregar) y eso es más o menos lo que sucede en los 3 escenarios.
Para abreviar, digamos que a veces la clasificación no es lo suficientemente rápida para leer el archivo 1. En tal caso, tee borra el archivo ANTES de que pueda leerlo.
Recomendaría el siguiente procedimiento:
cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
En caso de que quieras ver el resultado ordenado en stdout, hazlo así:
cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
No es una buena idea dejar que 2 comandos diferentes funcionen con 1 archivo en sistemas multiprocesador; nunca se puede estar seguro de cuál se ejecuta primero. En un sistema de un solo subproceso, el comportamiento sería diferente: secuencial.
Respuesta2
Dudo que ese comportamiento sea predecible (y seguramente no dependería de ello). El tee
comando probablemente inicia un nuevo proceso para enviar su entrada al "otro" destino. El sistema operativo almacenará en búfer la salida hasta que llegue al punto en el que crea el archivo de destino y escribe su búfer temporal en el archivo. El momento exacto en que esto sucede (y sobrescribe la fuente) probablemente dependa de:
- El tamaño del archivo y la memoria disponible para el búfer.
- El tiempo transcurrido
- Si la entrada desde la tubería
tee
termina
Esto va más allá de bash
: lo que se inicia es la forma en que funcionan los programas bash
. El shell simplemente interpreta los comandos que usted escribe e inicia los programas necesarios para ejecutar los comandos. El shell no tiene control sobre cómo funciona cada programa, y menos aún sobre cómo interactúan esos programas. Pedirle a un programa (o un conjunto de programas) que tome datos de un archivo de entrada y escriba el resultado en el mismo archivo de entrada en la misma oración es responsabilidad del usuario.
No olvide que bash es solo el intérprete de los comandos del usuario: es solo una herramienta shell
alrededor del sistema operativo para convertir las intenciones del usuario en llamadas al sistema.
Y esdocumentado, ¡también! Oeste correo, que aborda problemas similares. O estoHilo de desbordamiento de pila. Oeste hilo de Serverfault.
Tenga en cuenta que esto también puede suceder con la redirección de stdin
: si toma comandos de entrada de un archivo $ myprog < commandfile
:. Si myprog
escribe en el archivo de comandos, no hay garantía de que commandfile
se ejecuten todos los comandos.
Una analogía realmente básica sería algo como esta lista de instrucciones:
- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
¿Me imagino que harías una copia primero? (comando tomado deGuía avanzada de secuencias de comandos Bash)
Respuesta3
¿Entonces desea que se mantenga el contenido original del archivo, mientras agrega el archivo con los cambios?
tee over writes de forma predeterminada, intente usar el indicador -a para agregar el archivo con los cambios.
Respuesta4
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1
Puede escribir el archivo en un marcador de posición, cambiar el nombre del original a una copia de seguridad y luego mover el marcador de posición al original.