mardi 31 juillet 2012

bash, timeout

L'astuce du jour !

Si un script appelle une commande qui peut ou non rendre la main (chemin réseau) et que l'on souhaite toujours la récupérer, on peut faire appel à la commande timeout.
$ timeout 3 ssh user@host.example.com:~/ ls
Cela est très pratique lorsque la commande utilisée n'a pas prévu de rendre la main suite à un timeout spécifié en paramètre.

bash, récupérer le code de sortie d'un sous-shell

Ci-dessous on récupère le code de sortie d'un sous-shell afin d'effectuer un traitement particulier qui fait des pipes.

lundi 30 juillet 2012

bash, sort

Bien souvent, on recherche des fichiers avec la commande find dans une arborescence. L'option "-printf format" permet d'afficher une ligne pour chaque entrée trouvée. Le format permet d'afficher les dates/heures, le basename de l'entrée...

$ find src/main/webapp/changes.log -printf '%f\n'
changes.log
$ find src/main/webapp/changes.log -printf '%h\n'
src/main/webapp
Ca peut être bien utile de connaître cette option ;)

vendredi 27 juillet 2012

bash, initialisation des variables

Use a default value

${PARAMETER:-WORD}
${PARAMETER-WORD}

If the parameter PARAMETER is unset (never was defined) or null (empty), this one expands to WORD, otherwise it expands to the value of PARAMETER, as if it just was ${PARAMETER}. If you omit the : (colon), like shown in the second form, the default value is only used when the parameter was unset, not when it was empty.


Assign a default value

${PARAMETER:=WORD}
${PARAMETER=WORD}

This one works like the using default values, but the default text you give is not only expanded, but also assigned to the parameter, if it was unset or null. Equivalent to using a default value, when you omit the : (colon), as shown in the second form, the default value will only be assigned when the parameter was unset.


Pour résumer :
  • Le ":" indique que l'on remplace par l'expansion de WORD même si PARAMETER est vide (positionné mais vide)
  • Le "=" à la différence de ":" indique qu'en plus PARAMETER sera affecté par l'expansion de WORD si celle-ci est utilisée.

jeudi 26 juillet 2012

bash, génerer des mots de passe

Vous n'avez pas envie de réfléchir pour générer un mot de passe ? Voici une petite méthode à mettre dans votre bashrc qui vous facilitera le choix ;)
function genpass() {
    LENGTH=${1:-10}
    if [ "$2" == "0" ]; then
      CHAR="[:alnum:]"
    elif [ "$2" == "1" ]; then
      CHAR="[:graph:]"
    elif [ "${2:0:1}" != "+" ]; then
      echo "Erreur: vous devez spécifier les caractères acceptés"
      echo "Exemple: $ genpass 32 '+[:alnum:]_'"
      echo "2na2lku4FBqM7eNPC_aooahXV0c8GxI7"
      return
    else
      CHAR="${2:1}"
    fi
    cat /dev/urandom | tr -cd "$CHAR" | head -c $LENGTH
    echo
}

$ # génère un mot de passe de 10 caractères avec lettres et chiffres
$ genpass 10 0
toSGXjycaa
$ # génère un mot de passe de 10 caractères avec lettres, chiffres et symboles
$ genpass 10 1
^:5-LONhtn
$ # génère un mot de passe de 10 caractères avec ce que vous spécifiez après le +
$ genpass 10 "+abc[:digit:]"
b5836a29a2

mercredi 25 juillet 2012

bash, convertir la casse d'un flux

Pour faire des recherches de noms de fichiers correspondant à un certain pattern, j'ai eu besoin de changer la casse des noms...

Ici je recherche les fichiers qui contiennent "erreur" dans leur noms.
$ ls | tr '[:upper:]' '[:lower:]' | grep erreur

lundi 23 juillet 2012

python, obtenir la liste des fichiers .java

Pour récupérer la liste des .java dans une arborescence et sa sous-arborescence, on peut utiliser le code suivant :
import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk('/path/to/the/directory'):
  for filename in fnmatch.filter(filenames, '*.java'):
      matches.append(os.path.join(root, filename))

vendredi 20 juillet 2012

python, ouvrir un fichier en utf8

Pour ouvrir un fichier en utilisant un encodage particulier, il est possible de passer par le module codecs...
with codecs.open('the_file.csv', 'r', 'utf8') as f:
    l = f.readline()
    ...

bash, nombres en hexadécimal

Si pour une raison ou une autre, vous devez jouer à afficher des nombres héxadécimal en base 10 ou vice-versa, vous pouvez passer par la commande printf
$ printf "%x" 174
ae
$ printf "%d" 0xae
174
$ printf "%d\n" \'A
65

bash, tri par longueur de ligne

La commande sort ne permet pas de trier en fonction de la longueur des lignes. C'est dommage mais on peut utiliser awk, combiné avec sort pour avoir le même résultat.
$ cat /usr/share/dict/words 
   | awk '{ print length(), $0 | "sort -n" }' 
   | cut -d ' ' -f 2-

mardi 17 juillet 2012

Jetty 8 et paramètres d'initialisation

La documentation sur le net est obscure quant à la manière d'initialiser les paramètres de contexte avec jetty (hors du web.xml dans le webdefault.xml (équivalent du host.xml de tomcat)).

J'ai cherché, cherché et cherché...

Et puis j'ai trouvé :) Le nouveau code est le suivant :

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" 
"http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">

  <Set name="sessionHandler">
    <New class="org.eclipse.jetty.server.session.SessionHandler">
      <Arg>
        <New class="org.eclipse.jetty.server.session.HashSessionManager">
          <Set name="storeDirectory">jetty/sessions</Set>
        </New>
      </Arg>
    </New>
  </Set>
  <Get name="ServletContext">
    <Call name="setInitParameter">
      <Arg>driverSQL</Arg>
      <Arg>com.mysql.jdbc.Driver</Arg>
    </Call>
  </Get>
</Configure>
L'ancien code était le suivant :

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" 
"http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="sessionHandler">
    <New class="org.eclipse.jetty.server.session.SessionHandler">
      <Arg>
 <New class="org.eclipse.jetty.server.session.HashSessionManager">
   <Set name="storeDirectory">jetty-sessions</Set>
 </New>
      </Arg>
    </New>
  </Set>
  <Set name="initParams">
    <Map>

      <Entry>
 <Item>driverSQL</Item>
 <Item>com.mysql.jdbc.Driver</Item>
      </Entry>
</Map>
  </Set>
</Configure>
Ce n'est pas très différent mais il fallait trouver, la documentation en ligne n'en parlant pas ! En espérant que ça vous fasse économiser de précieuses heures... A vos servletContext.getInitParameter !!!

Servlets et encoding...

J'ai eu des petit soucis avec le charset...

Un svi envoyait bien "text/csv; charset=utf-8" (curl) mais pour une raison x ou y, mon ihm qui l'appelait voyait "text/plain;charset=ISO-8859-1" ce qui faisait que le fichier était mal affiché.

Dans le code ci-dessous, j'ouvre une connection et récupère le charset afin d'initialiser l'InputStreamReader et commencer les lectures...
        URL url = new URL(sviUrl);
        URLConnection conn = url.openConnection();  
        conn.connect();
        InputStream in = null;
        try {
            String charset = "ISO-8859-1";
            String contentType = conn.getContentType(); 
            if (contentType != null && contentType.indexOf(";charset=") >= 0) {
                contentType = contentType.substring(contentType.indexOf(";charset=")+9);
                charset = contentType.trim();
            }
            in = conn.getInputStream();
            InputStreamReader inr = new InputStreamReader(url.openStream(), charset);
            Writer out = resp.getWriter();
            try {
                IOUtils.copy(inr, out);
            } finally {
                inr.close();
            }
        } finally {
            if (in != null) {
                in.close();
            }
        }

lundi 16 juillet 2012

mysql, importer un fichier csv

Pour charger un fichier csv dans une table on peut utiliser la commande suivante :

mysql> load data infile '/path/to/file.csv' into table mytable character 
set 'utf8' fields terminated by ';' IGNORE 1 lines;
Query OK, 786 rows affected (0.13 sec)
Records: 786  Deleted: 0  Skipped: 0  Warnings: 0
Ici, le charset est spécifié et la première ligne du fichier CSV est ignorée (titre des colonnes).

bash, récupérer les noms de fichiers renvoyés par diff -rb --brief

La commande "diff -rb --brief" génère des lignes user-friendly qui sont malheureusement localisées.
Pour récupérer le nom des fichiers qui diffèrent, il est possible d'utiliser la commande lsdiff du package patchutils.
$ sudo apt-get install patchutils
$ files_to_transfer=$(diff -x '*.zip' -x '.svn' -rb -U 1 
      fitnesse-install/FitNesseRoot/FrontPage/Project 
      $dist_install/FrontPage/Project | lsdiff)

python, unicode...

>>> unicodedata.name(u"é")
'LATIN SMALL LETTER E WITH ACUTE'
>>> print u"e accent aigu : \N{LATIN SMALL LETTER E WITH ACUTE}"
e accent aigu : é

python, bash et encoding...

En python, lorsque la sortie n'est pas de type tty, l'encoding est "ascii" ce qui provoque des erreurs sur les caractères accentués. La solution est d'exporter la variable PYTHONIOENCODING qui définit l'encoding à utiliser.
$ python a.py 
é
$ python a.py | cat
Traceback (most recent call last):
  File "a.py", line 3, in 
    print u"\xe9";
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: 
ordinal not in range(128)
$ export PYTHONIOENCODING=utf-8 
$ python a.py | cat
é
$
Une autre solution est de changer l'encoding de stdout...
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import codecs
import locale

sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)

print u"é"

vendredi 13 juillet 2012

bash, moreutils

Je viens de découvrir un paquet mettant à disposition des outils bien sympathiques pour faciliter l'écriture de scripts bash. Il s'agit du paquet moreutils
$ sudo apt-get install moreutils
Exemples d'utilisation :
Pour envoyer un mail si des données sont lues sur l'entrée standard...
$ find . -name core | ifne mail -s "Core files found" root

Pour réécrire dans un même fichier sans avoir à créer un fichier temporaire...
$ sed '...' file | grep '...' | sponge file

mardi 3 juillet 2012

url et mot de passe

Pour réduire mes logs, je viens d'utiliser la syntaxe suivante :
http://user:password@host:port...
Ici, le mot de passe doit être encodé lorsque des caractères non alphanumériques sont utilisés (comprenez 0-9a-zA-Z). Chaque caractère non alphanumérique doit être encodé avec le format "%xx" où xx sera remplacé par le code hexadécimal du caractère.
Exemple: 

def conv(pw):
    r = ''
    for c in pw:
 if not c.isalpha():
     r += '%' + '%X' % ord(c)
 else:
     r += c
    return r 

>>> conv(u'tuéàtu')
u'tu\xe9\xe0tu'

lundi 2 juillet 2012

bash, fautes sur commande cd...

Si l'option cdspell est positionnée, les fautes de frappe dans la commande cd seront corrigées. Les erreurs prises en compte seront les caractères transposés, les caractères manquants et les caractères trop nombreux. Si un correctif est trouvé, le chemin utilisé pour le cd sera affiché et la commande exécutée. L'option n'est utilisée que dans le cas des shells intéractifs.
$ shopt -s cdspell

$ cd /ec
/etc

$ shopt -u cdspell