Debido a un error de aplicación aún no diagnosticado, tengo varios cientos de servidores con un disco lleno. Hay un archivo que se ha llenado con líneas duplicadas, no un archivo de registro, sino un archivo de entorno de usuario con definiciones variables (por lo que no puedo eliminar el archivo).
Escribí un sed
comando simple para verificar las líneas agregadas erróneamente y eliminarlas, y lo probé en una copia local del archivo. Funcionó según lo previsto.
Sin embargo, cuando lo probé en el servidor con el disco lleno, recibí aproximadamente el siguiente error (es de memoria, no copiar y pegar):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
Por supuesto, sé que no queda espacio. ¡Es por eso que estoy tratando de eliminar cosas! (El sed
comando que estoy usando reducirá un archivo de más de 4000 líneas a aproximadamente 90 líneas).
Mi sed
comando es solosed -i '/myregex/d' /path/to/file/filename
¿Hay alguna manera de aplicar este comando a pesar del disco lleno?
(Debe ser automatizado, ya que necesito aplicarlo a varios cientos de servidores como una solución rápida).
(Obviamente, el error de la aplicación debe diagnosticarse, pero mientras tanto los servidores no funcionan correctamente ...)
Actualización: La situación que enfrenté se resolvió eliminando algo más que descubrí que podía eliminar, pero todavía me gustaría la respuesta a esta pregunta, que sería útil en el futuro y para otras personas.
/tmp
es un no-go; Está en el mismo sistema de archivos.
Antes de liberar espacio en el disco, probé y descubrí que podía eliminar las líneas vi
abriendo el archivo y ejecutándolo :g/myregex/d
y luego guardando los cambios con éxito :wq
. Parece que debería ser posible automatizar esto, sin recurrir a un sistema de archivos separado para contener un archivo temporal ... (?)
fuente
sed -i
crea una copia temporal para operar. Sospecho queed
sería mejor para esto, aunque no estoy lo suficientemente familiarizado para proscribir una solución realed
ejecución:printf %s\\n g/myregex/d w q | ed -s infile
pero tenga en cuenta que algunas implementaciones también usan archivos temporales comosed
(puede probar busybox ed - afaik no crea un archivo temporal)echo
. usoprintf
. ysed
agregue algunos caracteres que suelte en la última línea para evitar perder espacios en blanco finales. Además, su shell debe ser capaz de manejar todo el archivo en una sola línea de comandos. ese es su riesgo, pruebe primero.bash
es especialmente malo en eso (creo que es hacer w / espacio de pila?) y puede enfermarte en cualquier momento. los dossed
'si recomendados al menos usarían el buffer de tubería del núcleo para un buen efecto entre ellos, pero el método es bastante similar. su subcomando comando también se truncaráfile
si el w / in sed es exitoso o no.sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}
y, si funciona, lee el resto de mi respuesta.Respuestas:
La
-i
opción realmente no sobrescribe el archivo original. Crea un nuevo archivo con la salida, luego lo renombra al nombre de archivo original. Como no tiene espacio en el sistema de archivos para este nuevo archivo, falla.Deberá hacerlo usted mismo en su script, pero cree el nuevo archivo en un sistema de archivos diferente.
Además, si solo está eliminando líneas que coinciden con una expresión regular, puede usar en
grep
lugar desed
.En general, rara vez es posible que los programas utilicen el mismo archivo como entrada y salida: tan pronto como comience a escribir en el archivo, la parte del programa que está leyendo el archivo ya no verá el contenido original. Por lo tanto, primero debe copiar el archivo original en algún lugar o escribir en un archivo nuevo y cambiarle el nombre cuando esté listo.
Si no desea utilizar un archivo temporal, puede intentar almacenar en caché el contenido del archivo en la memoria:
fuente
rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"
desde aquísed -i
preserva esas cosas?sed -i
no conserva ninguna de esas cosas. Acabo de probarlo con un archivo que no soy dueño, pero que se encuentra en un directorio que sí tengo, y me permitió reemplazar el archivo. El reemplazo es de mi propiedad, no del propietario original.var=$(< FILE); echo "$FILE" | grep '^"' > FILE
v=$(<file)&& printf %s\\n "$v" >file
, sino que ni siquiera usar&&
. El autor de la pregunta habla de ejecutarlo en un script, automatizando la sobrescritura de un archivo con una parte de sí mismo. al menos debe validar que puede abrir con éxito la entrada y la salida. Además, la cáscara podría explotar.Así es como
sed
funciona. Si se usa con-i
(editar en el lugar)sed
crea un archivo temporal con los nuevos contenidos del archivo procesado. Cuando terminesed
, reemplaza el archivo de trabajo actual con el archivo temporal. La utilidad no edita el archivo en el lugar . Ese es exactamente el comportamiento de cada editor.Es como si realizaras la siguiente tarea en un shell:
En este punto
sed
, intenta vaciar los datos almacenados en el archivo mencionado en el mensaje de error con lafflush()
llamada al sistema:Para su problema, veo una solución para montar un sistema de archivos separado (por ejemplo
tmpfs
, si tiene suficiente memoria o un dispositivo de almacenamiento externo) y mover algunos archivos allí, procesarlos allí y volverlos a mover.fuente
Desde que publiqué esta pregunta, he aprendido que
ex
es un programa compatible con POSIX. Tiene un enlace simbólico casi universalvim
, pero de cualquier manera, lo siguiente es (creo) un punto clave sobre losex
sistemas de archivos (tomado de la especificación POSIX):"... afectará a cualquier archivo ..." Creo que poner algo en el sistema de archivos (en absoluto, incluso un archivo temporal) contaría como "afectar a cualquier archivo". ¿Tal vez?*
El estudio cuidadoso de las especificaciones POSIX para
ex
indicar algunas "trampas" sobre su uso portátil previsto en comparación con los usos comunes con script de los que seex
encuentran en línea (que están llenos devim
comandos específicos).+cmd
es opcional según POSIX.-c
opciones también es opcional.:g
"come" todo hasta la próxima línea nueva no escapada (y, por lo tanto, lo ejecuta después de cada coincidencia encontrada para la expresión regular en lugar de una vez al final). Entonces-c 'g/regex/d | x'
solo elimina una instancia y luego sale del archivo.Entonces, de acuerdo con lo que he investigado, el método compatible con POSIX para editar in situ un archivo en un sistema de archivos completo para eliminar todas las líneas que coinciden con una expresión regular específica, es:
Esto debería funcionar siempre que tenga suficiente memoria para cargar el archivo en un búfer.
* Si encuentra algo que indique lo contrario, por favor, menciónelo en los comentarios.
fuente
ex +g/match/d -scx file
es compatible con POSIX?vi
funcionó en un sistema de archivos completo, creo que en la mayoría de los casos también funcionaríaex
, aunque tal vez no para un archivo descomunal.sed -i
no funciona en un sistema de archivos completo, independientemente del tamaño del archivo.¡Usa la pipa, Luke!
Leer archivo | filtro | respóndeme
en este caso
sed
no crea un nuevo archivo y solo envía una salida canalizada a ladd
que se abre el mismo archivo . Por supuesto, uno puede usargrep
en casos particularesluego truncar el resto.
fuente
sed
siempre usa archivos temporales?grep
de todos modos nosponge
comando. Sí,sed
con-i
lilke siempre crea archivos "seduyUdmw" con 000 derechos.Como se señaló en otras respuestas,
sed -i
funciona copiando el archivo a un nuevo archivo en el mismo directorio , haciendo cambios en el proceso y luego moviendo el nuevo archivo sobre el original. Por eso no funciona.ed
(el editor de línea original) funciona de manera similar, pero la última vez que lo verifiqué, lo usa/tmp
para el archivo scratch. Si/tmp
está en un sistema de archivos diferente del que está lleno,ed
puede hacer el trabajo por usted.Pruebe esto (en su indicador de shell interactivo):
El
P
(que es una P mayúscula ) no es estrictamente necesario. Se enciende provocando; sin ella, estás trabajando en la oscuridad, y algunas personas encuentran esto desconcertante. Elw
yq
son w rito y q uit.Si su
/tmp
directorio está en el sistema de archivos que está lleno (o si su sistema de archivos también está lleno), intente encontrar espacio en alguna parte. el caos mencionó el montaje de un tmpfs o un dispositivo de almacenamiento externo (por ejemplo, una unidad flash); pero, si tiene varios sistemas de archivos y no están todos llenos, simplemente puede usar uno de los otros existentes. caos sugiere copiar los archivos al otro sistema de archivos, editarlos allí (consed
) y luego volver a copiarlos. En este punto, esa puede ser la solución más simple. Pero una alternativa sería crear un directorio grabable en un sistema de archivos que tenga algo de espacio libre, establecer la variable de entornoTMPDIR
para que apunte a ese directorio y luego ejecutarloed
. (Divulgación: no estoy seguro de si esto funcionará, pero no puede doler).Una vez que empiece a
ed
trabajar, puede automatizar esto haciendoen un guion O , como lo sugiere don_crissti.
printf '%s\n' 'g/myregex/d' w q | ed -s filename
fuente
ed
o conex
) de modo que se use la memoria en lugar de un sistema de archivos separado? Eso es lo que realmente estaba buscando (y la razón por la que no he aceptado una respuesta)ed
hace muchos años. Todavía había cosas como las computadoras de 16 bits, en las que los procesos se limitaban a un espacio de direcciones de 64K (!), Por lo que la idea de un editor que leyera todo el archivo en la memoria no fue un comienzo. Desde entonces, por supuesto, la memoria se ha vuelto más grande, pero también lo han hecho los discos y los archivos. Como los discos son tan grandes, las personas no sienten la necesidad de lidiar con la contingencia de/tmp
quedarse sin espacio. Acabo de echar un vistazo rápido al código fuente de una versión reciente deed
, y todavía parece ... (Cont.)ed
(ex
oovi
) ofrezca una opción para mantener el búfer en la memoria. Por otro lado, Edición de texto con ed y vi - Capítulo 11: Procesamiento de texto - Parte II: Explorando Red Hat Linux - Red Hat Linux 9 Secretos profesionales - Los sistemas Linux dicen queed
el búfer de edición reside en la memoria, ... (Cont. )vi
(que es el mismo programa queex
). Creo que solo están usando una redacción descuidada e imprecisa, pero, si está en Internet (o en forma impresa), debe ser cierto, ¿verdad? Pagas tu dinero y tomas tu elección.Puede truncar el archivo con bastante facilidad si puede obtener el recuento de bytes a su desplazamiento y sus líneas se producen desde un punto inicial hasta el final.
O bien, si
${TMPDIR:-/tmp}
está en algún otro sistema de archivos, tal vez:Porque (la mayoría) los shells ponen sus documentos aquí en un archivo temporal eliminado. Es perfectamente seguro siempre que el
<<FILE
descriptor se mantenga de principio a fin y${TMPDIR:-/tmp}
tenga tanto espacio como sea necesario.Los shells que no usan archivos temporales usan tuberías, por lo que no es seguro usarlos de esta manera. Estos depósitos son típicamente
ash
derivados comobusybox
,dash
, BSDsh
-zsh
,bash
,ksh
, y la cáscara de Bourne, sin embargo, todos los archivos temporales de uso.aparentemente escribí un pequeño programa de shell en julio pasado para hacer algo como esto
Si
/tmp
no es viable, entonces, siempre que pueda guardar el archivo en la memoria, algo así como ...... como un caso general, al menos se aseguraría de que el archivo estuviera completamente protegido por el primer
sed
proceso antes de intentar truncar el archivo de entrada / salida.Una solución más específica y eficiente podría ser:
... porque no molestaría las líneas de almacenamiento en búfer que querías eliminar de todos modos.
Una prueba del caso general:
fuente
/tmp
que está en el mismo sistema de archivos. Me gusta tused
versión dual . Creo que una combinación de Barmar y su respuesta probablemente sería mejor, algo así como:myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar
(Para este caso, no me importa preservar las nuevas líneas finales).sed
|cat
Lo anterior nunca abre la salida a menos quesed
ya haya almacenado en el búfer todo el archivo y esté listo para comenzar a escribir todo en la salida. Si intenta almacenar el archivo en el búfer y falla,read
no tiene éxito porque encuentra EOF en la|
tubería antes de leer su primera línea nueva y, por lo tanto,cat >out
nunca sucede hasta el momento de escribirlo de la memoria por completo. un desbordamiento o algo similar simplemente falla. También toda la tubería devuelve el éxito o el fracaso cada vez. almacenarlo en una var es simplemente más arriesgado.file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shite
entonces el archivo de salida y el var se escribirían simultáneamente, lo que haría una copia de seguridad efectiva o una , que es la única razón por la que querría complicar las cosas más de lo necesario.read script
yread v
en su respuesta. Si puedes dar más detalles al respecto, te lo agradeceré mucho, ¡gracias!$script
es elsed
script que usarías para apuntar a cualquier parte de tu archivo que quisieras; es el script que te da el resultado final que deseas en la transmisión.v
es solo un marcador de posición para una línea vacía. en unbash
shell no es necesario porquebash
usará automáticamente la$REPLY
variable de shell en su lugar si no especifica uno, pero POSIXly siempre debe hacerlo. Me alegro de que lo encuentres útil, por cierto. Suerte con ello. im mikeserv @ gmail si necesitas algo en profundidad. Debería tener una computadora nuevamente en unos díasEsta respuesta toma prestadas ideas de esta otra respuesta y de esta otra respuesta, pero se basa en ellas, creando una respuesta que es más generalmente aplicable:
La primera línea ejecuta el
sed
comando con salida escrita en salida estándar (y no en un archivo); específicamente, a una tuberíawc
para contar los personajes. La segunda línea también ejecuta elsed
comando con la salida escrita en la salida estándar, que, en este caso, se redirige al archivo de entrada en modo lectura / escritura de sobrescritura (sin truncar), que se trata aquí . Esto es algo peligroso de hacer; es seguro solo cuando el comando de filtro nunca aumenta la cantidad de datos (texto); es decir, por cada n bytes que lee, escribe n o menos bytes. Esto es, por supuesto, cierto para elsed '/myregex/d'
comando; por cada línea que lee, escribe exactamente la misma línea, o nada. (Otros ejemplos:s/foo/fu/
os/foo/bar/
estaría a salvo, peros/fu/foo/
ys/foo/foobar/
no lo estaría).Por ejemplo:
porque estos 32 bytes de datos:
se sobrescribió con estos 25 caracteres:
dejando los siete bytes
night.\n
restantes al final.Finalmente, el
dd
comando busca el final de los nuevos datos depurados (byte 25 en este ejemplo) y elimina el resto del archivo; es decir, trunca el archivo en ese punto.Si, por alguna razón, el
1<>
truco no funciona, puedes hacerloAdemás, tenga en cuenta que, siempre que todo lo que esté haciendo sea eliminar líneas, todo lo que necesita es
grep -v myregex
(como señaló Barmar ).fuente
sed -i 'd' / ruta / a / archivo / nombre de archivo
fuente