sintaxis - Truncar un archivo mientras se usa(Linux)
comandos basicos de linux y ejemplos (13)
Tengo un proceso que está escribiendo una gran cantidad de datos para stdout, que estoy redirigiendo a un archivo de registro. Me gustaría limitar el tamaño del archivo al copiar ocasionalmente el archivo actual a un nuevo nombre y truncarlo.
Mis técnicas habituales de truncar un archivo, como
cp /dev/null file
no funcionan, presumiblemente porque el proceso lo está utilizando.
¿Hay alguna forma de que pueda truncar el archivo? ¿O eliminarlo y de alguna manera asociar el proceso ''stdout con un nuevo archivo?
FWIW, es un producto de terceros que no puedo modificar para cambiar su modelo de registro.
EDITAR la redirección sobre el archivo parece tener el mismo problema que la copia anterior: el archivo vuelve a su tamaño anterior la próxima vez que se escribe en:
ls -l sample.log ; echo > sample.log ; ls -l sample.log ; sleep 10 ; ls -l sample.log
-rw-rw-r-- 1 user group 1291999 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1292311 Jun 11 2009 sample.log
¿Comprobó el comportamiento de cualquier señal como SIGHUP al producto de terceros, para ver si comienza a registrar un archivo nuevo? Primero movería el archivo anterior a un nombre permanente.
kill -HUP [process-id]
Y luego comenzaría a escribir de nuevo.
Alternativamente (como Billy sugirió) tal vez redirija la salida de la aplicación a un programa de registro como multilog o el que se usa comúnmente con Apache, conocido como cronolog. Luego tendrá un control más detallado de dónde va todo antes de que se escriba en el descriptor de archivo inicial (archivo), que realmente es todo lo que es.
@Hobo usa freopen () , reutiliza la transmisión para abrir el archivo especificado por nombre de archivo o para cambiar su modo de acceso. Si se especifica un nuevo nombre de archivo, la función primero intenta cerrar cualquier archivo que ya esté asociado con la transmisión (tercer parámetro) y lo desasocia. Entonces, independientemente de si esa secuencia se cerró exitosamente o no, freopen abre el archivo especificado por nombre de archivo y lo asocia con la secuencia tal como lo haría fopen usando el modo especificado.
si un binario de terceros está generando registros, necesitamos escribir un contenedor que rotará los registros, y thirdparty se ejecutará en el hilo de Proxyrun como se muestra a continuación.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <string.h>
using namespace std;
extern "C" void * proxyrun(void * pArg){
static int lsiLineNum = 0;
while(1)
{
printf("/nLOGGER: %d",++lsiLineNum);
fflush(stdout);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t lThdId;
if(0 != pthread_create(&lThdId, NULL, proxyrun, NULL))
{
return 1;
}
char lpcFileName[256] = {0,};
static int x = 0;
while(1)
{
printf("/n<<<MAIN SLEEP>>>");
fflush(stdout);
sprintf(lpcFileName, "/home/yogesh/C++TestPrograms/std.txt%d",++x);
freopen(lpcFileName,"w",stdout);
sleep(10);
}
return 0;
}
A partir de coreutils 7.0, hay un comando truncate
.
Descargué y compilé los últimos coreutils
para poder truncate
.
Ran ./configure
y make
, pero no ejecutó make install
.
Todas las utilidades compiladas aparecen en la carpeta "src".
corrí
[path]/src/truncate -s 1024000 textfileineedtotruncate.log
en un archivo de registro de 1.7 GB.
No se modificó el tamaño de la lista cuando se usa ls -l
, pero se liberó todo el espacio en disco, que es lo que realmente necesitaba hacer antes de que /var
llene y se elimine el proceso.
Gracias por el consejo sobre "truncar"!
Eche un vistazo a la utilidad split(1)
, parte de GNU Coreutils.
En Linux (en realidad todos los unicies), los archivos se crean cuando se abren y eliminan cuando nada contiene una referencia a ellos. En este caso, el programa que lo abrió y el directorio en el que se abrió ''in'' contienen referencias al archivo. Cuando el programa cp desea escribir en el archivo, obtiene una referencia del mismo desde el directorio, escribe una longitud de cero en los metadatos almacenados en el directorio (esto es una ligera simplificación) y abandona el identificador. Luego, el programa original, que aún conserva el identificador original del archivo, escribe algunos datos más en el archivo y guarda lo que cree que debe ser la longitud.
incluso si va a eliminar el archivo del directorio, el programa continuará escribiéndole datos (y agotará el espacio en el disco) aunque ningún otro programa tenga ninguna forma de referenciarlo.
en resumen, una vez que el programa tiene una referencia (identificador) para un archivo, nada de lo que haga va a cambiar eso.
En teoría, existen formas de modificar el comportamiento de los programas al establecer LD_LIBRARY_PATH para incluir un programa que intercepta todas las llamadas al sistema de acceso a archivos. Recuerdo haber visto algo como esto en alguna parte, aunque no puedo recordar el nombre.
Lo interesante de esos archivos renovados es que los primeros 128 KB más o menos serán todos ceros después de que trunque el archivo copiando /dev/null
sobre él. Esto sucede porque el archivo se trunca a longitud cero, pero el descriptor de archivo en la aplicación aún apunta inmediatamente después de su última escritura. Cuando se escribe de nuevo, el sistema de archivos trata el inicio del archivo como cero bytes, sin escribir los ceros en el disco.
Idealmente, debe pedirle al proveedor de la aplicación que abra el archivo de registro con el indicador O_APPEND
. Esto significa que después de truncar el archivo, la siguiente escritura buscará implícitamente hasta el final del archivo (es decir, volverá al offset cero) y luego escribirá la nueva información.
Este código almacena la salida estándar por lo que está en modo O_APPEND
y luego invoca el comando dado por sus argumentos (como nice
ejecuta un comando después de ajustar su buen nivel, o nohup
ejecuta un comando después de arreglar las cosas por lo que ignora SIGHUP).
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
static char *arg0 = "<unknown>";
static void error(const char *fmt, ...)
{
va_list args;
int errnum = errno;
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc(''/n'', stderr);
fflush(0);
exit(1);
}
int main(int argc, char **argv)
{
int attr;
arg0 = argv[0];
if (argc < 2)
error("Usage: %s cmd [arg ...]", arg0);
if ((attr = fcntl(1, F_GETFL, &attr)) < 0)
error("fcntl(F_GETFL) failed");
attr |= O_APPEND;
if (fcntl(1, F_SETFL, attr) != 0)
error("fcntl(F_SETFL) failed");
execvp(argv[1], &argv[1]);
error("failed to exec %s", argv[1]);
return(1);
}
Mi prueba fue algo casual, pero apenas suficiente para convencerme de que funcionó.
Alternativa más simple
Billy señala en su answer que '' >>
'' es el operador de adición, y de hecho, en Solaris 10, bash (versión 3.00.16 (1)) usa el indicador O_APPEND
, lo que hace innecesario el código anterior, como se muestra (''Negro JL: ''es mi mensaje en esta máquina):
Black JL: truss -o bash.truss bash -c "echo Hi >> x3.29"
Black JL: grep open bash.truss
open("/var/ld/ld.config", O_RDONLY) Err#2 ENOENT
open("/usr/lib/libcurses.so.1", O_RDONLY) = 3
open("/usr/lib/libsocket.so.1", O_RDONLY) = 3
open("/usr/lib/libnsl.so.1", O_RDONLY) = 3
open("/usr/lib/libdl.so.1", O_RDONLY) = 3
open("/usr/lib/libc.so.1", O_RDONLY) = 3
open("/platform/SUNW,Ultra-4/lib/libc_psr.so.1", O_RDONLY) = 3
open64("/dev/tty", O_RDWR|O_NONBLOCK) = 3
stat64("/usr/openssl/v0.9.8e/bin/bash", 0xFFBFF2A8) Err#2 ENOENT
open64("x3.29", O_WRONLY|O_APPEND|O_CREAT, 0666) = 3
Black JL:
Use el redireccionamiento adjunto en lugar del código de envoltura ('' cantrip '') anterior. Esto solo demuestra que cuando se usa una técnica particular para otros fines (válidos), adaptarla a otra más no es necesariamente el mecanismo más simple, aunque funcione.
Pruebe > file
.
Actualización con respecto a los comentarios: funciona muy bien para mí:
robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file
content
robert@rm:~> > test-file
robert@rm:~> cat test-file
Redirigir la salida usando >> en lugar de>. Esto le permitirá truncar el archivo sin que el archivo vuelva a crecer a su tamaño original. Además, no te olvides de redirigir STDERR (2> y 1).
Entonces, el resultado final sería: myprogram >> myprogram.log 2>&1 &
Tuve un problema similar en redhat v6, echo > file
o > file
causaba que apache y tomcat fallaran ya que los archivos de registro se volvían inaccesibles para ellos.
Y la solución fue extraña
echo " " > file
limpiaría el archivo y no causaría ningún problema.
Tuve un problema similar y no pude hacer un "tail -f" en el resultado de un script que se ejecutó desde cron:
* * * * * my_script >> /var/log/my_script.log 2>&1
Lo arreglé cambiando la redirección de stderr:
* * * * * my_script >> /var/log/my_script.log 2>/var/log/my_script.err
a medida que se utiliza el archivo, si intenta anularlo o algo así, a veces puede "confundir" la aplicación que está escribiendo en el archivo de registro y es posible que no registre nada después de eso.
Lo que intentaría hacer es configurar un tipo de proxy / filtro para ese registro, en lugar de redirigirlo a un archivo, redirigirlo a un proceso o algo que pueda ingresar y escribir en un archivo continuo.
Tal vez se puede hacer por script; de lo contrario, podrías escribir una aplicación simple para eso (java u otra cosa). El impacto en el rendimiento de la aplicación debería ser bastante pequeño, pero tendrás que ejecutar algunas pruebas.
Por cierto, su aplicación, ¿es una aplicación web independiente, ...? Tal vez hay otras opciones para ser investigadas.
Editar: también hay un Operador de redireccionamiento adjunto >> que personalmente nunca he usado, pero puede que no bloquee el archivo.
en lugar de redirigirlo a un archivo, puede canalizarlo a un programa que automáticamente gira el archivo cerrándolo, moviéndolo y abriendo uno nuevo cada vez que se hace demasiado grande.