Corregir nombres de archivos

July 18th, 2023

Esto está súper fácil. Cuando los nombre está con un mal charset, o por cualquier otro motivo. Se puede usar detox:

detox -r -n *
nos mostrará todos los archivos que cambiará. Y:
detox -r *
Cambiará recursivamente todos los archivos. Espero les sirva.

Analisis de kmer

August 22nd, 2022

Con estos pequeños archivos se puede hacer un analisis de kmer rápido y simple en linux. Para compilar solamente ejecutar:
gcc -o kmer kmer.c

Luego para analizar la ocurrencia de 41mer ejecutar:
cat fastQfile.fq | parser | ./kmer 41 > salida.txt

La salida será algo así:

AAATAATGTACGGGGGAGATGCATGAATTTTTCGGATAAAA 190
AATAATGTACGGGGGAGATGCATGAATTTTTCGGATAAAAT 189
ATAATGTACGGGGGAGATGCATGAATTTTTCGGATAAAATC 190
TAATGTACGGGGGAGATGCATGAATTTTTCGGATAAAATCT 193
AATGTACGGGGGAGATGCATGAATTTTTCGGATAAAATCTG 193
ATGTACGGGGGAGATGCATGAATTTTTCGGATAAAATCTGA 193

En la primer columna está el mer y en la segunda la frecuencia. Los archivos son:

–parser–

#!/bin/bash
##parser from fastQ to kmer
if [ $# -eq 0 ]; then
    echo "usage: parser file.fq > file.txt";
    exit 1;
fi
#total of lines
lines=$(cat $1 | wc -l);
for i in $(seq 2 4 $lines); do
    sed $i'q;d' $1
done
exit 0;

–kmer.c–

/**
 * Author:    Matias Neiff and Tatiana Ponce
 * Created:   21.08.2020
 *
 * Software to evaluate the occurrence of kmers in a dna sequence. Use it good, use it for good.
 * otput: kmer [tab] frecuency
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
 **/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
// this value has to be sufficient big to host the entire sequence
#define MAX_SEQ_LEN 24845291000
// this is the read buffer
#define MAX_READ_LEN 100 * 1024
# 24845291

//compare to array string and return true if are identical
int compareArrays(char a[], char b[], int n)
{
  for (int i=0; i<n; ++i) if (a[i] != b[i]) return 0;
  return 1;
}

int usage() {
        printf("Usage: cat file | kmer k\n");
	exit(1);
}

int main(int argc, char **argv) {
    int k;
    char* p;
    errno = 0;
    long conv;
    // at least need two arguments
    if (argc == 2) {
	conv = strtol(argv[1], &p, 10);
    } else {
	usage();
    }

    //check if K is integer
    if (errno != 0 || *p != '\0' || conv > 9999 || conv < 1) {
	usage();
    } else {
	k = conv;
    }
    // allocate memory for the three big strings
    char* seq;
    char* buff;
    char* prevkmer;
    seq = (char*) malloc(MAX_SEQ_LEN*sizeof(char));
    buff = (char*) malloc(MAX_READ_LEN*sizeof(char));
    prevkmer = (char*) malloc(MAX_SEQ_LEN*sizeof(char));
    if (seq == NULL) {
        printf("Memory not allocated.\n");
        exit(0);
    }
    // we need to store the bytes readed trouhgt the pipe, and the total of bytes readed
    ssize_t bytesRead=0;
    ssize_t totalRead=0;
    bytesRead = read( STDIN_FILENO, buff, MAX_READ_LEN );
    while (bytesRead != 0) {
	buff[bytesRead]='\0';
	strcat(seq,buff);
	totalRead = totalRead + bytesRead;
	bytesRead = read( STDIN_FILENO, buff, MAX_READ_LEN );
    }
    // add the null to the end to complete the string format of C
    seq[totalRead]='\0';
    char kmer[k];
    char window[k];
    int seqlen = totalRead;
    int freq;
    prevkmer[0]='\0';
    //start the analysis
    for(int index=0;index<seqlen-k;index++) {
	// select kmer
	for (int i=0;i<k;i++) {
	    kmer[i]=seq[i+index];
	}
       // prevkmer store already proccessed kmer to avoid procces twice
	kmer[k]='\0';
	// do not procces if was already proccess with this kmer or if have an return car (incomplete mer)
	if (!strstr(prevkmer,kmer) && !strstr(kmer,"\n")) {
	// store as previous kmer to not proccess again
    	strcat(prevkmer,kmer);
	freq=0;
	// search frecuency of this kmer
	for (int x=0;x<seqlen-k;x++) {
	    //fill the window
	    for (int w=0;w<k;w++) {
		window[w]=seq[x+w];
	    }
	    // if kmer equal to window
	    if(compareArrays(kmer,window,k)) { freq++;x=x+k;}	    
	}
	if (freq>1) printf("%s\t%d\n",kmer,freq);
	}
    } //end for
    free(seq);
    free(buff);
  return 0;
} // end function: main

Copiar filtros de una cuenta a otra en zimbra

August 31st, 2021

Espero que les sea útil, solo tienen que reemplazar las cuentas (src and dst)

zmmailbox -z -m source_mailbox@domain.org gfrl | sed -i ‘s/^/afrl /’ filters.txt | zmmailbox -z -m destination_mailbox@domain.org

mp3 a ringtone

May 5th, 2019

Les dejo esta forma súper fácil de pasar de mp3 a ringtone con ffmpeg. Espero les sirva.
ffmpeg -i file.mp3 -ac 1 -ab 128000 -f mp4 -c:a aac -y ringtone.m4a

Saludos

Comparar dos directorios

April 3rd, 2019

Acá les dejo un script que hice en bash para comprar dos directorios, nada del otro mundo pero muy útil. Usa md5sum, no sé, espero les sirva.

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
dir1=$1
dir2=$2
cd $dir1
find ./  -type f -exec md5sum {} \; > /mnt/virtual5/dir1
cd $dir2
find ./  -type f -exec md5sum {} \; > /mnt/virtual5/dir2
echo "archivos que estan en $dir1 y no en $dir2"
for i in $(cat /mnt/virtual5/dir1 | awk '{print $1}'); do  
    if ! grep -lq $i /mnt/virtual5/dir2; then 
        archivo=$(cat /mnt/virtual5/dir1 | grep $i |cut -c36-);
	archpath="$dir1/$archivo"
	stat -c "%y %n" $"$archpath";
    fi; 
done;

echo "archivos que estan en $dir2 y no en $dir1"
for i in $(cat /mnt/virtual5/dir2 | awk '{print $1}'); do  
    if ! grep -lq $i /mnt/virtual5/dir1; then 
        archivo=$(cat /mnt/virtual5/dir2 | grep $i |cut -c36-);
	archpath="$dir2/$archivo"
	stat -c "%y %n" $"$archpath";
    fi; 
done;

IFS=$SAVEIFS

Poniendo tags id3 a varios mp3s

November 18th, 2018

Bueno esto es muy fácil de hacer en consola desde la mac, primero instalamos el eyeD3

brew install eye-d3

Luego borramos los tags que tengan los mp3s…

eyeD3  –remove-all *

Luego agregamos primero el V2 y luego el V1. Cosa de que sea compatible en windows viejos y en itunes.

eyeD3 -2 -a “Matias Neiff” -b “Matias Neiff” -A “Ni mas ni menos” -t “Ni mas ni menos” -G 13 -Y 2018 –add-image “../art/covermp3.jpg:FRONT_COVER” *.mp3

eyeD3 -1 -a “Matias Neiff” -b “Matias Neiff” -A “Ni mas ni menos” -t “Ni mas ni menos” -G 13 -Y 2018 *.mp3

Eso es todo amigos, espero les sirva.

data carving (recuperación de datos) postgresql

December 21st, 2016

Alguna vez se preguntaron cual sería el último recurso si por error (o una falla de disco) se borra nuestro directorio postgres corrompiendo totalmente la base de datos?, bueno, les puedo asegurar que a esta altura les encantaría tener backups, pero un poco aquello de que normalmente a uno lo contratan cuando el sistema es un desastre y por el otro lado “desasperación es la mejor de las maestras”. Por A o por B me tocó hacer esto, que no es más que el último recurso cuando por error o una falla de disco se borro la base de datos. Ojalá lo disfruten ya que es una de las cosas más difíciles que hice hasta ahora, en el top 3 digamos.

Bien lo primero que se le ocurre a cualquier hijo de cristiano que tuvo la desgracia de usar DOS es hacer el famoso, conocido, populuar y nunca bien ponderado undelete de microsoft, la versión de linux para ext4 sería extundelete y anda muy muy bien. Sería algo así:

extundelete --restore-all /dev/XXXN

Nos ceará un directorio llamado RECOVERY_FILES con todo lo que encontró, ahí podemos intentar hacer un REINDEX y VACUUM FULL de cada tabla, quizás también usando un zero_damaged_pages. El problema amigos, es que para tablas grandes nada de esto funcionaa, al menos no para mi. Y toca caer en el data carving, que no es más que cavar en el disco buscando pedazos de lo que necesitamos e intentar reconstruirlo. Si bien hay aplicaciones que hacen esto por nosotros para determinado tipo de archivos, como PhotoRec (http://www.cgsecurity.org/wiki/PhotoRec_Data_Carving) el gran problema es que nosotros no estamos buscando fotos, o archivos de texto, así que estos programas no hará nada por nosotros… necesitamos poder poner nuestros headers y ahí es cuando choca los cinco y entra a jugar foremost (http://foremost.sourceforge.net) este a diferencia de los anteriores podemos poner nuestros propios headers así que allá vamos, primero a buscar nuestros headers…

Buscamos nuestra tabla de postgres,

SELECT pg_relation_filepath('gis_gps');
  pg_relation_filepath  
------------------------
 base/45608546/45611313

Muy bien, ahora saquemos el header

hexdump -C postgresindoe | head
00000000  00 00 00 00 b8 2d 40 0f  00 00 00 00 a0 01 e8 01  |.....-@.........|
00000010  00 20 04 20 00 00 00 00  b0 9f a0 00 70 9f 78 00  |. . ........p.x.|
00000020  30 9f 80 00 f0 9e 80 00  a0 9e a0 00 58 9e 88 00  |0...........X...|

Luego de correrle este procedimiento con varias tablas se darán cuenta de que hay un patrón, no sé si será el mismo para todas las arqs y versiones de postgres, pero para mi fue este que agregué a mi foremost.conf:
—– foremost.conf extract—–
# case size header footer
#extension sensitive
NONE y 900000000000 ?\x00\x00\x00????\x00\x00\x00\x00?\x00??\x00\x20\x04\x20\x00\x00\x00\x00
——————————–

Muy bien ya estamos como para empezar a hurgar en nuestro disco…

foremost -d -i /dev/xXXXN -o /recover/output/

Ahora viene uno de nuestros grandes problemas, el header que nos inventamos se repite mucho dentro de una tabla, porque es el header del heap page, así que si teníamos un archivo como de 1GB nos hará varios archivos de 97m (por ejemplo). Así que nos toca trabajar con esto. Primero que nada hay que modificar el postgres (yo usé la versión 9.5) para agregar la funcion heap_page_tuples_attrs. Al parche lo pueden conseguir de acá:
A mi me tocó modificarlo y aplicarlo a mano, suerte con eso, pero no me quedé con una copia limpia como para darles. Pueden bajarse el original de este post.

Ahora podemos crear nuestra tabla con un create en limpio y reemplazar el filenode con uno que recuperó foremost, claro, nada funcionará excepto esto:

select * from heap_page_tuples_attrs('tabla',0);

Eureca!!!! un poco de luz al final del camino, pero, sin ánimos de desanimarlos… falta muuuuchoooo. Sigamos un poco más.

Por cada tipo de datos necesitaremos una función, les dejo acá las que usé para varchar, fecha, hora, real, y double.
El tema con los datos numéricos (y los de tiempo) es que en mi caso estaban en big endian, lo que significa que hay que cambiar todo el orden de los bytes primeros antes de poder trabajar. Luego hay que seguir el estándar IEEE 754 1985, sí, ese que aprendiste en la facultad de potencia y mantisa que siempre pensaste, esto no me va a servir para nada en la put@ vida… bienvenido a la put@ vida. Acá les dejo el link al estándar, por si les hiciera falta una ayuda de memoria.

Acá mis babys:


CREATE OR REPLACE FUNCTION getMaxPage(tabe text) RETURNS int AS $$
DECLARE
lp int;
BEGIN
FOR i IN REVERSE 1000..1 LOOP
--RAISE NOTICE 'i %',i;
BEGIN
lp:= (select lp_len from heap_page_tuples_attrs(tabe, i) limit 1);
if lp > 16 and lp < 500 then
return i;
end if;
EXCEPTION
            WHEN data_corrupted THEN --RAISE NOTICE 'tranqui';
			when internal_error THEN --RAISE NOTICE 'tranqui';
end;
end loop;
END;
$$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION real2date(tiempo bytea) RETURNS text AS $$
DECLARE
BEGIN

return  date('2000-01-01')+(floor(round((bit2real(tiempo)-1)*100000000000000)/11920929))::int+1;
	
END;
$$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION real2hhmmss(tiempo bytea) RETURNS text AS $$
DECLARE
msegundos bigint;
tmpseg bigint;
segundos text;
minutos text;
hora text;
BEGIN
msegundos:=trunc((bit2double(tiempo)-1)*10000000000000);
if msegundos::text = '-10000000000000' then
return '00:00:00';
end if;
tmpseg:=msegundos/2220.45;
--RAISE NOTICE 'msegundos %',msegundos;
--RAISE NOTICE 'tmpseg %',tmpseg;
hora = floor(tmpseg / 3600);
if char_length(hora)=1 then
hora := '0' || hora;
end if;
--RAISE NOTICE 'hora %',hora;
minutos = floor(tmpseg / 60)::int %60;
if char_length(minutos)=1 then
minutos := '0' || minutos;
end if;
--RAISE NOTICE 'minutos %',minutos;
segundos = tmpseg::int %60;
if char_length(segundos)=1 then
segundos := '0' || segundos;
end if;
--RAISE NOTICE 'segundos %',segundos;

return hora||':'||minutos||':'||segundos;	
	
END;
$$
LANGUAGE plpgsql;



CREATE OR REPLACE FUNCTION bit2real(doblehex bytea) RETURNS double precision AS $$
DECLARE
sign bigint;
binary_value text;
exponent text;
mantissa text;
byte_array bytea[4];
mantissa_index int;
exp_index int;
exp int;
potencia int;
vbdoble varbit;
result real;
BEGIN
vbdoble := get_byte(doblehex,3)::bit(8) || get_byte(doblehex,2)::bit(8) || get_byte(doblehex,1)::bit(8) || get_byte(doblehex,0)::bit(8);
IF vbdoble = '00000000000000000000000000000000' OR vbdoble = '10000000000000000000000000000000' THEN -- IEEE754-1985 Zero
        return 0.0;
END IF;

sign := substring(vbdoble from 1 for 1);
exponent := substring(vbdoble from 2 for 8);
mantissa := substring(vbdoble from 10);
exp_index:=1;
potencia=7;
-- RAISE NOTICE 'bin exponente %',substring(vbdoble from 2 for 11);
-- RAISE NOTICE 'bin mantisa %',substring(vbdoble from 13);
exp:=0;
WHILE exp_index < 12 LOOP
        IF substring(exponent from exp_index for 1) = '1' THEN
            exp := exp + power(2, potencia);
        END IF;
        exp_index := exp_index + 1;
		potencia := potencia -1;
END LOOP;
--RAISE NOTICE 'exponente %',exp;

IF exp > 126 THEN
   exp := exp - 127;
  ELSE
   exp:= -exp;
  END IF;


mantissa_index:=1;
result:=0;
WHILE mantissa_index < 52 LOOP
        IF substring(mantissa from mantissa_index for 1) = '1' THEN
            result := result + power(2, -(mantissa_index));
        END IF;
        mantissa_index := mantissa_index + 1;
END LOOP;
-- RAISE NOTICE 'mantissa %',result;
result := (1+ result) * power(2, exp);

IF(sign = '1') THEN
	result = -result;
END IF;

return result;	
	
END;
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION bit2double(doblehex bytea) RETURNS double precision AS $$
DECLARE
sign bigint;
binary_value text;
exponent text;
mantissa text;
byte_array bytea[8];
mantissa_index int;
exp_index int;
exp int;
potencia int;
vbdoble varbit;
result double precision;
BEGIN
vbdoble := get_byte(doblehex,7)::bit(8) || get_byte(doblehex,6)::bit(8) || get_byte(doblehex,5)::bit(8) || get_byte(doblehex,4)::bit(8) || get_byte(doblehex,3)::bit(8) || get_byte(doblehex,2)::bit(8) || get_byte(doblehex,1)::bit(8) || get_byte(doblehex,0)::bit(8);
IF vbdoble = '0000000000000000000000000000000000000000000000000000000000000000' OR vbdoble = '1000000000000000000000000000000000000000000000000000000000000000' THEN -- IEEE754-1985 Zero
        return 0.0;
END IF;

sign := substring(vbdoble from 1 for 1);
exponent := substring(vbdoble from 2 for 11);
mantissa := substring(vbdoble from 13);
exp_index:=1;
potencia=10;
-- RAISE NOTICE 'bin exponente %',substring(vbdoble from 2 for 11);
-- RAISE NOTICE 'bin mantisa %',substring(vbdoble from 13);
exp:=0;
WHILE exp_index < 12 LOOP
        IF substring(exponent from exp_index for 1) = '1' THEN
            exp := exp + power(2, potencia);
        END IF;
        exp_index := exp_index + 1;
		potencia := potencia -1;
END LOOP;
RAISE NOTICE 'exponente %',exp;

IF exp > 1022 THEN
   exp := exp - 1023;
  ELSE
   exp:= -exp;
  END IF;


mantissa_index:=1;
result:=0;
WHILE mantissa_index < 52 LOOP
        IF substring(mantissa from mantissa_index for 1) = '1' THEN
            result := result + power(2, -(mantissa_index));
        END IF;
        mantissa_index := mantissa_index + 1;
END LOOP;
-- RAISE NOTICE 'mantissa %',result;
result := (1+ result) * power(2, exp);

IF(sign = '1') THEN
	result = -result;
END IF;

return result;	
	
END;
$$
LANGUAGE plpgsql;

Bueno... seguro que ya se deben hacer una idea de lo que toca hacer ahora. Necesitamos un script que:
1) copie los archivos a las tablas "rotas"
2) reinicie el postgres
3) llame a un procedimiento que arregle todas las tablas

Bueno, primero el procedimiento que cura las tablas, sería así para mi caso, tómenlo de ejemplo solamente:

CREATE OR REPLACE FUNCTION migratabla() RETURNS void AS $$
DECLARE
t text;
tablas text[];
page int;
BEGIN
tablas:=ARRAY['db_satguard','db_sisspa','db_tech','db_teleplus','db_waytrack','db_worldtrack'];
FOReach t IN  ARRAY tablas LOOP


BEGIN
page:=getMaxPage(t);
if(page)>0 then
RAISE NOTICE 't %',t;
	FOR i IN REVERSE page..1 LOOP
	execute 'INSERT into ' || t || '_gis (gps_id_dispositivo, gps_vehiculo,gps_fecha,gps_hora,gps_status,gps_speed,gps_course,gps_magn_variation,gps_magn_var_direction,gps_event,gps_tstamp,gps_ubicacion,gps_latitud,gps_longitud,the_geom,gps_send_event) select 
substring(encode(t_attrs[2],''escape'') from 2),
substring(encode(t_attrs[3],''escape'') from 2),
real2date(t_attrs[4])::date,
real2hhmmss(t_attrs[5])::time,
substring(encode(t_attrs[6],''escape'') from 2),
bit2real(t_attrs[7]),
bit2real(t_attrs[8]),
bit2real(t_attrs[9]),
substring(encode(t_attrs[10],''escape'') from 2),
substring(encode(t_attrs[11],''escape'') from 2),
(real2date(t_attrs[4]) || '' '' || real2hhmmss(t_attrs[5]))::timestamp,
substring(encode(t_attrs[13],''escape'') from 2),
bit2double(t_attrs[14]),
bit2double(t_attrs[15]),
ST_geomfromtext(''POINT(''|| bit2double(t_attrs[15]) || '' '' || bit2double(t_attrs[14]) ||'')''),
bit2real(t_attrs[17])
from heap_page_tuples_attrs(''' || t || ''', ' || i || ' )
ON CONFLICT DO NOTHING
;';
	end loop;
end if;
EXCEPTION
            WHEN function_executed_no_return_statement THEN --RAISE NOTICE 'tranqui';
			when internal_error THEN --RAISE NOTICE 'tranqui';
end;
end loop;
END;
$$
LANGUAGE plpgsql;

Ahora mi script de bash:

for i in *; do 
cp $i /recover/5435/base/16384/17832; 
cp $i /recover/5435/base/16384/17797; 
cp $i /recover/5435/base/16384/17782; 
cp $i /recover/5435/base/16384/17768; 
cp $i /recover/5435/base/16384/17835; 
/usr/lib/postgresql/9.3/bin/pg_ctl -D /recover/5435/ -l /recover/5435/logfile restart -m inmediate;
sleep 0.5s;
echo "select migratabla();"| psql -p 5432 -h 127.0.0.1 recover; 
echo listo $i; 
mv $i pasados/
done;

Bueno, no está todo lo explicado que me gustaría, pero acabo de terminar de hacerlo y dejé el script corriendo, quería poner todo antes de que me olvide de algo. Esto básicamente funciona porque estamos usando postgres para dumpear el raw data de las páginas, y de ahí las parseamos en su formato. Esto es lo más bajo nivel que se puede llegar a trabajar en el disco, así que si con esto no salvan sus datos... lo siento, pero sus datos no están.

Espero que le salve la vida a mas de un admin, y bueno, si no me llaman que intento ayudarlos.
Saludos ?

espacios en el for de bash

December 20th, 2016

Esto es algo que me pasó millones de veces y al fin le encontré la solución definitiva.

 

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in *
do
  echo "$f"
done
IFS=$SAVEIFS

Espero les sirva, ojo que se viene un súper post de recuperación de datos en postgres. Un locura.

Hospedaje gis

March 21st, 2016

Esto es un post “patrocinado” por mi mismo, ya que soluciones root es mi empresa y sólo quiero posicionar a mi nuevo dominio gis hosting mejor en google. En la página www.gishosting.net va a ser en un futuro no muy lejano exclusivamente para contenido gis como mapserver, postgis, postgres, geoserver y otras tantas tecnologías todo orientado al hosting, espero les sea provechoso. Saludos.

túnel reverso con netcat

November 26th, 2015

Bueno, todo muy lindo con el ssh, pero… que pasa si lo que queremos es hacer lo mismo con un apache (u otro puerto) que corre, por ejemplo, en el puerto 80… pues bueno, también se puede de la mano de netcat.. la victorinox de intrnet.
Read the rest of this entry »