Archivo

Entradas Etiquetadas ‘archivos’

C.I. XVI: Hashtables VS Arrays, Matemáticas de Borges, renombrar archivos masivamente en Linux, Ejemplos de Crontab, Linux 3.2

Sábado, 7 de Enero de 2012 Gaspar Fernández Sin comentarios

Estos días, aunque inactivos, he seleccionado algunos enlaces que me han parecido interesantes:

Listar archivos dentro de un directorio o carpeta en C

Martes, 27 de Septiembre de 2011 Gaspar Fernández 4 comentarios

Árbol de directoriosNuestro software debe buscar archivos dentro de un directorio determinado, ya sea un archivo especial, una recopilación de datos del disco duro, una búsqueda de plugins, etc…

Hemos elegido lenguaje C para hacer esto, porque en bash podemos llamar a ls o find y apaga y vámonos.

Estas funciones, nos recordarán al uso de archivos con fopen() y fclose().

Para usar esta implementación, como veremos en el ejemplo, debemos incluir <sys/types.c> y <dirent.h> y, en principio, empezaremos con un ejemplo sencillo que lista los archivos del directorio actual (una vez tenemos el nombre y la ruta del archivo podemos hacer con él lo que queramos):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);
/* Función que hace algo con un archivo */
void procesoArchivo(char *archivo);

int main()
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;

  /* Empezaremos a leer en el directorio actual */
  dir = opendir (".");

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */
  /* Leyendo uno a uno todos los archivos que hay */
  while ((ent = readdir (dir)) != NULL)
    {
      /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */
      procesoArchivo(ent->d_name);
    }
    }
  closedir (dir);

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

void procesoArchivo(char *archivo)
{
  /* Para "procesar", o al menos, hacer algo con el archivo, vamos a decir su tamaño en bytes */
  /* para ello haremos lo que vemos aquí: http://totaki.com/poesiabinaria/2010/04/tamano-de-un-fichero-en-c/ */
  FILE *fich;
  long ftam;

  fich=fopen(archivo, "r");
  if (fich)
    {
      fseek(fich, 0L, SEEK_END);
      ftam=ftell(fich);
      fclose(fich);
      /* Si todo va bien, decimos el tamaño */
      printf ("%30s (%ld bytes)\n", archivo, ftam);
    }
  else
    /* Si ha pasado algo, sólo decimos el nombre */
    printf ("%30s (No info.)\n", archivo);
}

Este es un ejemplo sencillo, podemos profundizar un poco más. Vemos que readdir() nos devuelve todo lo que hay, y a veces, es muy útil diferenciar entre un fichero, un directorio, un enlace, un socket, un fifo, etc… para eso, en un principio utilizaremos la información del mismo dirent, que a veces la encontramos de forma rápida, aunque existen sistemas de archivos que no nos facilitan esa información de forma tan inmediata y tenemos que hacer stat(), un ejemplo de ello es nfs, ya que es significativamente más lento obtener esa información, si no la necesitamos, no intenta obtenerla, es para esos casos cuando obligamos a su obtención con stat().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);

/* Calculamos el tamaño del archivo */
long fileSize(char *fname);

/* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
unsigned char statFileType(char *fname);

/* Función que hace algo con un archivo, pero le pasamos el dirent completo, usaremos más datos */
void procesoArchivo(char *ruta, struct dirent *ent);

int main(int argc, char *argv[])
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;

  if (argc != 2)
    {
      error("Uso: ./directorio_2 <ruta>\n");
    }
  /* Empezaremos a leer en el directorio actual */
  dir = opendir (argv[1]);

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */
  /* Leyendo uno a uno todos los archivos que hay */
  while ((ent = readdir (dir)) != NULL)
    {
      /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */
      procesoArchivo(argv[1], ent);
    }
    }
  closedir (dir);

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

long fileSize(char *fname)
{
  FILE *fich;
  long ftam=-1;

  fich=fopen(fname, "r");
  if (fich)
    {
      fseek(fich, 0L, SEEK_END);
      ftam=ftell(fich);
      fclose(fich);
    }
  else
    printf("ERRNO: %d - %s\n", errno, strerror(errno));
  return ftam;
}

void procesoArchivo(char *ruta, struct dirent *ent)
{
  long ftam;
  char *nombrecompleto;
  char strtam[20];
  char strtipo[30]="";
  /* Tiene que ser del mismo tipo de dirent.d_type en nuestro sistema */
  static unsigned char tipoID[7]={DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK};
  static char* tipoSTRs[7]={"Dispositivo de bloques", "Dispositivo de caracteres", "Directorio", "FIFO", "Enlace", "Archivo regular", "Socket Unix"};

  int i;
  int tmp;
  unsigned char tipo;

  /* Sacamos el nombre completo con la ruta del archivo */
  tmp=strlen(ruta);
  nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */
  if (ruta[tmp-1]=='/')
    sprintf(nombrecompleto,"%s%s", ruta, ent->d_name);
  else
    sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name);

  /* Calcula el tamaño */
  ftam=fileSize(nombrecompleto);
  if (ftam>=0)
    sprintf(strtam, "%ld bytes", ftam);
  else
    strcpy(strtam, "No info");

  /* A veces ent->d_type no nos dice nada, eso depende del sistema de archivos que estemos */
  /* mirando, por ejemplo ext*, brtfs, sí nos dan esta información. Por el contrario, nfs */
  /* no nos la da (directamente, una vez que hacemos stat sí lo hace), y es en estos casos donde probamos con stat() */
  tipo=ent->d_type;
  if (tipo==DT_UNKNOWN)
    tipo=statFileType(nombrecompleto);

  if (tipo!=DT_UNKNOWN)
    {
      /* Podíamos haber hecho un switch con los tipos y devolver la cadena,
         pero me da la impresión de que así es menos costoso de escribir. */

      i=0;
      while ( (i<7) && (tipo!=tipoID[i]) )
    ++i;

      if (i<7)
    strcpy(strtipo, tipoSTRs[i]);
    }

  /* Si no hemos encontrado el tipo, éste será desconocido */
  if (strtipo[0]=='\0')
    strcpy(strtipo, "Tipo desconocido");

  printf ("%30s (%s)\t%s \n", ent->d_name, strtam, strtipo);

  free(nombrecompleto);
}

/* stat() vale para mucho más, pero sólo queremos el tipo ahora */
unsigned char statFileType(char *fname)
{
  struct stat sdata;

  /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
  if (stat(fname, &sdata)==-1)
    {
      return DT_UNKNOWN;
    }


  switch (sdata.st_mode & S_IFMT)
    {
    case S_IFBLK:  return DT_BLK;
    case S_IFCHR:  return DT_CHR;
    case S_IFDIR:  return DT_DIR;
    case S_IFIFO:  return DT_FIFO;
    case S_IFLNK:  return DT_LNK;
    case S_IFREG:  return DT_REG;
    case S_IFSOCK: return DT_SOCK;
    default:       return DT_UNKNOWN;
    }
}

Un ejemplo más grande, contamos los archivos que hay en un árbol de directorios. Uno a uno, pero de forma recursiva (cuando encontramos un directorio nos meteremos dentro y llamaremos de nuevo a la función principal (cuentaArchivos()):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

/* Función para devolver un error en caso de que ocurra */
void error(const char *s);

/* Calculamos el tamaño del archivo */
long fileSize(char *fname);

/* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */
unsigned char statFileType(char *fname);

/* Intenta sacar el tipo de archivo del ent */
unsigned char getFileType(char *ruta, struct dirent *ent);

/* Obtiene el nombre del fichero con la ruta completa */
char *getFullName(char *ruta, struct dirent *ent);

/* Genera una cadena de espacios, para dibujar el árbol */
char *generaPosStr(int niv);

/* Función principal, que cuenta archivos */
unsigned cuentaArchivos(char *ruta, int niv);

int main(int argc, char *argv[])
{
  unsigned num;

  if (argc != 2)
    {
      error("Uso: ./directorio_2 <ruta>\n");
    }
  printf("Entrando en: %s\n", argv[1]);
  num=cuentaArchivos(argv[1], 1);
  printf("%s . Total: %u archivos\n", argv[1], num);
  /* Empezaremos a leer en el directorio actual */

  return EXIT_SUCCESS;
}

void error(const char *s)
{
  /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */
  perror (s);
  exit(EXIT_FAILURE);
}

char *getFullName(char *ruta, struct dirent *ent)
{
  char *nombrecompleto;
  int tmp;

  tmp=strlen(ruta);
  nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */
  if (ruta[tmp-1]=='/')
    sprintf(nombrecompleto,"%s%s", ruta, ent->d_name);
  else
    sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name);
 
  return nombrecompleto;
}

char *generaPosStr(int niv)
{
  int i;
  char *tmp=malloc(niv*2+1);    /* Dos espacios por nivel más terminador */
  for (i=0; i<niv*2; ++i)
    tmp[i]=' ';
  tmp[niv*2]='\0';
  return tmp;
}

unsigned cuentaArchivos(char *ruta, int niv)
{
  /* Con un puntero a DIR abriremos el directorio */
  DIR *dir;
  /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */
  struct dirent *ent;
  unsigned numfiles=0;          /* Ficheros en el directorio actual */
  unsigned char tipo;       /* Tipo: fichero /directorio/enlace/etc */
  char *nombrecompleto;     /* Nombre completo del fichero */
  char *posstr;         /* Cadena usada para posicionarnos horizontalmente */
  dir = opendir (ruta);

  /* Miramos que no haya error */
  if (dir == NULL)
    error("No puedo abrir el directorio");
 
  while ((ent = readdir (dir)) != NULL)
    {
      if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) )
    {
      nombrecompleto=getFullName(ruta, ent);
      tipo=getFileType(nombrecompleto, ent);
      if (tipo==DT_REG)
        {
          ++numfiles;
        }
      else if (tipo==DT_DIR)
        {
          posstr=generaPosStr(niv);
          printf("%sEntrando en: %s\n", posstr, nombrecompleto);         
          printf("%s%s . Total: %u archivos ", posstr, nombrecompleto, cuentaArchivos(nombrecompleto, niv+1));
          /* Podemos poner las líneas que queramos */
          printf("\n");
          free(posstr);
        }
      free(nombrecompleto);
    }
    }
  closedir (dir);
 
  return numfiles;
}

unsigned char getFileType(char *nombre, struct dirent *ent)
{
  unsigned char tipo;

  tipo=ent->d_type;
  if (tipo==DT_UNKNOWN)
    {
      tipo=statFileType(nombre);
    }

  return tipo;
}

/* stat() vale para mucho más, pero sólo queremos el tipo ahora */
unsigned char statFileType(char *fname)
{
  struct stat sdata;

  /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */
  if (stat(fname, &sdata)==-1)
    {
      return DT_UNKNOWN;
    }

  switch (sdata.st_mode & S_IFMT)
    {
    case S_IFBLK:  return DT_BLK;
    case S_IFCHR:  return DT_CHR;
    case S_IFDIR:  return DT_DIR;
    case S_IFIFO:  return DT_FIFO;
    case S_IFLNK:  return DT_LNK;
    case S_IFREG:  return DT_REG;
    case S_IFSOCK: return DT_SOCK;
    default:       return DT_UNKNOWN;
    }
}

Se puede descargar todo el código fuente desde aquí: Listado de archivos dentro de un directorio en C (3.5Kb)

Actualización (14/10/2011): En el blog código para llevar encontramos un ejemplo de cómo utilizar este código para encontrar el número de núcleos de nuestra CPU.

Guía de supervivencia para disfrutar de Linux Mint Debian Edition XFCE (I : Instalación)

Viernes, 9 de Septiembre de 2011 Gaspar Fernández 19 comentarios

Me he decidido a escribir esta guía tras hablar con varios de mis alumnos de clases particulares. Siempre les intento explicar de las ventajas de un sistema operativo GNU/Linux y les animo a probarlo. Aunque la instalación generalmente no sea difícil (en las distribuciones tipo Fedora/Mandriva/Ubuntu/Linux Mint/…) siempre es algo nuevo, y supone una gran cantidad de información nueva para una persona que no ha tenido experiencia en estos sistemas, lo cual puede llegar a saturar a cualquiera. Es cierto, que para una primera toma de contacto es conveniente tener una mano amiga cerca para guiarnos durante el proceso, al menos la primera vez.

Las razones por las que he cogido Linux Mint Debian Edition XFCE son:

  • Porque es una distribución que uso, y me gusta
  • Porque muchas de las personas que van a tener una primera experiencia con Linux, suelen hacerlo en un ordenador más antiguo que el principal, y es bueno que la distribución esté optimizada y no consuma demasiados recursos
  • Porque aún hay cosas que tenemos que hacer en terminal, puede parecer un rollo, puede parecer retro, pero tenemos en nuestras manos una de las herramientas más potentes de la informática, disponible para cualquier ordenador, y para todos, sólo tenemos que tocar las teclas adecuadas
  • Porque me parece que tiene una relación velocidad/estabilidad muy buena
  • Porque me parece una buena distribución para embarcarse en el mundo Linux, y para aprender a utilizar el sistema (si queremos ser heavies podemos empezar con Gentoo, pero el usuario medio, incluso con interés puede terminar abandonando).
  • Porque, por todos estos motivos quiero colaborar en su difusión.

¿Qué podemos encontrar en esta primera guía?

  1. Descarga y copia en un soporte externo.
  2. Arranque e inicio de la instalación
  3. Configurar idioma, zona horaria y distribución de teclado
  4. Configurar las particiones
  5. Unos datos más previos a la instalación
  6. Para sentirse como pez en el agua…
  7. Notas finales

Descarga y copia en un soporte externo.

Antes de embarcarnos en la instalación debemos descargarnos la distribución (de forma libre y gratuita), desde algún mirror oficial. Debemos acceder a la descarga en 32bit o 64bit dependiendo de nuestra arquitectura y memoria RAM. Casi todos los procesadores nuevos (de 3 años hasta la actualidad) soportan 64bit (aunque al comprar el ordenador pudiera venir con una versión de 32bit de Windows, eso es lo de menos. Algo muy recomendable es que, independientemente de que nuestro ordenador soporte 64bit, si tenemos menos de 2Gb de RAM es mejor instalar la versión de 32bit.

Una vez descargado el archivo ISO (la imagen de CD o DVD), debemos grabarla en un disco o en una unidad USB flash o pendrive.

Pendrive PingüinoPara la primera opción, si estamos en Windows podemos utilizar alguna de estas utilidades, aunque podemos acceder a esta dirección, donde explican cómo hacerlo con una imagen de Ubuntu.

Si lo que queremos, para no gastar discos es hacerlo desde un pendrive, primero debemos asegurarnos de no tener nada importante dentro, ya que lo más recomendable es formatearlo y ejecutar la utilidad unetbootin, multi-plataforma, y muy fácil de usar.

Arranque e inicio de la instalación

Tal vez, nuestro ordenador cuando arranca, lea de primeras el CD o el puerto USB en busca de un soporte de arranque antes de iniciar el sistema operativo que tenemos instalado, con lo cual no tendríamos que preocuparnos.

Pero en ocasiones esto no es así. Normalmente existe una tecla con la que podemos seleccionar el dispositivo de arranque con un pequeño menú en pantalla (Escape, F1, F2, F5, F8, F10, F12, generalmente es alguna de estas, si no sale el logotipo de la marca normalmente pondrá en la pantalla qué tecla hay que pulsar), o pulsando Del (o Suprimir) para entrar en la configuración de la BIOS donde podremos seleccionar la secuencia de arranque (Boot Order / Boot Sequence) y pulsar F10 (casi siempre, pero mejor, leer la pantalla, que seguro que viene cómo hacerlo) para salvar cambios.

Al iniciar, arrancamos el menú con la opción por defecto y esperamos a que esté totalmente cargado antes de pulsar el icono Install Linux Mint y empezar con la instalación.

Configurar idioma, zona horaria y distribución de teclado

Linux Mint Debian Edition (Configurar idioma)Empezamos con la instalación en serio, y primero vamos a seleccionar nuestro idioma. En mi caso Español (España), aunque la configuración de idioma la terminaremos después de la instalación.

Una vez seleccionado el idioma, pulsamos sobre el botón Forward (Continuar).

LMDE Zona horaria

El siguiente paso será seleccionar la zona horaria. Buscamos el continente y la ciudad más cercana con la misma hora que la nuestra.

Y continuamos (Forward).

LMDE tecladoComo en las pantallas anteriores, aquí seleccionamos nuestra distribución de teclado. Normalmente la que viene por defecto está bien; en el cuadro de abajo podemos probar si el teclado está bien configurado escribiendo letras especiales y símbolos que suelen ser caracteres conflictivos y no en todos los teclados están distribuidos de la misma forma.

Configurar las particiones

Debemos distribuir en nuestro disco duro nuestra nueva instalación. En este punto se decidirá si la distribución se instala sola en el disco duro o debe convivir con otras instalaciones, incluso con Windows.LMDE particiones ISi pulsamos sobre Edit Partitions, tendremos la capacidad de crear, eliminar,  y redimensionar particiones en nuestro sistema, aunque por el momento no voy a tocar esa parte, es un sistema más o menos intuitivo donde podemos ver, como en esta imagen gráficamente el disco duro, y el espacio ocupado. Por ejemplo, si tenemos una partición Windows que ocupa todo el disco, podemos hacerla más pequeña (mejor si hacemos una copia de seguridad antes, aunque nunca pasa nada, seguro que la ley de Murphy juega en nuestra contra si no la hacemos).

LMDE particiones II

En mi caso, yo he creado varias particiones:

  1. Una muy pequeña (de unos 100Mb más o menos) que será para el arranque
  2. Una para intercambio (swap) 2 Gb más o menos. Es importante darle más tamaño que nuestra memoria RAM, ya que si hibernamos el equipo necesitaremos copiar toda nuestra RAM en el disco duro, y puede que necesitemos algo más para trabajar. Además de para hibernar, hablando mal y pronto esta partición nos servirá como RAM cuando ya tenemos llena la RAM.
  3. Partición raíz (yo suelo dar algo más de 1Gb, por lo que pueda pasar con respecto a archivos temporales), aunque no suele tener más de 400Mb siguiendo este sistema de particionado que propongo…
  4. NOTA: Si tienes disco duro libre, estaría bien crear una partición para archivos temporales, sobre todo si vamos a grabar DVDs, podremos almacenar las imágenes ISO de los discos temporalmente en esa partición.
  5. Una partición para los programas. En mi caso con 7.5Gb está bien, nos da para instalar muchos programas y nos sobrará disco, aunque si instalamos juegos o programas muy grandes tal vez se nos quede corta, habrá que ver el uso que vamos a hacer de nuestro Linux.
  6. Una partición de 2Gb para datos variables, estos serán los archivos de instalaciones de paquetes, colas de impresión, informes de sistema (logs) y algunas cosas más.
  7. La partición más grande de todas, para los directorios personales de los usuarios, donde éstos almacenarán sus datos, su configuración, sus descargas, etc.

Una vez hemos terminado de definir las particiones, procesamos los cambios y cerramos la ventana.

El siguiente paso es definir los puntos de montaje y cómo vamos a formatear nuestras particiones.

LMDE Particiones 3 (boot)Asignaremos a cada una de las particiones, un formato y un punto de montaje (este es el que determinará la misión de la partición en nuestro sistema, y será el punto desde el que podremos acceder a ella):

  1. La partición pequeña de arranque, se montará en /boot y será de tipo ext2
  2. A la partición swap no hará falta hacerle nada.
  3. La partición raíz se montará en / y será de tipo ext4
  4. La pertición de programas se montará en /usr y será de tipo ext4
  5. La partición de datos variables se montará en /var y será de tipo ext4
  6. La partición de datos de usuario se montará en /home ; si tienes una instalación anterior y no quieres perder los datos, no debes especificar el tipo, ya que se formatearía, lo dejamos tal cual.
  7. Si tienes  una partición para datos temporales, móntala en /tmp y dale de tipo ext4
  8. Si tienes particiones de una instalación de Windows anterior, puedes dejarlas sin montar, o darles un punto de montaje, por ejemplo /mnt/Windows , /mnt/Documentos_Windows  y no darle tipo, ya que se formatearía como indiqué en el punto 6. Linux Mint se encarga de detectar el tipo de formato de esa partición.

06_particiones_iv_todas

Una vez finalizado el particionado, pulsamos Forward de nuevo.

Unos datos más previos a la instalación

LMDE Nombre y algo másAntes de terminar, debemos especificar nuestro nombre (sólo para que se vea en pantalla, que también puede ser un alias), nuestro nombre de usuario, con el que entraremos al sistema; la contraseña y un nombre para el equipo: vamos sed originales y bautizad vuestro ordenador, empecemos a darle personalidad !!

LMDE GrubDesde esta ventana se instalará el gestor de arranque, un menú que se cargará antes que el sistema operativo y nos permitirá elegir  con qué sistema arrancar, también tendrá una opción de rescate.

LMDE resumenComo última ventana antes de la instalación veremos un resumen de lo que vamos a hacer, debemos prestar especial atención a las particiones , sobre todo si hay alguna que no queramos formatear, porque todavá no se han realizado los cambios (excepto si hemos creado, eliminado o redimensionado alguna partición). Si queremos mantener nuestra partición de Windows, debemos ver que se va a montar (Mount) en algún sitio (aunque no es obligatorio), pero bajo ningún concepto será formateada.

Para sentirse como pez en el agua…

Iniciamos la instalación, a partir de ahora va solo… se formateará, copiarán los archivos y se configurará de forma básica el sistema.

LMDE formateando

Uhmmm… ¿Nunca has instalado una distribución de Linux? Al poco tiempo de estar con el sistema, verás que puedes probar cómo funciona desde el mismo disco de instalación, lo que significa que, mientras se instala (aunque no tarde mucho), podrás navegar por Internet, escuchar música (de tu disco duro, por ejemplo) y hacer mil cosas más, sólo hay que echar un vistazo a los progrmas disponibles. Esto no es exclusivo de Linux Mint, muchas distribuciones lo permiten desde hace mucho tiempo… ¿eso no te lo permite Windows, verdad?

Por ejemplo, podemos configurar la red para navegar un rato por Facebook… (aunque esta configuración no se mantendrá una vez instalado el sistema)

LMDE configurar red
Pasados unos 15 minutos tendremos un sistema Linux Mint Debian Edition operativo; reiniciaremos el sistema, ¡listo!

LMDE reinicio

Notas finales

Una vez hayamos terminado la instalación es recomendable actualizar la distribución. Para ello, nos dirigimos al icono arriba a la derecha, como un escudo, junto al icono de la batería, aunque en la captura no aparecen (yo ya actualicé), nada más instalarla aparecerán algunos paquetes para actualizar.

LMDE Actualización

¡ Ya podéis disfrutar de un nuevo sistema !

Sed… de venganza (1): Sustituyendo cadenas en múltiples archivos

Lunes, 28 de Junio de 2010 Gaspar Fernández 1 comentario

3846929292_60721fb24e

Es uno de los grandes desconocidos y tan temidos comandos de que disponemos. Y es cierto que a veces da pereza mirarse el manual cuando queremos hacer algo que sed podría hacer rápidamente.

Lo que cuento hoy es su uso más popular (porque sed se puede usar para muuuuuchas cosas) y es muy simple, sustituir en un stream un texto por otro (Donde dije digo, digo Diego).

Imaginemos un fichero de texto, para ser originales llamémosle README, y en el texto queremos cambiar la palabra “Ireland” por “Spain”. Podemos hacer lo siguiente:

$ sed ’s/Ireland/Spain/g’ README

y veremos en pantalla el texto. Para guardarlo en el mismo archivo, si buscamos por Internet, veremos cómo la gente se complica la vida (que no digo que sea malo, yo también me la complico un poco más abajo), pero podemos usar el modificador -i (que tiene algunas funciones curiosas).

Si ahora hacemos:

$ sed -i ’s/Ireland/Spain/g’ README

Los cambios se guardarán automáticamente en el fichero README. Pero si investigamos un poco más, y queremos rematar la faena podemos hacer:

$ sed -i~ ’s/Ireland/Spain/g’ README

En este caso, guardaremos el cambio en el fichero README y grabaremos en README~ una copia de seguridad del fichero antiguo. La extensión de la copia de seguridad podemos cambiarla haciendo por ejemplo:

$ sed -i.bak ’s/Ireland/Spain/g’ README

Para que la copia sea README.bak

Pero ahora viene algo interesante, con el parámetro -i podemos modificar todos los archivos que queramos, sed acepta en la entrada múltiples archivos, por lo que si en un directorio con muchos archivos queremos cambiar un texto (imaginad que habéis hecho un proyecto relativamente grande, y hay una función con un nombre un poco ridículo, curiosamente es la que más veces llamáis, y como vamos a enseñar el código fuente, no queremos que nadie lea eso), podremos hacer:

$ sed -i ’s/nombre_ridiculo/nombre_elegante/g’ *

Ahora bien, si el proyecto está en múltiples directorios, siempre podemos usar find para localizar los archivos de la siguiente forma:

find -name ‘*.c’ -exec sed -i ’s/nombre_ridiculo/nombre_elegante/g’ {} \;

Con toda esta línea, buscaremos todos los archivos con extensión .c dentro del directorio actual y subdirectorios y se los pasaremos a sed, con el modificador -exec de find, ejecutaremos el comando que especificamos, donde {} indica el nombre de archivo (que nos lo da find) y con \; indicamos el fin del comando y sus parametros.

Pero esto no termina aquí, sed soporta expresiones regulares, y si por ejemplo queremos coger todas las imágenes de un fichero html, cambiarlas de directorio y añadirles un class (inicialmente encontramos <img src=”foto.jpg” alt=”" /> , y queremos que salga <img class=”imagen” src=”/static/foto.jpg” alt=”" />) podemos hacer lo siguiente:

sed ’s/src=”\(.[a-zA-Z\.\_\/]*\)”/src=”\/static\/\1” class=”imagen”/g’ fichero.html

Lo que hay en negrita, corresponde a la expresión regular que determina el nombre del archivo (caracteres de la a a la z, de la A a la Z, puntos, guiones bajos, y barras (en la cadena de origen), en la cadena de destino escribimos \1 donde queremos que coloque el texto correspondiente a la expresión anterior, es decir donde queremos que coloque el nombre del archivo.

Ni que decir tiene que podemos hacer una mezcla de todo lo dicho en este post ( expresiones regulares, y sustitución en múltiples archivos dentro de múltiples subdirectorios y guardando backups ), y estaremos delante de una potente herramienta.

Lo malo de ejecutar sed, es que tenemos que escapar muchos caracteres,por lo menos ), (, ., \, / y seguro que encontramos alguno más; para ello, siempre que queramos introducir un carácter de esos, debemos poner una contra barra (\) delante.

Foto: albertopveiga (Flickr)

Uso de llaves en BASH

Sábado, 15 de Mayo de 2010 Gaspar Fernández 1 comentario

llaves
Leo en el blog de Thalskarth (proveniente de Tux Files, que a su vez venía de Slice of Linux) un truco para hacer copias de seguridad de un archivo con bash de la siguiente forma:

cp archivo{,.bk}

Lo que hacemos es parecido a escribir esto otro:

cp archivo archivo.bk

Por lo que podemos intuir fácilmente para qué valen las llaves en este contexto: replicar alternativas. Es decir escribiremos lo que hay antes de la llave, y lo terminaremos con cada una de las opciones de dentro de las llaves que están separadas por comas. Y lo más fácil para entender esto es utilizar echo.
Probaremos lo siguiente:

$ echo “Voy a pintar mi casa de “{verde,azul,rojo,amarillo}
Voy a pintar mi casa de verde Voy a pintar mi casa de azul Voy a pintar mi casa de rojo Voy a pintar mi casa de amarillo

Bien, el mensaje se replica con un espacio entre réplicas. Podemos ahora probar algo más:

$ echo -e “Voy a pintar mi casa de “{verde,azul,rojo,amarillo}”.\n”
Voy a pintar mi casa de verde.
Voy a pintar mi casa de azul.
Voy a pintar mi casa de rojo.
Voy a pintar mi casa de amarillo.

Además, podemos ver que la llave no tiene por qué estar al final del parámetro, podemos ponerla en mitad y sigue funcionando. Hay un espacio al principio de la línea, como mencioné antes, las frases irán separadas por un espacio (es normal, las llaves nos sirven para introducir nuevos parámetros). Podremos solucionarlo con \b (backspace).

echo -e “\bVoy a pintar mi casa de “{verde,azul,rojo,amarillo}”.\n”
Voy a pintar mi casa de verde.
Voy a pintar mi casa de azul.
Voy a pintar mi casa de rojo.
Voy a pintar mi casa de amarillo.

Antes de nada un apunte, no debemos dejar espacios dentro de las llaves, ni entre las comas, ni dentro de cada opción, no olvidemos que son parámetros, aunque sí que podríamos poner un espacio si éste va entre comillas (igual que ocurre con los parámetros).

Crear PDFs

Y a partir de aquí, sólo necesitamos imaginación, por ejemplo podemos ver cómo lo usamos para crear un pdf con imágenes en jpeg (de una carpeta llamadas test-N.jpg). Lo creamos con ImageMagick y sólo queremos de la 1 a la 12:

$ convert test-{?.,10.,11.,12.}jpg todos.pdf

Aunque hay un método mejor, {} (las llaves) soportan rangos, por lo que podemos hacer:

$ convert test-{1..12}.jpg todos.pdf

Comprimir directorios

Podemos, por ejemplo crear un archivo comprimido de un directorio con el mismo nombre de la siguiente manera:

$ tar cvjf test{.tar.bz2,/}

como sustitución a:

$ tar cvjf test.tar.bz2 test/

Diferencias

Encontrar la diferencia de un archivo con su backup (suponemos que el backup es el mismo nombre terminado en ~ (tilde de la ñ):

$ diff test{,~}

O para diferencias rutas (recordemos que podemos poner llaves entre parámetros:

$ diff ~/proyectos/www/{proyecto1,proyecto2}/www/lib/my_lib.h

Que es lo mismo que:

$ diff ~/proyectos/www/proyecto1/www/lib/my_lib.h ~/proyectos/www/proyecto2/www/lib/my_lib.h

Lectura de datos en scripts

Para leer desde la entrada estandar tenemos read, y si queremos introducir cada palabra en una variable podemos usar read palabra1 palabra2 palabra3, y si queremos que estas variables tengan un prefijo común:

$ read palabra{A,B,C}

Combinaciones y juegos

Vamos a hacer múltiples sumas con bc:

$ echo -e {1..4}”+”{4..1}”\n” | bc

Esto pondrá en pantalla el resultado de: 1+4, 1+3, 1+2, 1+1, …, 4+3, 4+2, 4+1.
Con esto vemos que los rangos no sólo van en incremento sino también en decremento.
Pero aún más si hacemos:

echo test{a..z}

Nos completará con testa testb testc…testx, testy, testz

Por último, un ejemplo más complicado, y que, aunque pocas veces nos sirva (más que nada por no acordarnos), ahí queda, llaves anidadas:

$ echo test-{1{10..12},3{9..1}}
test-110 test-111 test-112 test-39 test-38 test-37 test-36 test-35 test-34 test-33 test-32 test-31

Tenemos la posibilidad de introducir opciones dentro de otras opciones y todas se representarán seguidas. ¿Tal vez nos sirva alguna vez para crear un fichero de texto con datos de prueba? Para crear un archivo con temperaturas de varias ciudades:

$ echo -e “\b”{”Madrid 1″{1..4},”Barcelona 1″{3..5},”Malaga “{19..22},”Sevilla 2″{3..4}}”\n” > test

Esto creará un fichero llamado test con el siguiente contenido:

Madrid 11
Madrid 12
Madrid 13
Madrid 14
Barcelona 13
Barcelona 14
Barcelona 15
Malaga 19
Malaga 20
Malaga 21
Malaga 22
Sevilla 23
Sevilla 24

Foto: bohman (Flickr)

Script para subir archivos rápidamente (y actualizarlos)

Jueves, 13 de Agosto de 2009 Gaspar Fernández 2 comentarios

Normalmente, cuando estoy desarrollando aplicaciones para Facebook, primero, las hago Offline, ejecutándolas en mi servidor local y, una vez que funcionan, las subo al servidor desde las que se ejecutarán, aunque muchas veces hay ciertos problemas una vez se está ejecutando la aplicación de forma definitiva, que requieren hacer algunas modificaciones más, y con esto, subir varias veces los archivos de los scripts.

Por otra parte, a veces, en ciertas aplicaciones web, es necesario introducir cierta información nueva (que hago fuera de línea) y cuando todas las novedades han sido introducidas procedo a subirlo todo junto.

También es importante, sobre todo para proyectos más o menos grandes, que sólo se suban al servidor los archivos nuevos o modificados antes de la última actualización, es muy importante, ya que la ejecución puede eternizarse si tenemos que subir 10Mb de datos cada vez que queremos actualizar, por lo tanto, almacenamos en un archivo el momento en que se ejecutó el script por última vez, y cada vez que buscamos archivos, lo hacemos con los que han sido actualizados a partir del momento que almacenamos en el fichero anterior.

Para eso creé este script (aún queda mucho trabajo por hacer), pero por ahora hace bastante bien el apaño. Yo llamo a este archivo (autoftp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/bash

servidor=$1
ruta_serv=$2
ruta_local=$3
endline="
"

dir_actual=`pwd`
cd $3

if [ -e $ruta_local"/last_update" ]; then
    f_locales=`find . -newer $ruta_local/last_update`
else
    f_locales=`find .`
fi

function cierratodo()
{
    kill $ftp_pid
    kdialog --msgbox $1
}

function estadoman()
{
    while read estado
    do
        estadonum=`echo $estado | cut -d' ' -f1`
        echo $estado
        if [[ $estado != "Not connected" ]]; then
            if [[ $estadonum == "226" ]]; then
                subidos=$(($subidos+1))
                kdialog --passivepopup "Subido archivo "$subidos" de "$total_archivos 2
            elif [[ $estadonum == "221" ]]; then
                cierratodo "Los archivos se subieron con éxito"
                date +%s > $ruta_local"/last_update"
            # Fin del programa
               
            fi
        else
            echo "Cerrandooo"
            cierratodo "La conexión se cerró inesperadamente"
        fi
    done
}

to_do="pass"$endline"cd $ruta_serv"$endline;
last_dir="";
total_archivos=0
for i in $f_locales
do
        arch_relat=${i:2}
        if [ ! -z $arch_relat ]; then
            nombre_dir=`dirname $arch_relat`
            if [[ $nombre_dir == "." ]]; then
                nombre_dir=""
            fi
                if [ -d $arch_relat ]; then
                    to_do=$to_do"cd $ruta_serv/$nombre_dir"$endline
                    to_do=$to_do"mkdir $ruta_serv/$arch_relat"$endline
                else
#                   echo "*"$nombre_dir"="$last_dir"*";
                    if [[ $last_dir != $nombre_dir ]]; then
                        last_dir=$nombre_dir
                        to_do=$to_do"cd $ruta_serv/$nombre_dir"$endline
                    fi

                    to_do=$to_do"put $ruta_local/$arch_relat "`basename $arch_relat`$endline
                    total_archivos=$(($total_archivos+1))
#                     to_do=$to_do"put $3/$arch_relat\n"
                   echo "FICHERO --- $arch_relat";
                fi
        fi
done

OLDIFS=$IFS
IFS="
"

to_do=$to_do"quit"$endline

if [[ $total_archivos > 0 ]]; then
    for j in $to_do ; do echo $j;done;

    rm /tmp/ftp_pipe > /dev/null 2>&1
    mkfifo /tmp/ftp_pipe > /dev/null 2>&1

ftp -v totaki.com > /tmp/ftp_pipe <<EOF &
`for j in $to_do ; do echo $j;done;`
EOF

    ftp_pid=$!

else
    kdialog --msgbox "No hay archivos para subir"
fi
cd $dir_actual
IFS=$OLDIFS

rm /tmp/ftp_pipe > /dev/null 2>&1

Para su uso, son necesarios tres parámetros:

  • Dirección del servidor
  • Ruta donde se colocan los archivos en el directorio remoto
  • Ruta de donde se buscarán los archivos en el directorio local

Requerimientos:

  • kdialog (Se puede modificar el script para utilizar otro fácilmente)
  • ftp (Este programa se encargará de subir los archivos)

Para configurar el script es necesario editar el archivo .netrc localizado en nuestro home y donde irá el nombre de usuario y contraseña del FTP (este fichero es leído por ftp automáticamente y así no necesitamos incluir passwords ni en la línea de comando ni en el propio script, que quedan muy feos). El fichero .netrc tiene la siguiente estructura:

machine [servidor] login [usuario] password [contraseña]

Por ejemplo podemos hacer un .netrc que contenga:

machine www.servidor.com login pepito password josefina

Una vez completado esto, podemos asociar una tecla al script, y con ello simplemente pulsando esa tecla se subirán los archivos al servidor.
Por último, yo lo tengo configurado en Fluxbox con Mod4+F5 o lo que es lo mismo la tecla de Windows y F5; basta con crear un script lanzador que incluya lo siguiente (yo lo llamo ftp_upload):

1
2
3
4
#!/bin/bash
# Modelo
#/home/gaspy/.scripts/autoftp [servidor] [ruta interna] [ruta_local]
#/home/gaspy/.scripts/autoftp ftp.servidor.com http_docs/pruebas /home/yomismo/proyectos/pruebas

añadir al fichero keys dentro de /home/[usuario]/.fluxbox/ la siguiente línea:

Mod4 F5 :execcommand ~/.scripts/ftp_upload

Visita otras webs de la red