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 sedcomando 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 sedcomando que estoy usando reducirá un archivo de más de 4000 líneas a aproximadamente 90 líneas).
Mi sedcomando 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.
/tmpes 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 viabriendo el archivo y ejecutándolo :g/myregex/dy 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 -icrea una copia temporal para operar. Sospecho queedsería mejor para esto, aunque no estoy lo suficientemente familiarizado para proscribir una solución realedejecución:printf %s\\n g/myregex/d w q | ed -s infilepero tenga en cuenta que algunas implementaciones también usan archivos temporales comosed(puede probar busybox ed - afaik no crea un archivo temporal)echo. usoprintf. ysedagregue 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.bashes 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áfilesi 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
-iopció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
greplugar 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 -ipreserva esas cosas?sed -ino 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 '^"' > FILEv=$(<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
sedfunciona. Si se usa con-i(editar en el lugar)sedcrea 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
exes un programa compatible con POSIX. Tiene un enlace simbólico casi universalvim, pero de cualquier manera, lo siguiente es (creo) un punto clave sobre losexsistemas 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
exindicar algunas "trampas" sobre su uso portátil previsto en comparación con los usos comunes con script de los que seexencuentran en línea (que están llenos devimcomandos específicos).+cmdes opcional según POSIX.-copciones 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 filees compatible con POSIX?vifuncionó 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 -ino 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
sedno crea un nuevo archivo y solo envía una salida canalizada a laddque se abre el mismo archivo . Por supuesto, uno puede usargrepen casos particularesluego truncar el resto.
fuente
sedsiempre usa archivos temporales?grepde todos modos nospongecomando. Sí,sedcon-ililke siempre crea archivos "seduyUdmw" con 000 derechos.Como se señaló en otras respuestas,
sed -ifunciona 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/tmppara el archivo scratch. Si/tmpestá en un sistema de archivos diferente del que está lleno,edpuede 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. Elwyqson w rito y q uit.Si su
/tmpdirectorio 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 entornoTMPDIRpara 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
edtrabajar, puede automatizar esto haciendoen un guion O , como lo sugiere don_crissti.
printf '%s\n' 'g/myregex/d' w q | ed -s filenamefuente
edo 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)edhace 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/tmpquedarse 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(exoovi) 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 queedel 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
<<FILEdescriptor 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
ashderivados 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
/tmpno 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
sedproceso 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
/tmpque está en el mismo sistema de archivos. Me gusta tusedversió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|catLo anterior nunca abre la salida a menos quesedya 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,readno tiene éxito porque encuentra EOF en la|tubería antes de leer su primera línea nueva y, por lo tanto,cat >outnunca 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" || shiteentonces 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 scriptyread ven su respuesta. Si puedes dar más detalles al respecto, te lo agradeceré mucho, ¡gracias!$scriptes elsedscript 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.ves solo un marcador de posición para una línea vacía. en unbashshell no es necesario porquebashusará automáticamente la$REPLYvariable 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
sedcomando con salida escrita en salida estándar (y no en un archivo); específicamente, a una tuberíawcpara contar los personajes. La segunda línea también ejecuta elsedcomando 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.\nrestantes al final.Finalmente, el
ddcomando 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