~
~
:wq

Showing posts with label shell/bash. Show all posts
Showing posts with label shell/bash. Show all posts

Saturday, 27 June 2015

Passing STDIN or positional arguments to python code enbedded in bash script (here-documents)

spanish version - all english posts

This is a short post about bash's here-documents and python embedded scripts. Very useful to write quick and dirty scripts.

The issue here is to demonstrate how to pass either positional arguments or stdin input (pipe) to python code inside a bash's here-document block of code

Proposal, write a bash script or function that accepts either pipe input or positional parameters and pass them to a python block of code within the bash script.

First approach: ever wonder how to pass bash positional args to inside python here document? Here is the code:

#! /bin/bash

[... bash stuff ...]

# our python block
python - $@ << EOF
import sys
print sys.argv[:]
EOF

[... more bash stuff ...]

this is how it works:

bash firstapproach.sh one two
['-', 'one', 'two']

Let's go ahead... What if you want to accept ether standard input or positional args? this is IMHO a good approach to the problem (using a function to make the code clearer):

#! /bin/bash

[... bash stuff ...]

# the function which passes to our python here-document either
# positional args or STDIN input
# pipe input is only used if no positional args are present:
myfunct () {
[[ $# > 0 ]] && ARGS=$@ || read ARGS
python - $ARGS << EOF
import sys
print sys.argv[:]
exit
EOF
}

[... more bash stuff ...]

And here is the resulting output of both the STDIN and the positional args:

~$ echo hola amigos | myfunct
['-', 'hola', 'amigos']

~$ myfunct hasta luego
['-', 'hasta', 'luego']

HTH

Friday, 26 June 2015

Pasar argumentos posicionales (o STDIN) del script bash a un bloque python en un here-document

english version - all spanish posts

Aquí va un post corto sobre scripts en bash con código incrustado de python en "here-documents". Muy útil para scriping rápido.

El asunto aquí es demostrar cómo pasar tanto argumentos posicionales como desde la entrada estandar a un bloque de código python incrustado en un here-document dentro de un script de bash

Propuesta: escribir un script o una función bash que acepte parámetros posicionales o STDIN y que se los pase al bloque de python dentro del here-document.

Aproximación inicial: ¿nunca has querido saber cómo pasar los argumentos posicionales del script de bash al bloque de python que tienes en tu script? Aquí va el código:

#! /bin/bash

[... código bash ...]

# Nuestro bloque python
python - $@ << EOF
import sys
print sys.argv[:]
EOF

[... más código bash ...]

Así funciona:

bash aproximacioninicial.sh one two
['-', 'one', 'two']

Vamos a avanzar un poco más... ¿Qué pasa si queremos aceptar también un pipe desde el STDIN? En mi «humilde» opinión esta sería una buena aproximación a la resolución del problema (utilizo una función para hacer el código más claro):

#! /bin/bash

[... código bash ...]

# la funciona que pasa a nuestro python tanto 
# argumentos posicionales como input del STDIN
# en este caso si no hay argumentos utilizamos el STDIN
myfunct () {
[[ $# > 0 ]] && ARGS=$@ || read ARGS
python - $ARGS << EOF
import sys
print sys.argv[:]
exit
EOF
}

[... mas código bash ...]

Y aquí está la salida resultante tanto de la STDIN como cuando usamos los argumentos posicionales:

~$ echo hola amigos | myfunct
['-', 'hola', 'amigos']

~$ myfunct hasta luego
['-', 'hasta', 'luego']

HTH

Thursday, 5 January 2012

Quick check of services status

This is just quick and dirty post.

A oneliner to check if all your server services have started rightly after a reboot. This first attempt is aimed for Debian and derivatives:

checkservices () { for service in /etc/rc2.d/S*; do STATUS=$(/etc/init.d/$(ls -l $service | awk '{print $NF}') status 2>/dev/null); [ "$STATUS" != "" ] && printf "Status of ${service##*/}:\t$STATUS\n"; done; }

Example:

root@server:~# checkservices
Status of S04openvpn:    * VPN 'server' is running
Status of S05bind9:      * bind9 is running
Status of S05dhcp3-server:      Status of DHCP server: dhcpd3 is running.
Status of S05sysstat:    * sadc cron jobs are enabled
Status of S06postfix:    * postfix is running

Make it portable (Did I said that this was a quick and dirty post?) :-D

checkservices () { for service in /etc/rc$(runlevel|awk '{print $NF}').d/S*; do STATUS=$(/etc/init.d/$(ls -l $service | awk '{print $NF}') status 2>/dev/null); [ "$STATUS" != "" ] && printf "Status of ${service##*/}:\t$STATUS\n"; done; }

Example (centos server):

[root@server ~]# checkservices
Status of S05kudzu:     kudzu has run
Status of S06cpuspeed:  cpuspeed is stopped
Status of S07iscsid:    iscsid (pid  1980) is running...
Status of S08ip6tables: Firewall is stopped.
[...]

Sunday, 17 April 2011

Set UUID on reiserfs partitions (fun with bash)

spanish version - all english posts

I've just realized that the reiserfs partitions of an old PC are not associated with their corresponding UUID in /etc/fstab. I usually use UUIDs in fstab to avoid problems when adding/removing disks.

This particular PC has several partitions with reiserfs filesystem. Many years ago I preferentially used reiserfs instead of ext2 for obvious reasons. With the advent of ext3 I stopped using the reiser filesystem on new installations. But my old PCs remained with reiserfs. That happened (and remained) over the time with non-system partitions (home, chroot, etc...)

Out of curiosity I've tried to find out how old these partitions are, but I have not been able (perhaps a seasoned forensic guru can help me ... debugreiserfs does not show this information). The oldest files I can see have access dates of 2004 but I'm sure the partitions are much older .... enough rambling!

Turning back to the UUID issue. The reiser partitions are not associated with a corresponding UUID:

root@oldandbeloveddesktop:~# debugreiserfs /dev/sda7 | grep UUID
debugreiserfs 3.6.21 (2009 www.namesys.com)

UUID: 00000000-0000-0000-0000-000000000000
root@oldandbeloveddesktop:~# blkid /dev/sda7
/dev/sda7: TYPE="reiserfs"

So we'll have to generate the UUIDs and assign them to their corresponding reiserfs partitions.

The fun part: We will do it in a single command:

root@oldandbeloveddesktop:~# mount -t reiserfs | while read part; do umount ${part%% *}; reiserfstune --uuid $(uuidgen -r) ${part%% *}; mount ${part%% *} ; done

That's it, we have assigned an UUID to each reiserfs partition. We can verify it using blkid:

root@oldandbeloveddesktop:~# blkid | grep reiserfs
/dev/sda7: TYPE="reiserfs" UUID="3cfa0f14-c35b-49fc-82a8-ff3e6e225af8"
/dev/sda10: TYPE="reiserfs" UUID="76255e20-92b1-4352-a604-6b371eb5ace0"
/dev/sda11: TYPE="reiserfs" UUID="06c74789-d403-408a-898f-b8e8880d3d3a"

We can now edit their corresponding fstab entries. It's the turn for sed and some more fun. Just run:

root@oldandbeloveddesktop:/etc# for part in $(mount -t reiserfs); do sed -i "/^\/dev.*reiser/ s@^${part%% }@#original ${part%% }\n$(blkid ${part%% }|awk '{print $NF}') @" /etc/fstab; done
root@oldandbeloveddesktop:/etc# mount -a -o remount

And that's all folks!

As this post is getting very short, lets fill it with a step by step explanation of the process.

The first command that helped us to assign a UUID to each partition reiserfs is summarized in the following steps.

  1. using "mount -t reiserfs" we get the list of the reiserfs partitions in use.

  2. the output will be passed to "bash's while" which will iterate each line of output assinging each outputed line to the "part" variable.

  3. Using bash parameter expansion we take only the partition device, which graphically can be reperesented as:

    root@oldandbeloveddesktop:~# part="/dev/sda7 /home reiserfs defaults 0 2"
    root@oldandbeloveddesktop:~# echo ${part%% *}
    /dev/sda7
    
  4. for each partition:

    4.1) unmount the partition with "umount"

    4.2) generate a random uuid with uuidgen:

    uuidgen -r
    

    4.3) and assign it to the partition using reiserfstune command.

    4.4) remount the partition.

The second command, which edits "/etc/fstab" entries replacing each /dev/sdXY string with its corresponding UUID, is detailed in the following steps:

  1. as the first command we use "mount -t reiserfs" to get the list of reiserfs partitions (mounted) in the system.

  2. as before, we pass each line from the output to ''$part'' using bash's "while". Remember that ''${part%% *}'' will correspond to each of the partitions in the format '/dev/sdXY'.

  3. For each reiserfs partition we execute sed infile editin the /etc/fstab file. The sed command can be divided into:

    3.1) a first part where you select only the lines of the reiserfs partitions. Those with the format:

    /dev/...............reiser............
    

    3.2) on the previous selection sed will just edit the field "/dev/sdXY". 3.3) and sed will replace it with two lines: a comment header (just to remember which was the original partition's name), and the corresponding fstab's entry with the UUID.

    3.3.1) I get the UUID value with "blkid" keeping just the UUID value, which corresponds to the last field in the output if we divide it into spaces (awk NF). For example if you originally had lines with the format:

        /dev/sda10 /chroot reiserfs defaults 0 2
    
    they will be replaced with::
        #original /dev/sda10
        UUID="76255e20-92b1-4352-a604-6b371eb5ace0"  /chroot reiserfs defaults 0 2
    

Well, that's all. The objective has been achieved: We have assigned a UUID for each of the reiserfs partitions, and we have had some fun time playing with the shell.

Establecer el UUID a particiones reiserfs. Jugando con bash

english version - all spanish posts

Acabo de darme cuenta que en un viejo PC las particiones en el fstab no están asociadas al correspondiente UUID. Normalmente suelo asociarlas ya que así evito cualquier problema al añadir/quitar discos.

Este PC en concreto tiene la mayoría de las particiones de los usuarios (distintos /home/XXXX) con reiserfs. Hace muchos años usaba reiserfs de manera preferente a ext2 por razones obvias. Con la llegada de ext3 dejé de usar el sistema de ficheros reiser.

Por curiosidad he tratado de averiguar la antigüedad de estas particiones pero no he sido capaz (quizá algún avezado forense me pueda ayudar... debugreiserfs no muestra esa información). Los ficheros mas antiguos tiene fechas de acceso del 2004 pero estoy seguro que las particiones son anteriores....

Volviendo al tema del UUID. Las particiones reiser no llevan asociado el UUID:

root@oldandbeloveddesktop:~# debugreiserfs /dev/sda7 | grep UUID
debugreiserfs 3.6.21 (2009 www.namesys.com)

UUID: 00000000-0000-0000-0000-000000000000
root@oldandbeloveddesktop:~# blkid /dev/sda7
/dev/sda7: TYPE="reiserfs"

Por lo que habrá que generar el UUID y asignárselo a las particiones reiserfs. Lo haremos en un solo comando:

root@oldandbeloveddesktop:~# mount -t reiserfs | while read part; do umount ${part%% *}; reiserfstune --uuid $(uuidgen -r) ${part%% *}; mount ${part%% *} ; done

Eso es todo, ya hemos asignado un uuid a cada una de las particiones reiserfs. Podemos verificarlo mediante blkid:

root@oldandbeloveddesktop:~# blkid | grep reiserfs
/dev/sda7: TYPE="reiserfs" UUID="3cfa0f14-c35b-49fc-82a8-ff3e6e225af8"
/dev/sda10: TYPE="reiserfs" UUID="76255e20-92b1-4352-a604-6b371eb5ace0"
/dev/sda11: TYPE="reiserfs" UUID="06c74789-d403-408a-898f-b8e8880d3d3a"

Como colofón podemos editar el fstab mediante sed para reemplazar las entradas tipo /dev/sdXY por su correspondiente UUID. Bastará ejecutar:

root@oldandbeloveddesktop:/etc# for part in $(mount -t reiserfs); do sed -i "/^\/dev.*reiser/ s@^${part%% }@#original ${part%% }\n$(blkid ${part%% }|awk '{print $NF}') @" /etc/fstab; done
root@oldandbeloveddesktop:/etc# mount -a -o remount

Y esto es todo amigos!

Como el post se me ha quedado muy corto voy a explicar de manera detallada el proceso.

El primer comando que nos servía para asignar un UUID a cada partición reiserfs se resume en los siguientes pasos.

  1. mediante mount -t reiserfs listamos las particiones reiserfs

  2. el output se lo pasamos al "while" de bash quien iterará cada línea de la salida del anterior comando asignándosela a la variable "part".

  3. Mediante la expansión de parámetros de bash obtenemos sólo el dispositivo de la partición; o lo que gráficamente es:

    root@oldandbeloveddesktop:~# part="/dev/sda7 /home reiserfs defaults 0 2"
    root@oldandbeloveddesktop:~# echo ${part%% *}
    /dev/sda7
    
  4. para cada una de las particiones:

    4.1) desmontamos la partición mediante umount

    4.2) obtenemos un uuid aleatorio con uuidgen:

    uuidgen -r
    

    4.3) y se lo asignamos con reiserfstune.

    4.4) volvemos a montar la partición.

El segundo comando, que edita el fichero /etc/fstab reemplazando las entradas tipo /dev/sdXY por su correspondiente UUID, se detalla en los siguientes pasos:

  1. como en el primer comando usamos "mount -t reiserfs" para obtener el listado de las particiones reiserfs que tenemos (montadas) en el sistema.

  2. al igual que antes el output se lo pasamos a while asignando a $part cada línea. Recordemos que ''${part %% *}'' corresponderá a cada uno de las particiones en el formato '/dev/sdXY'.

  3. Para cada partición reiserfs ejecutaremos sed sobre /etc/fstab. El comando sed se divide en:

    3.1) una primera parte en la que seleccionamos las líneas de las particiones reiserfs, es decir con formato:

    /dev/...............reiser............
    

    3.2) sobre la selección anterior editaremos el campo /dev/sdXY 3.3) y lo reemplazaremos por un header que recuerde cual era el nombre original y la entrada fstab con el correspondiente UUID.

    3.3.1) el UUID lo obtendremos con blkid quedándonos únicamente con el valor UUID="foo" que corresponde al último campo del output si lo dividimos en espacios (awk). Por ejemplo si originalmente la línea correspondía a:

        /dev/sda10 /chroot reiserfs defaults 0 2
    
    quedará como:
        #original /dev/sda10
        UUID="76255e20-92b1-4352-a604-6b371eb5ace0"  /chroot reiserfs defaults 0 2
    

Ahora, si, esto es todo. El objetivo ha sido conseguido. Hemos asignado un UUID a cada partición reiserfs y nos hemos divertido un rato con la shell.

Saturday, 13 February 2010

Fixing picture's date

spanish version - all english posts

I have a couple of bash scripts that I created when I bought my first digital camera back in 2003 or 2004. Those scripts allow me to clasify photos by date. The scripts use the date from the exif metadata that is embedded on each photo file header. That metadata is extracted using the strings command. With that timestamp the script renames each photo as YYYYMMDD_HHMMSS_#.jpg (the # indicates the number of the picture if several of them have the same timestamp). Also the scripts saves each photo in a folder tree of the form FOTOS/YYYY/MM.

With this system I have all the digital photos in a chronological order regardless the camera used to take them.

However, today, running the script to import photos from two of my digital cameras I've noticed that the pictures from one of the cameras had wrong timestamps. The camera which had wrong timestamps its a camera with a faulty battery and it seems that I made a mistake setting the date after the last reset. I set 2009 instead of current 2010 year. Nearly 200 photos will be bad classified if I don't find a cure for it.

Editing directly the ascii strings in the jpg header is useless, so a classical bash approach wont be useful... maybe some pythonic try with PIL... hmmm!

A quick search in Google shows a promising suggestion

jhead is a command line tool. If I had known it a few years ago I wouldn't have created the bash scripts for classifying photos. However I wouldn't have had the fun of creating them. jhead allows not only to display and manipulate exif's metadata but also allows to manipulate the photo files themselves.

Let me show you how to use jhead to fix the timestamp of the photos just in case you have the same problem I had. Of course, first you should install it (I use Ubuntu 9.10):

aptitude update && aptitude install jhead

Edit the picture files to change the date is as easy as running the following command. With this command I've managed to increase the timestamp in a year for 200+ photos in the SD card:

jhead -da2010:01:01-2009:01:01 *.jpg

jhead is well worth so I recommend that you take some time to read its manpage to see what it can offer (which is a lot).

Another example that I want to comment is how jhead manages to replace with in a single and simple command much of the functionality that implements the script that I use to sort my photos. Let's rename a picture in the form of YYYMMDD_HHMMSS.jpg using its metadata's timestamp:

~$ jhead -nf'%Y%m%d_%H%M%S' IMGP4890.JPG
IMGP4890.JPG --> 20100212_090624.jpg

Lovely!

As a tribute to my script (which has been working like a charm since 2004) I'm posting it below. Now that I know jhead my script will no longer be used... As you can see my old script in addition to rename the photos with their timestamp, as said before, the script moves them to a FOTOS/YYYY/MM directory tree structure, and it was able to manage photos with the same timestamp.

The script is a bit convoluted, reviewing it for writing this post I realize that my scripting technique has improved much over time, fortunately!:

#! /bin/bash
# vim:ts=4:sw=4:et:ft=sh
# $Source: camara_scripts/RCS/fotos_script_testTEST.sh,v $
# <hmontoliu@yahoo.es>
# 2004-01-13

: ${DESTDIR:="$HOME/FOTOS/"}
echo "Utilizando directorio destino: $DESTDIR"
echo
echo "OK (Ctrl-C aborta)?"; read foo

shopt -s extglob # for improved file pattern matching
for imgname in *.+(jpg|JPG);
do
    newname="$(head "$imgname"|strings|sed -n '/[0-9]\{4\}\:.*/ {s/ /_/g; s/://g; s/$/.jpg/; p}'|uniq)"
    eval $(echo $newname | sed -n 's/^\([0-9]\{4\}\)\([0-9]\{2\}\).*/year=\1 month=\2/p')
    mkdir -p ${DESTDIR}/$year/$month
    cp -v --backup=numbered --suffix=- "${imgname}" "${DESTDIR}/$year/$month/$newname"
done

# lets rename the xxx.jpg.~#~ generated by cp backup to xxx-#-.jpg
for file in $(find $DESTDIR -regex '.*~[0-9]+~')
do
    safenewname="$(echo $file | sed 's/\(.*\).jpg.~\(.[^~]*\)~/\1-\2.jpg/')"
    if [ -f "$safenewname" ]; then
        mv -iv "$file" "$safenewname"
    else
        mv -v "$file" "$safenewname"
    fi
done

I will stop using it; A new script with jhead and rename (perl's prerename) should be no more than two lines!

Rectificar la fecha en las fotografías

english version - all spanish posts

Tengo una serie de scripts en bash que creé cuando me compré la primera cámara digital, en el 2003 o 2004, que me permiten clasificar las fotos por fechas. Extraen los metadatos del encabezamiento de las fotografías mediante el comando strings. A partir de la fecha que proporcionan los metadatos exif renombran cada fotografía como AAAMMDD_HHMMSS_#.jpg (el # indica el número de fotografía en caso de que varias tuvieran el mismo timestamp) y además guardan cada fotografía en un directorio llamado FOTOS/AAA/MM. De esta forma tengo todas las fotografías digitales ordenadas por fechas independientemente de la cámara usada.

Resulta que hoy al ejecutar el script para importar las fotos de dos de mis cámaras digitales me he dado cuenta de que las fotos de una de las cámaras tenían mal puesta la fecha. Se trata de una cámara que tiene mal la batería y se resetea a la configuración de origen cada vez que la recargo. Por error al volver a conectar la cámara me equivoqué y puse año 2009 en lugar de 2010. Unas 200 fotos se clasificarán mal si no lo remedio.

Editar directamente el texto ascii del encabezamiento de las fotografías (el que se obtiene con strings) no sirve para cambiar la fecha (lo he probado) por lo que con las herramientas bash habituales no me iba a ser posible editar las 200 fotos con el año mal puesto.

Después de un rápido «googleo» obtengo una prometedora sugerencia:

jhead es una herramienta de línea de comandos que de haberla conocido hace unos años me hubiera ahorrado los scripts de bash pero también toda la diversión que tuve con ellos al hacerlos. Permite no solo mostrar y manipular los metadatos exif de las imágenes sino manipular los ficheros de las fotos a partir de estos datos.

Os muestro como usar jhead para reparar la fecha de las fotos por si os pasa algo similar. En primer lugar lo instalamos (yo uso en el ordenador donde guardo las fotos Ubuntu 9.10):

aptitude update && aptitude install jhead

Editar las fotos para cambiar la fecha es tan sencillo como ejecutar el siguiente comando que se encarga de modificar la fecha de las fotos añadiendo un año a todas ellas. Con este comando he conseguido incrementar la fecha en un año en las 200 fotos de la tarjeta:

jhead -da2010:01:01-2009:01:01 *.jpg

jhead merece mucho la pena así que os recomiendo que os deis una vuelta por su manpage para ver todo lo que puede ofrecer (que es bastante).

Otro ejemplo que me gustaría comentaros es como jhead con un solo comando sustituye gran parte de la funcionalidad del script que uso para clasificar fotos. Vamos a renombrar una foto usando el timestamp de sus metadatos exif de modo que pase a llamarse AAAMMDD_HHMM.jpg:

~$ jhead -nf'%Y%m%d_%H%M%S' IMGP4890.JPG
IMGP4890.JPG --> 20100212_090624.jpg

Realmente sencillo.

Como homenaje a mi script que ha venido funcionando perfectamente desde el 2004 os lo muestro a continuación, ya que con jhead va a dejar de ser usado. Como podéis ver además de renombrar las fotos, como os he comentado al principio, el script las clasifica en directorios AAAA/MM y gestiona fotos con el mismo timestamp. El script es un poco enrevesado, revisándolo para este post me doy cuenta de que mi técnica de scripting afortunadamente ha mejorado mucho con el tiempo:

#! /bin/bash
# vim:ts=4:sw=4:et:ft=sh
# $Source: camara_scripts/RCS/fotos_script_testTEST.sh,v $
# <hmontoliu@yahoo.es>
# 2004-01-13

: ${DESTDIR:="$HOME/FOTOS/"}
echo "Utilizando directorio destino: $DESTDIR"
echo
echo "OK (Ctrl-C aborta)?"; read foo

shopt -s extglob # for improved file pattern matching
for imgname in *.+(jpg|JPG);
do
    newname="$(head "$imgname"|strings|sed -n '/[0-9]\{4\}\:.*/ {s/ /_/g; s/://g; s/$/.jpg/; p}'|uniq)"
    eval $(echo $newname | sed -n 's/^\([0-9]\{4\}\)\([0-9]\{2\}\).*/year=\1 month=\2/p')
    mkdir -p ${DESTDIR}/$year/$month
    cp -v --backup=numbered --suffix=- "${imgname}" "${DESTDIR}/$year/$month/$newname"
done

# lets rename the xxx.jpg.~#~ generated by cp backup to xxx-#-.jpg
for file in $(find $DESTDIR -regex '.*~[0-9]+~')
do
    safenewname="$(echo $file | sed 's/\(.*\).jpg.~\(.[^~]*\)~/\1-\2.jpg/')"
    if [ -f "$safenewname" ]; then
        mv -iv "$file" "$safenewname"
    else
        mv -v "$file" "$safenewname"
    fi
done

En fin, voy a dejar de usarlo, con jhead y rename (prename de perl) el nuevo script se quedará en solo un par de líneas.

Tuesday, 26 January 2010

When did the system boot

spanish version - all english posts

The most common way to find out when was the last start is using the command "uptime":

root@blogspot:~# uptime
 22:28:20 up 22 days, 12:48,  1 user,  load average: 0.11, 0.04, 0.00

However that command doesn't tell us WHEN but HOW LONG!

Hence, only for curiosity, here are a few recipes to get that WHEN:

With "who":

root@blogspot:~# who -b
         system boot  2010-01-04 09:39

Using "last":

root@blogspot:~# last reboot
reboot   system boot  2.6.24-24-server Mon Jan  4 09:39 - 22:30 (22+12:51)

wtmp begins Mon Jan  4 09:34:08 2010

Unfortunately "last" depends on the "wtmp" registry which is regularily cleaned (depending on the distribution and how is logrotate configured). For this reason in servers with a high uptime the command "last reboot" is not quite useful.

Finally, the method I like best: Using the epoch timestamp from /proc/stat:

root@blogspot:~# date -d @$(sed -n 's/btime \(\d*\)/\1/p' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

As usually, when I use sed, awk or grep I like to get their equivalents of each of them. So here are some alternatives of the above command (there are dozens of them):

With awk:

root@blogspot:~# date -d @$(awk '$1 ~ /btime/ {print $2}' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

And with grep; in this case using it's perl regexp expressions compatibility:

root@blogspot:~# date -d @$(grep -Po '(?<=btime )(\d*)' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

Cuándo se inició el sistema

english version - all spanish posts

La forma mas habitual de averiguar cuando se produjo el último inicio del sistema es mediante el comando uptime:

root@blogspot:~# uptime
 22:28:20 up 22 days, 12:48,  1 user,  load average: 0.11, 0.04, 0.00

Pero ese comando no nos dice CUANDO sino HACE CUANTO.

Así que aunque solo sea por curiosidad aquí van unas cuantas recetas para obtener el CUANDO:

Mediante who:

root@blogspot:~# who -b
         system boot  2010-01-04 09:39

Con last:

root@blogspot:~# last reboot
reboot   system boot  2.6.24-24-server Mon Jan  4 09:39 - 22:30 (22+12:51)

wtmp begins Mon Jan  4 09:34:08 2010

Lamentablemente "last" depende del registro wtmp que suele ser limpiado periódicamente (dependiendo de la distribución y de la configuración que se haga de logrotate). Por este motivo en servidores con un uptime muy elevado "last reboot" puede no ser demasiado útil.

Finalmente el método que más me gusta: a partir de la fecha epoch del arranque está almacenada en /proc/stat:

root@blogspot:~# date -d @$(sed -n 's/btime \(\d*\)/\1/p' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

Como cada vez que uso sed, awk o grep me gusta obtener sus equivalentes para cada uno os propongo alguna alternativa (de las decenas que hay) al anterior comando sed; con awk:

root@blogspot:~# date -d @$(awk '$1 ~ /btime/ {print $2}' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

Y con grep, en este caso con su compatibilidad con expresiones perl:

root@blogspot:~# date -d @$(grep -Po '(?<=btime )(\d*)' /proc/stat)
Mon Jan  4 09:39:24 CET 2010

Saturday, 23 January 2010

Counting pages

spanish version - all english posts

We have an application that generates PDF reports from rst formated text files. One of the requirements that the applicaction should have is the numbering of pages in the format "Page X of Y". Unfortunaltely our application is an implemetation of rst2pdf 0.11 which doesn't have shuch feature (at least in the version we have). Although it wouldn't be too complex to implement it by modifying the code of python-docutils, this goes against my principles: Packages should be maintained by their developers at first instance, and by the distro packagers at second stage (in this case Ubuntu LTS 8.04). As an administrator I do not like altering this policy because then we must be aware of future security updates that may break things when our changes will be overriden.

I'm a bash guy, hence I always tend to prototype using the shell. So here is a quick way to implement a method to count the total pages of a PDF document w/o installing any specific tool.

Total PDF pages obtained by grep:

hmontoliu@blogspot:/tmp$ grep -c --binary-files=text '/Page\b' foo.pdf
818

Of course the package xpdf-utils includes the command "pdfinfo" which reports the number of pages in a document; but again, if you can avoid installing extra packages on a server much better.

The avobe command also applies to ps documents with minor modifications:

hmontoliu@blogspot:/tmp$ grep -c '%%Page' bar.ps
5

More information at "man grep" :-)

Implementing this simple grep in python as a call to the shell or doing a grep-like stuff through re or string.count('text') is trivial; for example:

In [1]: with open('/tmp/foo.pdf','rb') as f:
   ...:     f.read().count('/Page>')
   ...:
   ...: 

Out[1]: 818

Contando páginas

english version - all spanish posts

Tenemos una aplicación que genera informes en PDFs a partir de ficheros con formato rst. Uno de los requisitos que debía tener la aplicación es la numeración de páginas de tipo "página X de Y". Lamentablemente se trata de una implementación a partir de rst2pdf 0.11 que de momento no incorpora esa característica (al menos en la versión que tenemos). Aunque no sería demasiado complejo implementarla modificando el código de python-docutils esto va en contra de mis principios: Los paquetes los deben mantener primero sus desarrolladores y en segunda instancia la distribución de turno; en este caso Ubuntu LTS 8.04. Como administrador no me gusta alterar esta política pues luego hay que estar pendiente de qué se rompe o se deja de romper con las actualizaciones de seguridad.

Como las cabras que tiran al monte, yo siempre tiro a la shell para «prototipar». Así que aquí va un método rápido de implementar que permite contar el total de páginas de un PDF sin herramientas específicas.

Páginas de un documento PDF obtenidas con grep:

hmontoliu@blogspot:/tmp$ grep -c --binary-files=text '/Page\b' foo.pdf
818

Por supuesto el paquete xpdf-utils incluye el comando "pdfinfo" que informa del número de páginas de un documento; pero, nuevamente, si se puede evitar la instalación de paquetes extra en un servidor mucho mejor.

El anterior grep nos vale también para documentos PS con mínimas modificaciones:

hmontoliu@blogspot:/tmp$ grep -c '%%Page' bar.ps
5

Mas información en man grep :-)

Implementar este sencillo grep en python como llamada a la shell o haciendo un grep-like directamente con python mediante re o con un cadena.count('texto') es trivial; por ejemplo:

In [1]: with open('/tmp/foo.pdf','rb') as f:
   ...:     f.read().count('/Page>')
   ...:
   ...: 

Out[1]: 818