.hgignoreHoy estaba tratando de editar rápidamente un archivo desde el shell de Cygwin bash, y agregué una línea que fue un error. No estoy seguro de si esta era la mejor manera de hacerlo, pero rápidamente pensé en usar head -1 .hgignorepara eliminar la línea ofensiva (anteriormente solo tenía una línea en el archivo). Efectivamente, cuando se ejecuta, da la primera línea como la única salida.
Pero cuando intenté redirigir la salida y reescribir el archivo usando head -1 .hgignore > .hgignore, el archivo estaba vacío. ¿Por qué pasó esto? Si trato de agregar en su lugar, head -1 .hgignore >> .hgignorese agrega correctamente, pero obviamente este no es el resultado deseado. ¿Por qué una redirección truncada no funciona en este caso?
shell
io-redirection
Voithos
fuente
fuente

cutcambiar un archivo en su lugar? , ¿Cómo puedo hacer que iconv reemplace el archivo de entrada con la salida convertida?Respuestas:
Cuando el shell obtiene una línea de comando como:
command > file.outel shell se abre (y quizás crea) el archivo llamadofile.out. El shell establece el descriptor de archivo 0 en el descriptor de archivo que obtuvo desde la apertura. Así es como funciona la redirección de E / S: cada proceso conoce los descriptores de archivo 0, 1 y 2.La parte difícil de esto es cómo abrir
file.out. La mayoría de las veces, deseafile.outabrir para escritura en el desplazamiento 0 (es decir, truncado) y esto es lo que hizo el shell por usted. Truncó .hgignore, lo abrió para escribir, duplicó el descriptor de archivo a 0, luego lo ejecutóhead. Clobbering instantáneo de archivos.En bash shell, haces una
set noclobberpara cambiar este comportamiento.fuente
Creo que Bruce responde lo que está pasando aquí con la tubería de shell.
Una de mis pequeñas utilidades favoritas es el
spongecomando de moreutils . Resuelve exactamente este problema al "absorber" todas las entradas disponibles antes de abrir el archivo de salida de destino y escribir los datos. Le permite escribir tuberías exactamente como esperaba:La solución del pobre es canalizar la salida a un archivo temporal, luego, una vez que se termina la canalización (por ejemplo, el siguiente comando que ejecuta) es mover el archivo temporal nuevamente a la ubicación del archivo original.
fuente
head -1 .hgignore | tee .hgignore?teeestá en coreutils, y como beneficio adicional / efecto secundario, esto también escribe en STDOUTteeabre y trunca el archivo en el que está escribiendo cuando se instancia como todo lo demás, por lo que no resuelve el problema principal aquí de la condición de carrera al leer el contenido del archivo antes de truncarlo con la escritura.teeparece hacer lo deseado. Tengo la versión8.13en mi máquina.teeprograma truncará sus archivos, no está configurado para duplicarlos.En
filese trunca antes deheadcomenzar, pero si lo escribe:no
filees como se abre en modo lectura-escritura. Sin embargo, cuandoheadtermina de escribir, no trunca el archivo, por lo que la línea de arriba sería no operativa (headsimplemente reescribiría la primera línea sobre sí misma y dejaría las otras intactas).Sin embargo, después de que
headhaya regresado y mientrasfdtodavía esté abierto, puede llamar a otro comando que haga eltruncate.Por ejemplo:
Lo que importa aquí es que
truncatearriba,headsolo mueve el cursor para fd 1 dentro del archivo justo después de la primera línea. Reescribe la primera línea que no necesitábamos, pero eso no es dañino.Con una cabeza POSIX, podríamos escapar sin reescribir esa primera línea:
Aquí, estamos usando el hecho de que
headmueve la posición del cursor en su stdin. Si bienheadnormalmente leería su entrada en grandes fragmentos para mejorar el rendimiento, POSIX requeriría (cuando sea posible)seekretroceder justo después de la primera línea si hubiera ido más allá. Sin embargo, tenga en cuenta que no todas las implementaciones lo hacen.Alternativamente, puede usar el
readcomando de shell en este caso:fuente
STDINsimilar a lo que has logrado usandoperlanteriorddpuede truncarse en cualquier desplazamiento absoluto arbitrario en el archivo. Por lo que puede determinar el desplazamiento de la segunda línea y truncado a partir de ahí con el bytedd bs=1 seek="$offset" of=fileLa solución del hombre real es
o como una sola línea
O con GNU sed:
(No, estoy bromeando. Usaría un editor interactivo
vi .hgignoreGddZZ) .fuente
:wqmásZZ?:xque es lo que hacen los dedos de forma automáticaZQes lo mismo que:q!Puede usar Vim en modo Ex:
2,seleccione las líneas 2 hasta el finaldEliminarxguardar y cerrarfuente
Para la edición de archivos en el lugar, también puede usar el truco de manejo de archivos abiertos como se muestra por Jürgen Hötzel en la salida Redirect de sed 's / c / d /' myFile a myFile .
fuente
rm .hgignoresu energía falla, quitando horas de duro trabajo. Ok, no importa.hgignore, pero ¿por qué harías algo tan complicado de todos modos? Por lo tanto, mi voto negativo: técnicamente correcto pero una muy mala idea.perl -i(para la edición in situ), y no me sorprendería si algunas implementaciones de losed -ihicieran también (aunquesedparece que la última versión de GNU no lo hace).