.hgignore
Hoy 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 .hgignore
para 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 >> .hgignore
se 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
cut
cambiar 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.out
el 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.out
abrir 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 noclobber
para 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
sponge
comando 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
?tee
está en coreutils, y como beneficio adicional / efecto secundario, esto también escribe en STDOUTtee
abre 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.tee
parece hacer lo deseado. Tengo la versión8.13
en mi máquina.tee
programa truncará sus archivos, no está configurado para duplicarlos.En
file
se trunca antes dehead
comenzar, pero si lo escribe:no
file
es como se abre en modo lectura-escritura. Sin embargo, cuandohead
termina de escribir, no trunca el archivo, por lo que la línea de arriba sería no operativa (head
simplemente reescribiría la primera línea sobre sí misma y dejaría las otras intactas).Sin embargo, después de que
head
haya regresado y mientrasfd
todavía esté abierto, puede llamar a otro comando que haga eltruncate
.Por ejemplo:
Lo que importa aquí es que
truncate
arriba,head
solo 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
head
mueve la posición del cursor en su stdin. Si bienhead
normalmente leería su entrada en grandes fragmentos para mejorar el rendimiento, POSIX requeriría (cuando sea posible)seek
retroceder 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
read
comando de shell en este caso:fuente
STDIN
similar a lo que has logrado usandoperl
anteriordd
puede 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=file
La 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 .hgignore
GddZZ
) .fuente
:wq
másZZ
?:x
que es lo que hacen los dedos de forma automáticaZQ
es lo mismo que:q!
Puede usar Vim en modo Ex:
2,
seleccione las líneas 2 hasta el finald
Eliminarx
guardar 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 .hgignore
su 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 -i
hicieran también (aunquesed
parece que la última versión de GNU no lo hace).