Trucs & Astuces

Applications

Ansible

Logo Ansible

La gestion automatisée de tout un parc de serveurs,

De l'installation d'applications à la configuration en passant par toutes les tâches de maintenance.

Tout est automatique grâce aux playbooks Ansible.

Pré-requis: maitriser le YAML, disposer d'un environnement virtuel en Python et disposer sur son système du programme sshpass.


Pour les systèmes à base de dnf

$ sudo dnf install python3 sshpass

Pour les systèmes à base de apt

$ sudo apt install python3 sshpass

Pour les systèmes à base de zypper

$ sudo zypper install python3 sshpass

 
 

 

 

 

Fichier attachéTaille
Image icon logo ansible34.32 Ko

Ansible: Installer son environnement virtuel Python

Pour utiliser Ansible, il est nécessaire de disposer d'un environnement virtuel Python.

Pour ce faire, rien de plus simple:

1 - Création de l'environnement virtuel dans son dossier personnel

$ cd ~
$ python3.11 -m venv ansible

2 - Activation de l'environnement virtuel et effectuer la mise à jour de l'outil python pip

$ cd ansible/
$ source bin/activate
$ python3 -m pip install -U pip

3 - Installation du module Ansible

$ python3 -m pip install -U ansible

4 - Vérifier la bonne installation de Ansible

$ ansible --version
ansible [core 2.16.7]
  config file = None
  configured module search path = ['/home/ronan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ronan/ansible/lib/python3.11/site-packages/ansible
  ansible collection location = /home/ronan/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/ronan/ansible/bin/ansible
  python version = 3.11.3 (main, Aug  9 2023, 09:30:45) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)] (/home/ronan/ansible/bin/python3)
  jinja version = 3.1.4
  libyaml = True

$ ansible -m ping localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Et voilà, l'environnement virtuel Python pour Ansible est prêt et opérationnel.

Etiquettes: 

Ansible: le fichier inventaire

Pour fonctionner, Ansible a besoin de connaitre la liste exhaustive de tous les serveurs qu'il doit administrer.

La documentation Ansible en ligne

1 - Création du fichier hosts

$ vim hosts

2 - Le contenu du fichier doit ressembler à ceci

$ cat hosts
[nginx]
SERVER1
SERVER2

[rabbitmq]
SERVER3

[glpi]
SERVER4

[docker]
SERVER5
SERVER6

[prod]
SERVER1
SERVER3
SERVER4
SERVER5

[dev]
SERVER2
SERVER6

Il est possible d'indiquer entre [...] un terme permettant de regrouper les différents serveurs en catégorie.

La commande suivante permet d'afficher tous les hosts présents dans le fichier inventaire.

$ ansible-inventory --graph
@all:
  |--@ungrouped:
  |--@nginx:
  |  |--SERVER1
  |  |--SERVER2
  |--@rabbitmq:
  |  |--SERVER3
  |--@glpi:
  |  |--SERVER4
  |--@docker:
  |  |--SERVER5
  |  |--SERVER6
  |--@prod:
  |  |--SERVER1
  |  |--SERVER3
  |  |--SERVER4
  |  |--SERVER5
  |--@dev:
  |  |--SERVER2
  |  |--SERVER6

... d'un groupe en particulier

$ ansible-inventory --graph nginx
@nginx:
  |--SERVER1
  |--SERVER2

 
 

 

 

Etiquettes: 

Ansible: le fichier de configuration ansible.cfg

Le fichier ansible.cfg n'est pas forcément nécessaire pour l'utilisation d'Ansible, mais il est fortement conseillé pour la simplifier.

Documentation Ansible en ligne

Voici un exemple limité des principaux paramètres à renseigner.

$ cat ansible.cfg
[defaults]
inventory          = ./hosts
interpreter_python = /usr/bin/python3
host_key_checking  = False
log_path           = ./ansible.log
remote_user        = root

- On y indique l'emplacement du fichier inventaire hosts
- L'interpréteur python à utiliser sur les serveurs distants
- On ne vérifie pas les clés des serveurs distants. Cela évite d'avoir tous les serveurs de renseignés dans le fichier ~/.ssh/know_hosts
- On renseigne le fichier pour les logs
- Le user utilisé pour se connecter aux différents serveurs

Il est possible de générer automatiquement le fichier de configuration via cette commande

$ ansible-config init --disabled > ansible.cfg

 

Etiquettes: 

Ansible: utilisation avec une clé ssh

Pour éviter, à chaque fois, de saisir le mot de passe (option -k) permettant de se connecter aux serveurs distants, il est possible d'utiliser une connexion par clé ssh.

La documentation Ansible en ligne

Tout est indiqué ici pour générer une clé ssh.

Une fois la clé ssh générée, il faut la copier sur tous les serveurs distants.
Pour cela, nous allons utiliser Ansible pour le faire à grande échelle.

Nous allons donc utiliser la commande authorized_key pour le faire.

$ ansible -m authorized_key -u mon_user -a "user=mon_user exclusive=true state=present key=""{{ lookup('file', '~/.ssh/id_rsa.pub') }}""" all -k

- L'option -m authorized_keys permet d'indiquer que nous souhaitons utiliser la commande authorized_keys
- L'option -u mon_user permet d'indiquer avec quel utilisateur se connecter aux serveurs distants.
- L'option -a "..." permet de renseigner les arguments nécessaires pour la commande authorized_keys. On y indique le user concerné, le terme exclusive permet d'indiquer qu'il doit y avoir une seule clé ssh d'autorisée, le terme state permet d'indiquer que la clé publique doit être présente (absent si l'on souhaite la supprimer) et enfin, le terme key permet, grâce à la fonction lookup, de copier le contenu du fichier ~/.ssh/id_rsa.pub du serveur local dans le fichier authorized_keys du ou des serveurs distants.
- Le mot clé all permet d'indiquer que l'opération doit être effectuée sur tous les hosts présents dans l'inventaire.
- L'option -k permet de saisir le mot de passe pour se connecter aux serveurs distants.

Ansible: les commandes ...

Voici quelques exemples d'utilisation des différentes commandes disponibles avec Ansible

Etiquettes: 

Ansible: la commande ping

La première commande Ansible que nous allons utiliser est la commande ping.

La documentation Ansible en ligne

Cette commande va permettre de tester si tous les serveurs sont accessibles et disponibles.

L'option -k permet de saisir le mot de passe pour se connecter aux serveurs. Le mot de passe doit être identique à tous les serveurs. Pour éviter cette manipulation, il est possible d'utiliser une connexion via une clé ssh.

L'option -m permet d'indique la commande à utiliser (ping).

Le mot clé all permet d'indiquer que nous souhaitons exécuter la commande sur tous les hosts présents dans le fichier inventaire.

$ ansible -k -m ping all
SSH password:
SERVER5 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER3 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER4 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER6 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Si je souhaite exécuter la commande ping uniquement sur mes hosts nginx

$ ansible -k -m ping nginx
SSH password:
SERVER2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
SERVER1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Rien de plus simple.

Etiquettes: 

Ansible: la commande setup

La comande setup permet d'obtenir un inventaire exhaustif des hosts de l'inventaire.

La documentation Ansible en ligne

Attention, cette commande retourne un json de plus de 1000 lignes contenant toutes les informations du ou des hosts interrogés.

Chaque clé du json peut être utilisée dans les playbooks Ansible grâce à la variable ansible_facts.

$ ansible -m setup SERVER1
SERVER1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.50.2.253",
            "172.17.0.1"
        ],
...
       "module_setup": true
    },
    "changed": false
}

Il est possible de filtrer directement sur un paramètre précis.

$ ansible -m setup SERVER1 -a "filter=ansible_processor_nproc"
SERVER1 | SUCCESS => {
    "ansible_facts": {
        "ansible_processor_nproc": 2
    },
    "changed": false
}

Pour info, tout comme les hosts présents dans le fichier inventaire, le serveur Ansible lui-même peut être utilisé grâce au mot clé localhost.

$ ansible -m setup localhost -a "filter=ansible_loadavg"
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_loadavg": {
            "15m": 0.43,
            "1m": 1.9,
            "5m": 0.78
        }
    },
    "changed": false
}

Etiquettes: 

Ansible: la commande "command"

La commande command permet d'exécuter une commande simple.

Voir la commande shell pour des exécutions plus complexes.

La documentation Ansible en ligne

Le commande n'étant pas exécutée par l'interpréteur de commande, l'utilisation des caractères spéciaux tels que * < > | ; & ne fonctionne pas.

Quelques exemples:

$ ansible -m command -a "uptime" SERVER1
SERVER1 | CHANGED | rc=0 >>
 13:37:18 up 12 days,  3:58,  2 users,  load average: 0,16, 0,11, 0,25

$ ansible -m command -a "w" SERVER2
SERVER2 | CHANGED | rc=0 >>
 13:41:22 up 12 days,  4:02,  2 users,  load average: 0,10, 0,09, 0,20
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
user pts/0    10.99.17.12      lun.10    2.00s  1.31s  0.00s w
user pts/1    10.99.17.12      09:53   34:37   0.25s  0.01s sshd: user [priv]

$ SERVER3 -m command -a "date" SERVER3
localhost | CHANGED | rc=0 >>
mer. juin  5 13:55:55 CEST 2024

 

Etiquettes: 

Ansible: la commande shell

La commande shell permet d'exécuter des commandes simples mais aussi complexes et également sous forme de scripts.

Documentation Ansible en ligne

Exemples:

$ ansible -m shell -a 'find /var/log/nginx/ -type f -name "access.log*" | xargs stat --printf "%-80n%-15s%-35w\n"' nginx
SERVER2 | CHANGED | rc=0 >>
/var/log/nginx/access.log-20240116                                              7052050        2023-11-15 17:37:33.620096531 +0100
/var/log/nginx/access.log                                                       10191          2024-01-16 09:28:20.137458998 +0100
SERVER1 | CHANGED | rc=0 >>
/var/log/nginx/access.log-20240116                                              2101711        2023-11-15 16:03:11.480149331 +0100
/var/log/nginx/access.log                                                       22278          2024-01-16 09:29:28.959290202 +0100

$ ansible -m shell -a 'cat /etc/passwd | grep root' SERVER1:SERVER2
SERVER1 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
SERVER2 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash

$ ansible -m shell -a 'echo "Hello world!" > /tmp/hello_world ; ls -l /tmp/hello_world ; cat /tmp/hello_world' localhost
localhost | CHANGED | rc=0 >>
-rw-r--r-- 1 user group 13  5 juin  14:20 /tmp/hello_world
Hello world!

Etiquettes: 

Ansible: les playbooks ...

Quelques exemples de playbooks.

Ansible: playbooks: Template basique

Voici un template basique pour démarrer un nouveau playbook Ansible.

- name: Description du playbook
  hosts: all
  gather_facts: true
  tasks:
    ...


Le format est en YAML.
Il faut être très rigoureux concernant l'indentation.
Enormément d'erreurs sont dues à une mauvaise indentation.

La clé name permet de nommer le playbook
La clé hosts permet d'indiquer la liste des hosts présents dans l'inventaire à utiliser
La clé gather_facts permet d'obtenir un inventaire complet des hosts (true ou false)
La clé tasks permet de renseigner toutes les tâches que le playbook doit exécuter.

Concernant la clé hosts, la sélection est multiple.

Par exemple, le mot clé all permet d'indiquer la liste complète de tous les hosts.
Si je veux un host en particulier
    hosts: SERVER1 # (attention à la casse pour les nom de hosts).
Si je veux deux hosts
    hosts: SERVER1:SERVER2
Si je veux tous les hosts exceptés le SERVER2
    hosts: all:!SERVER2
Si je veux mes hosts nginx de prod
    hosts: nginx:&prod

Etiquettes: 

Ansible: playbooks: Template élaboré

Voici un template plus élaboré pour démarrer un nouveau playbook Ansible et prenant en compte des variables.

---
# Description
- name: Description du playbook
  hosts: localhost
  gather_facts: true
  gather_subset: all
  become: true
  serial: false
  collections:
    - community.vmware
  vars:
    bar: 1
    foo: "bar"
    bar_dict: {
      foo: {bar: "foo", foo: 2},
      bar: {bar: "bar", foo: 3}
    }
    date: "{{lookup('pipe','date +%Y%m%d')}}"
    bar_list:
      - bar
      - foo
    tmp_dir: /tmp
    tmp_file: "{{ tmp_dir }}/toto"
    enc_vault_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          32326566396363316431393632363934396534323464343139636430386337633930383363303531
          3765303765326332316633316565356533623630383337320a306131613737336662616234343135
          37373938646264653264653639386262303662366464396163346366666230646332393639653532
          3761333761623763610a656337316333393963386466303734663066313334396234656166383731
          3830
    ansible_distro: "{{ ansible_distribution | lower }}"
  vars_prompt:
    - name: install_git
      prompt: Faut-il installer le programme git (o/n) ?
      private: false
      default: o
  pre_tasks:
      - include_vars: vars.yaml
  tasks:
    - name: the first task
      # Comment
      ansible.builtin.debug:
        msg: |
          let's go
          inventory_hostname: {{ inventory_hostname }}
          bar: {{ bar }}
          foo: {{ foo }}
          bar_dict: {{ bar_dict }}
          bar_dict['bar']['foo']: {{ bar_dict['bar']['foo'] }}
          date: {{ date }}
          bar_list: {{ bar_list }}
          tmp_dir: {{ tmp_dir }}
          tmp_file: {{ tmp_file }}
          dec_vault_password: {{ enc_vault_password }}
          install_git: {{ install_git }}
          include_vars__other: {{ other }}
          ansible_distribution: {{ ansible_distribution }}
          ansible_distro: {{ ansible_distro }}
        verbosity: 0
 

 

Quelques petites explications:

- gather_facts (true/false) permet d'obtenir l'inventaire du ou des hosts
- gather_subset (liste exhaustive)  permet d'obtenir des informations supplémentaire concernant le ou les hosts
- become (true/false) permet d'obtenir les droits SUDO
- serial (true/false) permet d'indiquer s'il faut exécuter les tâches en série, host par host ou simultanément sur tous les hosts
- collections permet d'inclure des plugins Ansible supplémentaires
- vars permet d'inclure différentes variables (string, int, dict, list)
- vars_prompt permet d'inclure des variables initialisées par un prompt lors de l'exécution du playbook
- pre_tasks permet d'exécuter des tâches avant l'exécution des tâches du playbook (j'inclus des variables globales grâce à include_vars)

Pour info, la commande lookup('pipe', ...) permet de récupérer le résultat de la sortie standard de la commande passée en paramètre.
Plus d'info ici

Etiquettes: 

Apache

Logo Apache

Invalid command 'php_flag'

Erreur rencontrée :

Après une mise à jour d'Apache sur mon serveur, celui-çi ne voulait plus redémarrer avec cette erreur en retour.

Invalid command 'php_flag', perhaps misspelled or defined by a module not included in the server configuration

Résolution :

Installé le paquet libapache2-mod-php5

$ sudo apt-get install libapache2-mod-php5
$ sudo service apache2 start

Load Balancing

Effectuer du load balancing avec les modules proxy, proxy_balancer et proxy_ajp d'Apache2

Activer les modules nécessaires :

$ sudo a2enmod proxy
$ sudo a2enmod proxy_balancer
sudo a2enmod proxy_ajp

Paramétrage en mode HTTP :

Ajouter dans le fichier httpd.conf situé dans le répertoire conf d'Apache (ajuster les valeurs en rouge)

<Proxy balancer://mycluster>
BalancerMember http://adresseIpDuServeur1 min=10 max=500 timeout=60 loadfactor=1
BalancerMember http://adresseIpDuServeur2 min=10 max=500 timeout=60 loadfactor=1
</Proxy>
 
ProxyPreserveHost On
ProxyRequests Off
ProxyPass /monAdresse balancer://mycluster stickysession=JSESSIONID scolonpathdelim=On
ProxyPassReverse /monAdresse balancer://mycluster stickysession=JSESSIONID scolonpathdelim=On
 

Paramétrage en mode AJP (Tomcat) :

Ajouter dans le fichier httpd.conf situé dans le répertoire conf d'Apache (ajuster les valeurs en rouge)

ProxyPass /monAdresse balancer://mycluster stickysession=JSESSIONID scolonpathdelim=On
<Proxy balancer://mycluster>
BalancerMember ajp://adresseIpDuServeur1:8009/ min=10 max=500 timeout=60 route=node1 loadfactor=1
BalancerMember ajp://adresseIpDuServeur1:8009/ min=10 max=500 timeout=60 route=node2 loadfactor=1
Order deny,allow
Allow from all
</Proxy>
 
Modifier les fichiers serveur.xml situé dans les répertoires conf de Tomcat (sur tous les serveurs à utiliser)
Décommenter la ligne :
<Connector maxThreads="400" port="8009" protocol="AJP/1.3" redirectPort="8443" />
Décommenter et modifier la ligne (incrémenter la valeur du node pour chaque serveur) :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="noden">
 
Pour finir, redémarrer tous les serveurs Tomcat et le serveur Apache pour prendre en compte les modifications.

Redirection automatique http vers https

Pré-requis :

  1. Activer le mod_ssl sur Apache
  2. Générer un certificat SSL
  3. Avoir son site accessible aussi bien en http qu'en https

Comment rediriger automatiquement une connexion http en https pour un virtual host précis:

Par exemple http://monDomaine.com --> https://monDomaine.com

  1. Editer le fichier de configuration du virtual host (/etc/apache2/sites-enabled/monDomaine.com.conf)
  2. Ajouter les informations suivantes indiquées en rouge
    <VirtualHost *:80>
    ServerName monDomaine.com
    CustomLog /var/log/apache2/toto-access.log combined
    ErrorLog /var/log/apache2/toto-error.log
    <IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteLog /var/log/apache2/https_rewrite.log
    RewriteLogLevel 1
    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R]
    </IfModule>
    </VirtualHost>
  3. Redémarrer Apache (/etc/init.d/apache2 restart)
 
Pour rediriger une page bien précise :
 
 
Préciser à la fin du fichier de configuration du virtual host l'information suivante
 

RedirectPermanent /login "https://monDomaine.com/login"

 

Réécriture d'URL et redirection de ports Apache

Sous Apache, la réécriture d'URL et la redirection de ports est possible grâce au module "mod_rewrite".

Pour l'activer, rien de plus simple :

$ a2enmod rewrite

Les informations de réécriture sont à renseignées, soit dans le fichier de configuration d'Apache (/etc/apache2/apache2.conf), soit dans les fichiers de configuration des virtualhosts.

Réécitrure d'URL

  • Pour réécrire / en /ma_page

> RewriteEngine On
> RewriteRule ^/$ /ma_page [L,R]

  • Pour réécrire /mon_ancienne_page en /ma_nouvelle_page

> RewriteEngine On
> RewriteRule ^/mon_ancienne_page$ /ma_nouvelle_page [L,R]

Redirection de ports

  • Pour rediriger toutes les requêtes du port 8080 vers le port 80

> RewriteEngine On
> RewriteCond  %{SERVER_PORT} ^8080$
> RewriteRule ^(.*) http://%{SERVER_NAME}:80/ [L,R]

  • Pour rediriger 'http://mon_site:8080/mon_ancienne_page' vers 'http://mon_site/ma_nouvelle_page'

> RewriteEngine On
> RewriteCond  %{SERVER_PORT} ^8080$
> RewriteRule ^(.*)/mon_ancienne_page$ http://%{SERVER_NAME}:80/ma_nouvelle_page [L,R]

Etiquettes: 

Tester les capacités d'un serveur web

Tests réalisés sur une distribution Linux style Ubuntu

$ sudo apt-get install apache2-utils
$ ab -n 1000 -c 20 http://monserveur/index.html

  • -n = nombre de requetes
  • -c = nombre de requetes en parallèle (cad envoyé au meme moment)

Cela permet de faire des tests de charge sur votre serveur et de récupérer ses capacités en terme de nombre de requete / seconde.

Etiquettes: 

Tomcat + Apache + SSL

Voici un exemple de configuration permettant d'avoir un frontend Apache ou Nginx, un backend Tomcat (ou autre) et le tout avec une connexion sécurisée SSL.

Un frontend Apache ou Nginx en mode proxy avec un backend Tomcat, rien de plus commun, une simple configuration comme celle-ci suffit à obtenir le résultat souhaité dans la conf virtualhost d'Apache ou de Nginx.

Pour Apache:

<VirtualHost *:80>
ServerName mondomaine.fr
...
ProxyPass           /         http://localhost:8080
ProxyPassReverse    /         http://localhost:8080
...
</VirtualHost>

Pour Nginx:

server {
    server_name mondomaine.fr;
    listen 80;    
    location / {
        include proxy_params;
        proxy_pass http://localhost:8080;
    }
}

Pour la connexion SSL, une modification doit être faite dans la configuration server.xml du Tomcat.

Il est nécessaire de définir un nouveau connector pour la connexion sécurisée.

<Connector connectionTimeout="20000" port="6443" protocol="HTTP/1.1" scheme="https" secure="true" proxyPort="443" proxyName="mondomaine.fr" />

Il faut définir un port d'écoute (6443), le "scheme" https, la valeur true à la propriété "secure", le port du serveur proxy (dans la conf apache / nginx) et enfin le nom du domaine associé (dans la conf apache / nginx).

Pour terminer, la conf à faire dans le virtualhost apache / nginx

Pour apache:

<VirtualHost *:443>
    ServerName mondomaine.fr
    ...
    SSLEngine on
    SSLCertificateFile "C:\Certbot\live\mondomaine.fr\fullchain.pem"
    SSLCertificateKeyFile "C:\Certbot\live\mondomaine.fr\privkey.pem"
    ...
    ProxyPass           /         http://localhost:6443
    ProxyPassReverse    /        http://localhost:6443
    ...
</VirtualHost>

Pour nginx:

server {
        server_name mondomaine.fr;
        location / {
                include proxy_params;
                proxy_pass http://localhost:6443;
        }
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/mondomaine.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mondomaine.fr/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

Attention, la configuration du serveur proxy s'effectue bien en HTTP, mais sur le port 6443.

Seule la connexion au frontend est sécurisée (avec le monde extérieur).
Celle entre le frontend et le backend n'a pas besoin de l'être puisqu'ils sont sur le même serveur.

Drupal

Logo Drupal

Tous mes trucs et astuces pour utiliser Drupal

Etiquettes: 

Débloquer un site hors-ligne

Quand un site Drupal est mis hors-ligne, plus personne ne peut y accéder.
Cette option est pratique pour des opérations de maintenance et/ou de mises à jour.
Le seul inconvénient, ne pas quitter sa session avant d'avoir remis le site en ligne sous peine de ne plus pouvoir y accéder.
Si par malheur cela se produit, pas de panique, une solution simple existe afin de remettre son site en ligne.

Pour débloquer son site, il suffit de s'y connecter en y ajoutant /user ou /?q=user à la fin de l'adresse.

http://monSite.fr/user (si les URLs simplifiées sont activées)
ou
http://monSite.fr/?q=user (si les URLs simplifiées ne sont pas activées)

Seul l'administrateur est autorisé à se connecter.

Etiquettes: 

Installer la barre de progression PECL pour Drupal (sous Linux)

1 - Installer le paquet php5-dev.

$ sudo apt-get install php5-dev

2 - Se placer dans le répertoire /tmp.

$ cd /tmp/

3 - Vérifier à l'adresse http://pecl.php.net/package/uploadprogress la dernière version disponible puis télécharger l'archive souhaitée.

$ wget http://pecl.php.net/get/uploadprogress-1.0.3.1.tgz

4 - Extraire l'archive téléchargée.

$ tar xzvf uploadprogress-1.0.3.1.tgz

5 - Se placer dans le répertoire uploadprogress-x.x.x

$ cd uploadprogress-1.0.3.1/

6 - Compiler les sources et installer le programme

$ phpize
$ ./configure
$ make
$ sudo make install

7 - Rajoutez "extension=uploadprogress.so" au fichier php.ini.

8 - Redémarrer apache.

$ /etc/init.d/apache2 restart
 

Etiquettes: 

MAJ des modules Drupal sur un hébergement OVH

Si Drupal est installé sur un hébergement OVH, les mises à jour de modules via l'interface de mise à jour de Drupal ne fonctionne plus.

Obligation de télécharger le module à mettre à jour et de l'uploader sur le serveur via le FTP.

Pour retrouver un comportement standard, il suffit de modifier un tout petit fichier situé à la racine du serveur FTP.

Il s'agit du fichier .ovhconfig

Dans ce fichier, il suffit de modifier la ligne qui contient

container.image=legacy

par

container.image=stable

Tout devrait rentrer dans l'ordre

Mise à jour Drupal 7.24 & 6.29

Suite à la mise à jour de Drupal en version 7.24 & 6.29, il est indipensable d'ajouter ou de modifier le fichier .htaccess présent dans le répertoire sites/default/files.

C'est une question de sécurité.
Cela empêchera toute exécution de scripts malicieux.

$ cat sites/default/files/.htaccess
# Turn off all options we don't need.
Options None
Options +FollowSymLinks
 
# Set the catch-all handler to prevent scripts from being executed.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
  # Override the handler again if we're run later in the evaluation list.
  SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
</Files>
 
# If we know how to do it safely, disable the PHP engine entirely.
<IfModule mod_php5.c>
  php_flag engine off
</IfModule>

Si le fichier .htaccess n'existe pas, le créer.

Il faut également protéger le répertoire temporaire de Drupal avec ce même fichier .htaccess.
Pour connaitre le répertoire temporaire de Drupal, il suffit d'aller dans "Configuration --> Média --> Système de fichiers".
En règle générale, sur un serveur Linux, il s'agit du répertoire /tmp.

Pour le protéger, rien de plus simple, il suffit d'effectuer une copie du fichier sites/default/files/.htaccess dans le répertoire /tmp.

Sur un serveur Linux, le répertoire /tmp est vider à chaque redémarrage.
Il faut donc penser à recopier le fichier .htaccess après chaque redémarrage.
Sinon, une petite tâche cron et le problème est réglé :

$ crontab -l
# m h  dom mon dow   command
@reboot cp -a /var/www/public_html/sites/default/files/.htaccess /tmp/

Penser à modifier le chemin d'accès au fichier .htaccess.

Pour finir, supprimer le fichier CHANGELOG.txt. Cela évitera à n'importe qui de connaitre la version actuellement installée.

Mon module Drupal : Test de connexion

Si mon module Drupal "Test de connexion" vous intéresse, vous pouvez le télécharger en cliquant sur les liens ci-dessous correspondant à votre version de Drupal.

Disponible pour la version 6.x de Drupal :

http://www.isbeta.fr/6ac7a

MD5SUM : 25c575cc262272e6a2a7dc656196c25b  test_connexion.tar.gz

Disponible pour la version 7.x de Drupal :

http://www.isbeta.fr/91e09

MD5SUM : 9b7a14d2e38c1111f2cc841df365f839  test_connexion-7.x-1.0.tar.gz

Etiquettes: 

Récupérer (extraire) les données d'un noeud

Dans Drupal 7, il est tout à fait possible d'extraire n'importe quelles données d'un noeud (Page, Article etc etc) grâce aux API Drupal.

Quelles sont les données récupérables :

Exemple avec un article test créé spécialement.

Pour afficher cette description, il suffit d'utiliser l'API node_load en indiquant le NID du noeud concerné :

<?php
$nid = 412;
$node = node_load($nid);
print_r($node);
?>

Ce qui donne :

stdClass Object
(
    [vid] => 431
    [uid] => 0
    [title] => Article vide pour test
    [log] => 
    [status] => 0
    [comment] => 2
    [promote] => 0
    [sticky] => 0
    [nid] => 412
    [type] => story
    [language] => fr
    [created] => 1334738152
    [changed] => 1386527877
    [tnid] => 0
    [translate] => 0
    [revision_timestamp] => 1386527877
    [revision_uid] => 1
    [taxonomy_vocabulary_2] => Array
        (
        )

    [body] => Array
        (
            [und] => Array
                (
                    [0] => Array
                        (
                            [value] => 

Contenu vide pour test

[summary] => [format] => 2 [safe_value] =>

Contenu vide pour test

[safe_summary] => ) ) ) [rdf_mapping] => Array ( [rdftype] => Array ( [0] => sioc:Item [1] => foaf:Document ) [title] => Array ( [predicates] => Array ( [0] => dc:title ) ) [created] => Array ( [predicates] => Array ( [0] => dc:date [1] => dc:created ) [datatype] => xsd:dateTime [callback] => date_iso8601 ) [changed] => Array ( [predicates] => Array ( [0] => dc:modified ) [datatype] => xsd:dateTime [callback] => date_iso8601 ) [body] => Array ( [predicates] => Array ( [0] => content:encoded ) ) [uid] => Array ( [predicates] => Array ( [0] => sioc:has_creator ) [type] => rel ) [name] => Array ( [predicates] => Array ( [0] => foaf:name ) ) [comment_count] => Array ( [predicates] => Array ( [0] => sioc:num_replies ) [datatype] => xsd:integer ) [last_activity] => Array ( [predicates] => Array ( [0] => sioc:last_activity_date ) [datatype] => xsd:dateTime [callback] => date_iso8601 ) ) [cid] => 0 [last_comment_timestamp] => 1334738152 [last_comment_name] => [last_comment_uid] => 1 [comment_count] => 0 [name] => [picture] => 0 [data] => )

Si on souhaite afficher la date de création du noeud, il suffit d'écrire

<?php
$nid = 412;
$node = node_load($nid);
$timestamp = $node->created;
$date = date("d-m-Y", $timestamp);
echo $date;
?>

Ce qui donne :

18-04-2012

Pour afficher le contenu

<?php
$nid = 412;
$node=node_load($nid);
print $node->body['und']['0']['value'];
?>

Ce qui donne :

Contenu vide pour test

Dans Drupal 6, l'affichage du contenu s'effectue de cette manière :

<?php
$nid = 412;
$node=node_load($nid);
print $node->body;
?>

Les arrays ['und']['0']['value'] sont inexistants.

Etiquettes: 

Réinitialiser le mot de passe administrateur de Drupal

En cas de perte des identifiants, il faut savoir que tous les comptes utilisateurs sont enregistrés dans la table users.

Dans cette table, à chaque compte correspond un id unique (colonne UID).
Pour Drupal, les utilisateurs anonymes et privilégiés sont systématiquement les deux premières lignes de cette table et ont pour UID respectif 0 et 1.

Pour changer identifiant et mot de passe, il suffit d'exécuter la requête SQL suivante avec la console MySQL ou PhpMyAdmin :

$ mysql -u root -p
> use drupal
> update users set
        name = 'identifiant_admin',
        pass = md5('mot_de_passe')
    where uid = 1;

Eclipse

Logo Eclipse

Tous mes trucs et astuces pour utiliser Eclipse

Raccourcis clavier

Liste des raccourcis clavier afin d'utiliser Eclipse plus rapidement.

  • Pour obtenir la liste complète des raccourcis clavier, appuyer sur CTRL + SHIFT + L
  • Pour dupliquer une ligne, appuyer sur CTRL + ALT + Up
  • Pour commenter ou décommenter une ligne ou un bloc, appuyer sur CTRL + SHIFT + /
  • Pour utiliser l'auto-complétion, appuyer sur CTRL + Espace
  • Pour indenter le code, appuyer sur CTRL + SHIFT + I
  • Pour effacer la ligne courante, appuyer sur CTRL + D
  • Pour sélectionner un bloc de texte, appuyer sur ALT + SHIFT + A (très pratique pour sélectionner une partie d'un code sur plusieurs lignes pour pouvoir le modifier en une seule fois ou écrire un commentaire sur plusieurs lignes tout en restant aligné - le curseur se transforme en + permettant de dessiner une zone d'encadrement)
Etiquettes: 

Firefox

Logo Firefox

Tous mes trucs et astuces pour utiliser Firefox

Etiquettes: 

Aperçu d'un site dans différentes résolutions d'affichages

Comment savoir si son site internet s'affiche correctement dans différentes résolution.

Afin de simuler l'affichage de son site sur un PC, un portable, un netbook, une tablette et un smartphone, Firefox possède une fonction permettant de manipuler la résolution d'affichage.

Pour activer l'option, appuyer sur les touches Ctrl + Shift + M

Idem pour désactiver l'option.

Etiquettes: 

Installer son propre serveur de synchronisation WEAVE pour Firefox

Logo Weave

Pour pouvoir sauvegarder et/ou synchroniser tous ses marque-pages, mots de passe, préférences et son historique de navigation du navigateur Firefox, il faut utiliser l'extension Sync.

Depuis la version 4 de Firefox, cette extension est installée par défaut.

Pour cela, il suffit de se créer un nouveau compte sur le serveur de la fondation Mozilla, via les options de Sync.

Toutes ces données sont cryptées et sauvegardées sur leur serveur.

De cette manière, il est possible de synchroniser toutes ses données entre plusieurs navigateurs Firefox (à la maison, sur l'ordinateur familial, sur le portable et au travail)

Cela permet également de faire des sauvegardes régulières. Plus besoin d'installer des extensions supplémentaires pour sauvegarder les mots de passe et les marque-pages.

Mais il est également possible d'avoir son propre serveur de synchronisation grâce à l'application Weave écrite en PHP.


Pré-requis :

Avoir un serveur LAMP (Linux Apache MySQL PHP)

Installé l'extension SQLite pour PHP

$ apt-get install php5-sqlite


Installation :

Télécharger l'archive weave_minimal.tgz :

$ cd /var/www
$ wget http://www.isbeta.fr/4f612 -O weave_minimal.tgz

MD5SUM : 12c7dfef24eb0bb249a1f4723cd8daf6  weave_minimal.tgz

Extraire l'archive :

$ tar -xvzf ./weave_minimal.tgz

Ajouter un alias dans la configuration Apache se nommant /weave et pointant vers /var/www/weave_minimal/index.php

Modifier le propriétaire du dossier weave_minimal :

$ chown -R www-data:www-data /var/www/weave-minimal/

Redémarrer Apache :

$ service apache2 restart

Pour créer la base de données, ouvrir un navigateur, Firefox de préférence ;o) et saisir cette adresse dans la barre d'adresse  > http://localhost/weave/1.0/blah/info/collection <
Si tout se passe bien, saisir "blah" pour l'utilisateur et laisser le mot de passe vide.
Valider en appuyant sur Entrée puis quitter Firefox (ou l'autre navigateur ;o)

La base de données "weave_db" doit être créée (pour vérifier):

$ cd /var/www/weave_minimal/ && ls -l

Pour créer un utilisateur :

$ cd /var/www/weave_minimal/
$ php ./create_user

Ce script PHP permet de créer, modifier ou supprimer un utilisateur

L'installation du serveur est terminée, il ne reste plus qu'à configurer Sync.

  1. Cliquer sur le menu Firefox puis Configurer Sync
     
  2. Cliquer sur Se connecter de la section J'ai déjà un compte Firefox Sync
     
  3. Cliquer sur  Je n'ai pas l'appareil avec moi
     
  4. Saisir le nom de l'utilisateur et le mot de passe
     
  5. Dans Serveur, choisir Utiliser un serveur personnalisé... puis saisir l'adresse http du serveur perso
    < http://mondomaine.com/weave/ > (le protocole HTTPS est recommandé pour plus de sécurité mais il faut le praramétré au niveau du serveur Apache)
     
  6. Saisir "1234" pour la Clé de synchronisation puis cliquer sur Suivant

Si tout se passe comme il faut, une page doit s'ouvrir dans Firefox indiquant que la configuration est terminée.

Répéter les opérations 1 à 6 pour tous les navigateurs Firefox à synchroniser.

Etiquettes: 

MySQL

Logo MySql
Tous mes trucs et astuces pour utiliser MySQL

Afficher verticalement le résulat d'une requête

Généralement, le résultat d'une requête est affiché horizontalement.

mysql> SELECT * FROM test2 LIMIT 0,5;
+----+-----+
| id | val |
+----+-----+
|  1 |  10 |
|  2 |  20 |
|  3 |  30 |
|  4 |  40 |
|  5 |  50 |
+----+-----+
5 rows in set (0.00 sec)

Pour afficher le résultat verticalement, il suffit de remplacer le ";" par "\G".

mysql> SELECT * FROM test2 LIMIT 0,5\G
*************************** 1. row ***************************
 id: 1
val: 10
*************************** 2. row ***************************
 id: 2
val: 20
*************************** 3. row ***************************
 id: 3
val: 30
*************************** 4. row ***************************
 id: 4
val: 40
*************************** 5. row ***************************
 id: 5
val: 50
5 rows in set (0.00 sec)

Ajouter un super utilisateur MySql

Les commandes suivantes ajoute l'utilisateur "superUser" ayant tous les droits "ALL PRIVILEGES.....WITH GRANT OPTION" sur toutes les bases de sonnées "*.*", avec le mot de passe "secret" et pouvant se connecter de n'importe quelle machine "%":

Se connecter au serveur mysql avec l'utilisateur root

$ mysql -u root -p
> GRANT ALL PRIVILEGES ON *.* TO 'superUser'@'%' IDENTIFIED BY 'secret' WITH GRANT OPTION;
> FLUSH PRIVILEGES;

 

Etiquettes: 

Connexion automatique à un serveur MySQL

Sous Ubuntu, par exemple, pour se connecter à un serveur MySQL, il est nécessaire de saisir dans la console la commande suivante :

$ mysql -h mon_serveur_mysql -u nom_user -p nom_base_de_donnees

-h pour indiquer le serveur mysql (optionnel si le serveur est localhost)
-u pour indiquer le nom d'utilisateur
-p pour permettre la saisie du mot de passe
et on termine la commande par le nom de la base de données à utiliser (optionnel).

Il existe un moyen plus simple pour se connecter à MySQL surtout si les paramètres de connexions sont souvent les mêmes.
Pour cela, il suffit de créer dans son répertoire personnel le fichier .my.cnf.

$ touch ~/.my.cnf

D'y renseigner les informations suivantes :

$ cat ~/.my.cnf
[client]
user = nom_user
password = mon_password
host = adresse_ip_ou_fqdn_du_serveur_mysql

De protéger le fichier des regards indiscrets

$ chmod 0600 ~/.my.cnf

Dorénavant, cette simple commande suffira à se connecter au serveur MySQL :

$ mysql nom_base_de_donnees

Créer un trigger

Pour créer un trigger dans MySql suite à une insertion de données avec enregistrement de données dans une autre table

$ .
> CREATE TRIGGER nom_du_trigger AFTER INSERT ON nom_de_la_table_a_surveiller
> FOR EACH ROW
> INSERT INTO nom_de_la_table (champs1, champs2, champs3) VALUES ( valeur1, valeur2, valeur3);

 

Pour créer un trigger dans MySql suite à une modification de données avec enregistrement de données dans
une autre table

$ .
> CREATE TRIGGER nom_du_trigger AFTER UPDATE ON nom_de_la_table_a_surveiller
> FOR EACH ROW
> INSERT INTO nom_de_la_table (champs1, champs2, champs3) VALUES ( valeur1, valeur2, valeur3);

Pour créer un trigger dans MySql suite à une suppression de données avec enregistrement de données dans
une autre table

$ .
> CREATE TRIGGER nom_du_trigger AFTER DELETE ON nom_de_la_table_a_surveiller
> FOR EACH ROW
> INSERT INTO nom_de_la_table (champs1, champs2, champs3) VALUES ( valeur1, valeur2, valeur3);


 
A la place du mot clé AFTER, nous pouvons également utiliser BEFORE qui exécute l'action avant l'évènement.
 
Pour récupérer les valeurs de la table surveillée utiliser les mots clés OLD et/ou NEW (OLD.nom_du_champ ou NEW.nom_du_champ)
 
Par exemple :

$ .
> CREATE TRIGGER mon_trigger AFTER INSERT ON ma_table
> FOR EACH ROW
> INSERT INTO ma_table_log (id_ajoute, valeur1_ajoutee, valeur2_ajoutee) VALUES ( NEW.id, NEW.valeur1,
NEW.valeur2);

 

Etiquettes: 

Gestion des dates

Dans MySql, les dates sont au format yyyy-mm-dd (aaaa-mm-jj)
 
Ajouter x jour(s) à un champ de type date :
SELECT DATE_ADD(champ_date, INTERVAL x DAY) FROM ma_table
mysql> SELECT DATE_ADD('2013-09-06', INTERVAL 10 DAY) FROM sys;
2013-09-16
ou
SELECT champ_date + INTERVAL x DAY FROM ma_table
mysql> SELECT '2013-09-06' + INTERVAL 10 DAY FROM sys;
2013-09-16
Ajouter x mois à un champ de type date :
SELECT DATE_ADD(champ_date, INTERVAL x MONTH) FROM ma_table
mysql> SELECT DATE_ADD('2013-09-06', INTERVAL 2 MONTH) FROM sys;
2013-11-06
Ajouter x année(s) à un champ de type date :
SELECT DATE_ADD(champ_date, INTERVAL x YEAR) FROM ma_table
mysql> SELECT DATE_ADD('2013-09-06', INTERVAL 2 YEAR) FROM sys;
2015-09-06
Mettre à jour un champ de type date en y ajoutant x jour(s) et/ou x mois et/ou x année(s) :
UPDATE ma_table SET champ_date = DATE_ADD(champ_date, INTERVAL x DAY)
UPDATE ma_table SET champ_date = DATE_ADD(champ_date, INTERVAL x MONTH)
UPDATE ma_table SET champ_date = DATE_ADD(champ_date, INTERVAL x YEAR)
Pour un retranchement, utiliser le signe moins (-) devant la valeur x
mysql> SELECT DATE_ADD('2013-09-06', INTERVAL -2 YEAR) FROM sys;
2011-09-06
Afficher la date du jour dans une requête :
SELECT CURRENT_DATE FROM ma_table
mysql> SELECT CURRENT_DATE FROM sys;
2013-09-06
ou
SELECT CURDATE() FROM ma_table
mysql> SELECT CURDATE() FROM sys;
2013-09-06
Afficher le jour, le mois ou l'année d'un champ de type date :
SELECT DAY(champ_date) FROM ma_table
mysql> SELECT DAY('2013-09-06') FROM sys;
6
SELECT MONTH(champ_date) FROM ma_table
mysql> SELECT MONTH('2013-09-06') FROM sys;
9
SELECT YEAR(champ_date) FROM ma_table
mysql> SELECT YEAR('2013-09-06') FROM sys;
2013

 

Etiquettes: 

Ignorer l'erreur "Duplicate entry for key"

Lors d'une insertion en masse dans une table Mysql, si une clé existe déjà, la totalité de l'insertion n'est pas exécutée.

Pour ignorer cette erreur, il suffit d'utiliser le mot clé IGNORE dans la requête d'insertion.

Exemple :

Définition de la table test2

CREATE TABLE IF NOT EXISTS `test2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `val` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `val` (`val`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Définition d'une clé unique sur le champ "val".

Données de la table

mysql> SELECT * FROM test2;
+----+-----+
| id | val |
+----+-----+
|  1 |  10 |
|  2 |  20 |
|  3 |  30 |
|  4 |  40 |
|  5 |  50 |
+----+-----+
5 rows in set (0.00 sec)

Requête d'insertion en masse

mysql> INSERT INTO test2 (val) VALUES (60), (70), (10), (80);
ERROR 1062 (23000): Duplicate entry '10' for key 'val'
mysql> SELECT * FROM test2;
+----+-----+
| id | val |
+----+-----+
|  1 |  10 |
|  2 |  20 |
|  3 |  30 |
|  4 |  40 |
|  5 |  50 |
|  6 |  60 |
|  7 |  70 |
+----+-----+
7 rows in set (0.00 sec)

Lors de l'insertion, une erreur est générée pour la valeur "10".
Celle-ci existe déjà dans la table.

Le "SELECT" montre que seules les valeurs "60" et "70" ont été insérées.
La valeur "80" n'a pas été insérée car la requête a générée une erreur au moment de l'insertion de la valeur "10".

Requête d'insertion avec le mot clé "IGNORE"

mysql> INSERT IGNORE INTO test2 (val) VALUES (80), (90), (10), (100);
Query OK, 3 rows affected (0.00 sec)
Records: 4  Duplicates: 1  Warnings: 0

mysql> SELECT * FROM test2;
+----+-----+
| id | val |
+----+-----+
|  1 |  10 |
|  2 |  20 |
|  3 |  30 |
|  4 |  40 |
|  5 |  50 |
|  6 |  60 |
|  7 |  70 |
|  8 |  80 |
|  9 |  90 |
| 10 | 100 |
+----+-----+
10 rows in set (0.00 sec)

Le résultat de la requête indique que 3 insertions ont été exécutées sur 4.
La valeur "10" a été ignorée.

Le "SELECT" montre que les valeurs "80", "90" et "100" ont bien été insérées.

Etiquettes: 

MySQL: Mettre à jour le champ d'une table en fonction d'un champ d'une autre table

L'exemple suivant permet de mettre à jour le champ nom_region de la table codes_postaux à partir du champ nom_region de la table departements

UPDATE
    codes_postaux c
SET
    c.nom_region = (
    SELECT
        d2.nom_region
    FROM
        departements d2
    WHERE
        d2.code_departement = LEFT(c.Postcode_from,
        2))
WHERE
    EXISTS (
    SELECT
        1
    FROM
        departements d1
    WHERE
        d1.code_departement = LEFT(c.Postcode_from,
        2) )
    AND c.Country_name = 'FRANCE'

Ceci grâce principalement au mot clé EXISTS

Personnellement, je trouve que cette syntaxe est beaucoup plus rapide que certaines autres trouvées sur internet comme par exemple avec une jointure avec la table source entre le UPDATE et le SET

UPDATE table1 AS b
INNER JOIN table2 AS g ON b.champ1= g.champ1
SET b.champ2= g.champ2
WHERE  (b.champ2= '' or b.champ2= 0) and
  g.champ2 > 0
Etiquettes: 

Restaurer / importer une base de données MySql

Pour restaurer une base de données

Attention, il faut que la base de données soit créée auparavant

Dans une console

$ mysql -h adresse_ip -u user -p -D nom_de_la_base_de_données < nom_du_fichier_sql

Par exemple

$ mysql -h localhost -u root -p -D mesvideos < mesvideos.sql

Cela a pour effet d'importer le contenu du fichier mesvideos.sql dans la base de données mesvideos.

Pour restaurer toutes les bases de données

Pour info, il n'est pas nécessaire que les bases de données soient créées auparavant

Dans une console

$ mysql -h adresse_ip -u user -p < nom_du_fichier_sql

Par exemple

$ mysql -h localhost -u root -p < allbases.sql

Cela a pour effet d'importer la totalité des bases de données contenues dans le fichier allbases.sql.

Etiquettes: 

Sauvegarder / exporter une base de données

Pour sauvegarder une base de données

Dans une console

$ mysqldump -h adresse_ip -u user -p nom_de_la_base_de_donnees > nom_du_fichier_sql

Par exemple

$ mysqldump -h localhost -u root -p mesvideos > mesvideos.sql

Cela a pour effet d'exporter toutes les données de la base mesvideos dans le fichier mesvideos.sql

Pour sauvegarder toutes les bases de données

Dans une console

$ mysqldump -h adresse_ip -u user -p -A > nom_du_fichier_sql

Par exemple

$ mysqldump -h localhost -u root -p -A > allbases.sql

Cela a pour effet d'exporter toutes les bases de données dans le fichier allbases.sql

Etiquettes: 

Se connecter à un serveur MySql à partir de n'importe quel host

Par défaut, sur une distribution style Debian, le serveur MySql accepte les connexions uniquement à partir de la machine locale.

Toutes connexions à partir d'un host différent sont automatiquement refusées.

$ netstat -lpn | grep mysql
tcp        0      0 127.0.0.1:3306            0.0.0.0:*               LISTEN      4884/mysqld

Pour autoriser les connexions à partir de n'importe quels hosts, il faut modifier le fichier /etc/mysql/my.cnf.

Modifier le ligne bind-address = 127.0.0.1 et remplacer 127.0.0.1 par 0.0.0.0

$ cat /etc/mysql/my.cnf | grep "bind-address"
bind-address            = 0.0.0.0

Redémarrer le serveur MySql afin d'appliquer la modification

$ service mysql restart

Puis vérifier avec la commande netstat

$ netstat -lpn | grep mysql
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      6039/mysqld

Etiquettes: 

Utilisation de MySQL en console

Connexion MySQL

Pour se connecter au sevreur MySQL depuis une console

$ mysql -u nom_du_user -p

puis saisir le mot de passe

Etiquettes: 

Ajouter ou modifier le mot de passe ROOT

Pour changer le mot de passe de l'utilisateur root (qui n'est pas le même que l'utilisateur root du système), tapez la commande suivante dans un terminal :

$ sudo mysqladmin -u root password nouveau_mot_de_passe -p

Redéfinir de force le mot de passe ROOT

Méthode 1

On stoppe le serveur MySQL

$ sudo /etc/init.d/mysql stop

On se connecte au serveur sans tenir compte des privilèges utilisateurs :

$ sudo /usr/bin/mysqld_safe --skip-grant-tables & mysql -h localhost

Nous allons utiliser la table contenant les paramètres de MySQL :  

$ > use mysql

 

On met en place le nouveau mot de passe :

$ > update user set password = password('<votre_mot_de_passe>') where user = 'root' and host='localhost';

On quitte le prompt de MySQL :

$ > quit

On redémarre le serveur MySQL en utilisation normale :

$ sudo /etc.init.d/mysql restart

Méthode 2

$ sudo /etc/init.d/mysql reset-password

Méthode 3

$ sudo dpkg-reconfigure mysql-server-5.0

Etiquettes: 

Controler l'intégrité des bases de données

$ mysqlcheck -A -u root -p

Puis saisir le mot de passe

 

Enregistrer le résultat d'une requête dans un fichier

Se connecter à MySQL :

$ mysql -h localhost -u user -p

Sélectionner la base de données :

$ .
> USE maBaseDeDonnees;

Exécuter la requête :

$ .
> SELECT * INTO OUTFILE '/home/user/monFichier.txt' FROM maTable;

! Il faut que le user connecté à MySQL ait l'autorisation d'écrire dans le répertoire indiqué

Etiquettes: 

Sélectionner la base de données à utiliser

Il existe 2 manières pour sélectionner une base de données MySQL

Directement lors de la connexion

$ mysql -u root -p votre_base

Une fois connecté à MySQL mysql

$ .
> use votre_base;

Etiquettes: 

VirtualBox

VirtualBox

Tous mes trucs et astuces pour utiliser VirtualBox

Dupliquer un disque

Dans une console, saisir la commande suivante :

$ vboxmanage clonevdi nom_du_disque_a_cloner nom_du_nouveau_disque

Démarrer une machine virtuelle via une console

Sous Linux

En mode graphique :

$ vboxmanage startvm nom_de_la_machine_virtuelle

Sans interface graphique :

$ vboxmanage startvm nom_de_la_machine_virtuelle --type headless

(Dans ce mode, le contrôle de la machine est accessible uniquement via SSH)

Sous Windows

En mode graphique :

"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" startvm "nom_de_la_machine_virtuelle"

Sans interface graphique :

"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" startvm "nom_de_la_machine_virtuelle" --type headless

(Dans ce mode, le contrôle de la machine est accessible uniquement via SSH)

Arrêter une machine virtuelle via une console

$ vboxmanage controlvm nom_de_la_machine_virtuelle poweroff

ZIMBRA

Logo Zimbra

Zimbra est un serveur de messagerie avec des fonctionnalités de travail collaboratif.

La version Open Source comprend la fonction de serveur de messagerie, de calendriers partagés, de carnets d'adresses partagés, de gestionnaire de fichiers, de gestionnaire de tâches, wiki, messagerie instantanée.

A l'heure actuelle, ZIMBRA est compatible avec un certains nombres de distributions Linux 64 bits.
Toutes les versions 32 bits sont dépréciées.
 Cepandant, il est quand même possible d'installer Zimbra sur la version 32 bits de Ubuntu 8.04 LTS.
Voir la liste des distribution compatible.

 

Installation de Zimbra sur Ubuntu 8.04 LTS 32 bits

Voir la documentation Ubuntu

La dernière version disponible de Zimbra est la 7.1.3 GA Release

Zimbra nécessite un serveur à lui tout seul.
Il ne peut pas être installé sur un serveur déjà configuré comme serveur mail, http etc ...
Il lui faut une machine ayant au minimum 1 GO de mémoire vive.

Etiquettes: 

Installation Ubuntu 8.04 LTS

ZIMBRA étant installé dans le répertoire /opt, il est fortement conseillé de créer une partition spécifique ayant comme point de montage /opt.

Par exemple, pour un disque dur de 40 GB, une partition racine / de 10 GB pour le système, une partition /opt de 30 GB pour Zimbra et une partition Swap de 2.9 GB.

Procéder à l'installation d'Ubuntu 8.04 LTS avec le strict minimum.

Les paquets supplémentaires à installer sont OpenSSH-server & DNS Server

choix du nom de la machine

Au moment du choix des paquets supplémentaires à installer choisir OpenSSH-server & DNS Server

choix des paquets à installer

Pour paramétrer notre nouveau système, nous installerons Webmin

Etiquettes: 

Installation de Webmin

Se connecter au système (via SSH par exemple).

$ sudo wget http://www.webmin.com/jcameron-key.asc
$ sudo apt-key add jcameron-key.asc

Ajouter le dépot Webmin au fichier /etc/apt/sources.list :

$ echo "deb http://download.webmin.com/download/repository sarge contrib" >> /etc/apt/sources.list

$ sudo apt-get update
$ sudo apt-get install webmin

Une fois l'installation terminée, se connecter à Webmin via un navigateur web à l'adresse https://ip_du_serveur:10000

Etiquettes: 

Paramétrage de Webmin

Se connecter à Webmin via un navigateur à l'adresse https://adresse_ip_du_serveur:10000

Le user et le password correspondent à l'utilisateur créé lors de l'installation d'Ubuntu.

Le menu Webmin :

menu_webmin

Pour mettre Webmin en français, cliquer sur le menu Webmin, puis Change Language and Theme

Cocher l'option Personal choice puis sélectionner French (FR) dans la liste déroulante

Valider en cliquant sur Make Changes

Recharger Webmin en appuyant sur F5

webmin_en_francais

Via Webmin, configurer le serveur en IP fixe.

Cliquer sur le menu Réseau puis sur Configuration Réseau

configuration_reseau

Cliquer sur Interfaces Réseau puis sélectionner l'onget Interfaces Permanentes

Cliquer sur le nom de l'interface réseau (ETH0 en règle générale)

Sélectionner l'option Static configuration.

Renseigner l'adresse IP puis le masque de sous-réseau

Cliquer sur Sauvegarder

interface_eth0

Cliquer sur Retourner à configuration réseau

Cliquer sur Passerelles et Routage

Sélectionner l'option Passerelle puis y indiquer l'adresse IP du routeur

Cliquer sur Sauvegarder

adresse passerelle

Cliquer sur Adresses de la machine

Sélectionner la ligne correspondant à l'adresse IP 127.0.1.1 et la supprimer (Attention à ne pas supprimer la ligne correspondant à l'adresse IP 127.0.0.1).

Cliquer sur Ajouter une nouvelle adresse de machine

Y renseigner la nouvelle adresse IP du serveur et son nom de machine puis cliquer sur Créer

Adresse machine

Cliquer sur Retourner à configuration réseau

Cliquer sur Client DNS

Dans le champ Nom du poste, y indiquer le nom de la machine.

Dans le champ Serveurs DNS, y indiquer l'adresse IP 127.0.0.1

Dans le champ Domaines de recherche, sélectionner l'option Listé et y indiquer son nom de domaine

Client DNS

Cliquer sur Sauvegarder

Enfin, cliquer sur le bouton Appliquer la configuration

Attention, si la nouvelle adresse IP appliquée au serveur est différente de la précédente, la connexion à Webmin sera perdue. Il suffit juste de se reconnecter à Webmin via la nouvelle adresse IP du serveur.

Etiquettes: 

Configuration de Bind9

Pré-requis :

Installer le paquet dnsutils

$ sudo apt-get install dnsutils

Configuration de Bind9 :

Tous les fichiers nécessaires sont disponibles en bas de cette page

Dans les exemples suivants, mon serveur à comme adresse IP fixe : 192.168.1.10

Configuration du fichier /etc/hostname

Ce fichier doit contenir uniquement le nom de la machine.

Configuration du fichier /etc/hosts

Fichier modifié précédemment via Webmin.

Ce fichier doit ressembler à cela

Configuration du fichier /etc/resolv.conf

Fichier modifié précédemment via Webmin.

Ce fichier doit ressembler à cela

Configuration du fichier /etc/bind/named.conf

En bleu les infos à rajouter dans ce fichier.

Attention à bien respecter l'indentation du texte avec une tabulation

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the
// structure of BIND configuration files in Debian, *BEFORE* you customize
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

include "/etc/bind/named.conf.options";

// prime the server with knowledge of the root servers
zone "." {
    type hint;
    file "/etc/bind/db.root";
};

// be authoritative for the localhost forward and reverse zones, and for
// broadcast zones as per RFC 1912

zone "localhost" {
    type master;
    file "/etc/bind/db.local";
};

zone "127.in-addr.arpa" {
    type master;
    file "/etc/bind/db.127";
};

zone "0.in-addr.arpa" {
    type master;
    file "/etc/bind/db.0";
};

zone "255.in-addr.arpa" {
    type master;
    file "/etc/bind/db.255";
};

//ajout des liens vers le fichier de configuration du domaine mondomaine.com
zone "mondomaine.com" {
        type master;
        file "/etc/bind/zones/mondomaine.com.hosts";
};

//ajout des liens vers le fichier de configuration du reverse pour le LAN
zone "1.168.192.in-addr.arpa" {
        type master;
        file "/etc/bind/zones/rev.192.168.1.in-addr.arpa";
};

include "/etc/bind/named.conf.local";

Configuration du fichier /etc/bind/named.conf.options

On indique dans ce fichier sur quel port effectuer les requêtes d'identification (sur le port 53)

On y indique également les adresses des serveurs DNS externes (ceux de Free par exemple) dans la section forwarders

Attention à bien respecter l'indentation du texte avec une tabulation

options {
    directory "/var/cache/bind";

    // If there is a firewall between you and nameservers you want
    // to talk to, you might need to uncomment the query-source
    // directive below.  Previous versions of BIND always asked
    // questions using port 53, but BIND 8.1 and later use an unprivileged
    // port by default.

    query-source address * port 53;

    // If your ISP provided one or more IP addresses for stable
    // nameservers, you probably want to use them as forwarders. 
    // Uncomment the following block, and insert the addresses replacing
    // the all-0's placeholder.

    forwarders {
         212.27.40.240;
         212.27.40.241;
    };


    auth-nxdomain no;    # conform to RFC1035
    listen-on-v6 { any; };
};

Création du dossier /etc/bind/zones

$ sudo mkdir /etc/bind/zones

Création du fichier /etc/bind/zones/mondomaine.com.hosts

$ sudo nano /etc/bind/zones/mondomaine.com.hosts

Y indiquer les infos suivantes

Attention à bien respecter l'indentation du texte avec une tabulation

$ttl 86400
@    IN    SOA    zimbra.mondomaine.com. mail.mondomaine.com. (
            2008061802
            21600
            3600
            604800
            86400 )

;ENREGISTREMENT "A" DNS <-> IP CLASSIQUES
@    IN    NS    mail.mondomaine.com.
    IN    MX    10 mail.mondomaine.com.
    IN    A    192.168.1.10
mail    IN    A    192.168.1.10
zimbra    IN    A    192.168.1.10

;ENREGISTREMENT MESSAGERIE
mondomaine.com.    IN    MX    10 zimbra

Création du fichier /etc/bind/zones/rev.192.168.1.in-addr.arpa

$ sudo nano /etc/bind/zones/rev.192.168.1.in-addr.arpa

Y indiquer les infos suivantes

Attention à bien respecter l'indentation du texte avec une tabulation

@    IN    SOA    mondomaine.com. admin.mondomaine.com. (
            2006081405
            28800
            604800
            604800
            86400 )

    IN    NS    zimbra.mondomaine.com.
10    IN    PTR    zimbra.mondomaine.com.

Vérification de la configuration

Redémarrage de Bind afin d'appliquer les modifications

$ sudo /etc/init.d/bind9 restart (ou # sudo service bind9 restart)
$ named-checkconf

Vérifier les enregistrements DNS via DIG & NSLOOKUP directement sur le serveur via SSH

$ dig mx mondomaine.com
; <<>> DiG 9.4.2-P2.1 <<>> mx mondomaine.com
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57311
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 1, ADDITIONAL: 2
 
;; QUESTION SECTION:
;mondomaine.com.                    IN      MX
 
;; ANSWER SECTION:
mondomaine.com.             86400   IN      MX      10 zimbra.mondomaine.com.
mondomaine.com.             86400   IN      MX      10 mail.mondomaine.com.
 
;; AUTHORITY SECTION:
mondomaine.com.             86400   IN      NS      mail.mondomaine.com.
 
;; ADDITIONAL SECTION:
mail.mondomaine.com.        86400   IN      A       192.168.1.10
zimbra.mondomaine.com.      86400   IN      A       192.168.1.10
 
;; Query time: 16 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Feb 12 10:00:24 2011
;; MSG SIZE  rcvd: 118

$ dig a mail.mondomaine.com
; <<>> DiG 9.4.2-P2.1 <<>> a mail.mondomaine.com
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18243
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0
 
;; QUESTION SECTION:
;mail.mondomaine.com.               IN      A
 
;; ANSWER SECTION:
mail.mondomaine.com.        86400   IN      A       192.168.1.10
 
;; AUTHORITY SECTION:
mondomaine.com.             86400   IN      NS      mail.mondomaine.com.
 
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Feb 12 10:05:01 2011
;; MSG SIZE  rcvd: 63

$ nslookup mondomaine.com
Server:         127.0.0.1
Address:        127.0.0.1#53
 
Name:   mondomaine.com
Address: 192.168.1.10

L'installation et la configuration de BIND9 est enfin terminée

Pour simplifier la configuration d'un serveur Bind9, j'ai écrit un script de configuration automatique.

Pour le télécharger, cliquer ici

MD5SUM : 86a0a889ce9afe13fd12df48722f8182  configure_bind9.sh

Fichier attachéTaille
Binary Data hosts.278 octets
Binary Data resolv.conf46 octets
Binary Data named.conf1.25 Ko
Binary Data named.conf_.options710 octets
Binary Data mondomaine.com_.hosts351 octets
Binary Data rev.192.168.1.in-addr.arpa170 octets
Etiquettes: 

Installation de Zimbra

Paquets pré-requis à l'installation de Zimbra

$ sudo apt-get install libpcre3 libgmp3c2 libstdc++5 libltdl3 fetchmail curl sysstat sqlite3

Editer le fichier /etc/default/sysstat

$ sudo nano /etc/default/sysstat

Modifier ENABLED="true"

Téléchargement de Zimbra 7.1.3 GA

Zimbra 7.1.3 GA est la dernière version compatible avec une version Linux 32 bits

Voir sur le site de Zimbra la dernière version disponible

Récupérer la dernière version de Zimbra :

$ wget http://files.zimbra.com/downloads/7.1.3_GA/zcs-7.1.3_GA_3346.UBUNTU8.201...

Décompresser et restaurer l'archive téléchargée

$ tar xvzf ./zcs-7.1.3_GA_3346.UBUNTU8.20110928134533.tgz
$ cd zcs-7.1.3_GA_3346.UBUNTU8.20110928134533/
$ sudo ./install.sh

Détail de l'installation :

  • Do you agree with the terms of the software license agreement? [N] Y (répondre Y)
  • Install zimbra-ldap [Y] (laisser le choix par défaut)
  • Install zimbra-logger [Y] (laisser le choix par défaut)
  • Install zimbra-mta [Y] (laisser le choix par défaut)
  • Install zimbra-snmp [Y] (laisser le choix par défaut)
  • Install zimbra-store [Y] (laisser le choix par défaut)
  • Install zimbra-apache [Y] (laisser le choix par défaut)
  • Install zimbra-spell [Y] (laisser le choix par défaut)
  • Install zimbra-memcached [N] (laisser le choix par défaut)
  • Install zimbra-proxy [N] (laisser le choix par défaut)
  • The system will be modified.  Continue? [N] Y (répondre Y)

Poursuite de l'installation ...

Pendant l'installation, les enregistrements DNS sont vérifiés par rapport au nom de domaine zimbra.mondomaine.com. Une erreur va donc être retournée.

DNS ERROR resolving MX for zimbra.mondomaine.com
It is suggested that the domain name have an MX record configured in DNS
Change domain name? [Yes] Yes ici, répondre YES
Create Domain: [zimbra.mondomaine.com] mondomaine.com  ici, entrer le nom du domaine 
MX: zimbra.mondomaine.com (192.168.1.10)
MX: mail.mondomaine.com (192.168.1.10)

Paramétrage du mot de passe admin

  • Address unconfigured (**) items  (? - help) Taper 3
  • Select, or 'r' for previous menu [r] Taper 4
  • Password for admin@mondomaine.com (min 6 characters): [MOvHWVTgi] Saisir un mot de passe
  • Select, or 'r' for previous menu [r] Taper r
  • *** CONFIGURATION COMPLETE - press 'a' to apply
    Select from menu, or press 'a' to apply config (? - help) Taper a
  • Save configuration data to a file? [Yes] (laisser le choix par défaut)
  • Save config in file: [/opt/zimbra/config.23103] (laisser le choix par défaut)
  • The system will be modified - continue? [No] (répondre Y)

Poursuite du paramétrage automatique ...

  • Notify Zimbra of your installation? [Yes] (taper Y ou N)

Configuration des différents services ...

Patienter un petit moment le temps que tous les services démarrent

  • Configuration complete - press return to exit

L'installation est enfin terminée

Il ne reste plus qu'à se connecter à l'interface de configuration du serveur Zimbra via un navigateur à l'adresse https://adress_ip_du_serveur:7071

Etiquettes: 

Paramétrage de Zimbra

Le paramétrage de Zimbra se fait via son interface de configuration à l'adresse https://adresse_ip_du_serveur:7071

user = admin
password = celui choisi lors de l'installation

L'interface de connexion au serveur :

interface connexion serveur

Le menu de Zimbra :

menu zimbra

Les différents paramétrages à modifier :

Paramètres globaux

- Agent MTA

  • Renseigner un relais MTA pour les livraisons externes
    Pour un relais MTA avec authentification, voir ici
  • Renseigner le nom de l'hôte SMTP entrant

Valider en cliquant sur Enregistrer

Domaines

- Information générale

  • Renseigner le nom d'hôte du service public
  • Renseigner le fuseau horaire

- Zimlets

  • Tout sélectionner

Valider en cliquant sur Enregistrer

Classe de service

- Défault

- Caractéristiques

  • Cocher Messenger

- Préférences

- Options générales

  • Cocher Connexion automatique aux services IM
  • Sélectionner la langue

- Options mail

  • Cocher Afficher les images extérieures dans les mails HTML
  • Choisir Grouper les mails par mails
  • Choisir Toujours rédiger les mails avec HTML
  • Choisir Style de police par défaut de l'éditeur des mails HTML : Arial

- Options d'agenda

  • Sélectionner le fuseau horaire

Valider en cliquant sur Enregistrer

Les ports à ouvrir dans le routeur :

Ports forwarding Zimbra

Etiquettes: 

Commandes Zimbra

Toutes ces commandes ZIMBRA sont exécutées avec le user "zimbra" :

$ sudo su
$ su - zimbra

Stopper Zimbra

$ zmcontrol stop

Démarrer Zimbra

$ zmcontrol start

Etat Zimbra

$ zmcontrol status

Afficher la version de Zimbra

$ zmcontrol -v

Afficher la configuration locale de Zimbra

$ zmlocalconfig -s

Afficher le mot de passe MySQL du compte root

$ zmlocalconfig -s mysql_root_password

Afficher le mot de passe MySQL du compte zimbra

$ zmlocalconfig -s zimbra_mysql_password

Se connecter à MySQL avec le compte root

$ mysql -S /opt/zimbra/db/mysql.sock -u root --password=mysql_root_password

Afficher les répertoires d'un utilisateur

$ zmmailbox -z -m user@monDomaine.com gaf

-z : utiliser les droits de l'administrateur
-m : spécifie pour quel utilisateur on liste les répertoires
gaf : Get All Folders

Vider le répertoire d'un utilisateur

$ zmmailbox -z -m user@monDomaine.com ef /nomDuRepertoire

ef : Empty Folder

Afficher la liste des users

$ zmprov -l gaa

Afficher la liste des users admin

$ zmprov -l gaaa

Modifier le mot de passe d'un compte

sp (setPassword)

$ zmprov sp moncompte@monDomaine.com myNewPassword

Déverrouiller un compte

ma (modifyAccount)

$ zmprov ma moncompte@monDomaine.com zimbraAccountStatus active

Créer un compte mail

ca (createAccount)

$ zmprov ca moncompte@monDomaine.com monPassword displayName moncompte

Exporter les différents dossiers d'un compte dans une archive zip

Les différents dossiers exportables (autres que mail):

  • _gal
  • Briefcase
  • Calendar
  • Chats
  • Contacts
  • Notebook
  • Public
  • Tasks
  • Emailed Contacts

Les dossiers standards des mails :

  • Inbox
  • Sent
  • Drafts
  • Junk
  • Trash

Commande à exécuter :

Pour info, tout dépend de la configuration du serveur Zimbra, mais il est possbile que les WebServices Rest soient disponibles en https sur le port 8443.
Dans ce cas, il faut le prendre en compte dans les commandes ci-dessous.

$ zmmailbox -z -m user@domaine.com getRestUrl -u http://`zmhostname`:8080 "/nom_du_dossier?fmt=zip" > /mon_dossier_de_sauvegarde/mon_fichier_export.zip

Exporter la totalité d'un compte dans une archive zip

$ zmmailbox -z -m compte@domaine.fr getRestURL -u http://`zmhostname`:8080 "//?fmt=zip" > ./compte.zip

Importer les différents dossiers d'un compte d'une archive zip

Les différentes valeurs du paramètre resolve :

  • skip : ignore les doublons
  • modify : modifie les anciens éléments
  • reset : supprime tout (dossiers, éléments) avant import
  • replace : supprime les éléments avant import

 Commande à exécuter :

$ zmmailbox -z -m user@domaine.com postRestUrl -u http://`zmhostname`:8080 "/nom_du_dossier?fmt=zip&resolve=reset" /mon_dossier_de_sauvegarde/mon_fichier_export.zip

Importer la totalité d'un compte à partir d'une archive zip

$ zmmailbox -z -m compte@domaine.fr postRestURL -u http://`zmhostname`:8080 "//?fmt=zip&resolve=reset" ./compte.zip

Réactiver la fonction "Notebook" du "Porte-documents" dans Zimbra 7

$ zmprov mc default zimbraFeatureBriefcaseSpreadsheetEnabled TRUE
$ zmprov mc default zimbraFeatureBriefcaseSlidesEnabled TRUE
$ zmcontrol restart

Réactiver la fonction de messagerie instantanée dans Zimbra 7

$ zmprov -l -v mcf zimbraXMPPEnabled TRUE
$ zmprov -v mc default zimbraFeatureIMEnabled TRUE
$ zmprov -v mc default zimbraFeatureInstantNotify TRUE
$ zmcontrol restart

Sauvegarde Zimbra

Script de sauvegarde

$ cat sauvegarde.sh
#!/bin/bash
#Sauvegarde automatique
 
#Arret des services ZIMBRA
su - zimbra -c "/opt/zimbra/bin/zmcontrol stop" >> /backup/log/01stopZimbra.`date +%Y_%m_%d_%s`.log 2>&1
 
#Pause de 30 secondes
sleep 30
 
#Sauvegarde incrementielle du repertoire /opt/zimbra dans une archive TAR
/bin/tar --create --file=/backup/archive.`date +%Y_%m_%d_%s`.tar --listed-incremental=/backup/backup.list /opt/zimbra >> /backup/log/02tar.`date +%Y_%m_%d_%s`.log 2>&1
 
#Redemarrage des services ZIMBRA
su - zimbra -c "/opt/zimbra/bin/zmcontrol start" >> /backup/log/03startZimbra.`date +%Y_%m_%d_%s`.log 2>&1
 
#Statuts des services Zimbra
su - zimbra -c "/opt/zimbra/bin/zmcontrol status" > /backup/log/04statusZimbra.`date +%Y_%m_%d_%s`.log 2>&1

1 fois par semaine (ou plus en fonction des besoins) réinitialiser la sauvegarde incrémentielle en supprimant le fichier /backup/backup.list.

Restauration :

Stopper les services ZIMBRA

$ sudo su -
$ su - zimbra -c "zmcontrol stop"

Supprimer le répertoire /opt/zimbra puis restaurer la première archive complète

$ rm -r /opt/zimbra/
$ tar --extract --listed-incremental=/dev/null --file archive.1.tar

Puis restaurer les archives suivantes si besoin

$ tar --extract --listed-incremental=/dev/null --file archive.2.tar

Redémarrer les services ZIMBRA

$ su - zimbra -c "zmcontrol start"

Sur un historique de sauvegarde de 10 archives, pour restaurer l'archive 4, restaurer les archives 1, 2, 3 & 4 et ce dans le bon ordre. Ne pas restaurer directement l'archive 4, elle serait incomplète.

 
Restaurer une sauvegarde suite à un plantage du système
 
  1. Réinstaller Ubuntu Server
  2. Réinstaller Zimbra via l'option ./install.sh --softwareonly
  3. Exécuter la commande /opt/zimbra/libexec/zmsetup.pl
  4. Restaurer les données via la procédure de restauration
Etiquettes: 

Webmail : Ajouter un compte externe avec un certificat autosigné

Pour accepter les certificats non reconnus dans la configuration de Zimbra : 

$ sudo nano /opt/zimbra/conf/localconfig.xml

Dans la section : 

  <key name="ssl_allow_untrusted_certs">
    <value>false</value>
  </key>
 
Remplacer "false" par "true"
 
  <key name="ssl_allow_untrusted_certs">
    <value>true</value>
  </key>
 
Sauvegarder puis redémarrer les services Zimbra :

$ sudo su
$ su - zimbra
$ zmcontrol restart
$ exit

Etiquettes: 

Push e-mail avec Z-PUSH : Installation et configuration pour Zimbra

z-push

Pouvoir profiter du push e-mail avec Zimbra est possible grâce à une application PHP simulant un serveur Microsoft Server ActiveSync.

Pour cela, rien de plus simple.

Le seul inconvénient est de disposer d'un serveur, autre que celui sur lequel est installé Zimbra, car il faut obligatoirement un serveur sur lequel sera installé Apache et PHP.

Un second serveur Ubuntu sera donc utile pour pouvoir profiter du push e-mail.

$ wget http://download.berlios.de/z-push/z-push-1.5.5-790.tar.gz

  • Copier l'archive dans le répertoire public_html du home correspondant au serveur virtuel

$ cp ./z-push-1.5.5-790.tar.gz /home/push/public_html/

  • Se rendre dans le dossier public_html et extraire le contenu de l'archive

$ cd /home/push/public_html/ && tar -xvzf ./z-push-1.5.5-790.tar.gz

  • Se rendre dans le dossier ./z-push/backend nouvellement créé

$ cd ./z-push/backend/

$ wget http://freefr.dl.sourceforge.net/project/zimbrabackend/Release51/zimbra5...
$ tar -xvzf zimbra51.tgz

  • Retourner dans le dossier z-push et éditer le fichier config.php

$ cd .. && nano ./config.php

  • Dans le fichier config.php, modifier les lignes suivantes :

    === Disable Provisioning ===
    define('PROVISIONING', false);

    === Replace ===
    $BACKEND_PROVIDER = "BackendZimbra";

    === Add ===
    define('ZIMBRA_URL', 'http://<zimbra url>');
    define('ZIMBRA_USER_DIR', 'zimbra');
    define('ZIMBRA_SYNC_CONTACT_PICTURES', true);
    define('ZIMBRA_VIRTUAL_CONTACTS',true);
    define('ZIMBRA_VIRTUAL_APPOINTMENTS',true);
    define('ZIMBRA_VIRTUAL_TASKS',true);
    define('ZIMBRA_IGNORE_EMAILED_CONTACTS',true);
    define('ZIMBRA_HTML',false);
    define('ZIMBRA_ENFORCE_VALID_EMAIL',true);

    === Optional ===
    define('ZIMBRA_NOKIA_MFE_FIX',true);
    define('ZIMBRA_NOKIA_MFE_ALWAYS_OVERWRITE',true);

    Commenter toutes les définitions IMAP (SERVER, PORT etc etc)
  • Le fichier doit ressembler à ceci config.php
  • Modifier le propriétaire du dossier state

$ chown www-data:www-data ./state/

  • Via Virtualmin, pour le domaine push.mondomaine.com par exemple, créer un Alias /Microsoft-Server-ActiveSync pointant vers /home/push/public_html/z-push/index.php

    Dans Virtualmin, on sélectionne le domaine dans la liste déroulante, puis Services --> Configure Website
    Cliquer sur le bouton Alias et redirections puis saisir les infos dans la section Alias des répertoires des documents
    Cliquer sur le bouton Sauvegarder puis sur Appliquer les changements en haut à droite
     

La configuration du serveur PUSH est enfin terminée.

Il ne reste plus qu'à configurer Microsoft Exchange Active Sync sur le téléphone en y indiquant l'adresse http du serveur (push.mondomaine.com par exemple - Ne pas indiquer l'alias /Microsoft-Server-ActiveSync dans l'adresse du serveur) et les services à synchroniser (agenda, carnet d'adresses, tâches etc etc)

Il faut, bien entendu, que le serveur push.mondomaine.com (par exemple) soit accessible de l'extérieur.


Comment faire pour que le webmail du serveur Zimbra (installé sur la machine 10.10.0.10 par exemple) et le serveur push.mondomaine.com (installé sur la machine 10.10.0.20 par exemple) soient tous les 2 accessibles de l'extérieur sur le port HTTP 80 et/ou HTTPS 443.

Surtout que dans un routeur, il est possible de rediriger un port que sur une seule adresse IP.

Donc, soit on redirige le port 80 & 443 vers la machine 10.10.0.10 et dans ce cas le serveur web installé sur la machine 10.10.0.20 n'est pas accessible soit on fait l'inverse et dans ce cas c'est le webmail Zimbra installé sur la machine 10.10.0.10 qui n'est plus accessible.

Pour résoudre ce problème, il suffit de créer un second serveur virtuel sur la machine où se trouve le serveur push.mondomaine.com et de le nommer zimbra.mondomaine.com (par exemple) et de le configurer comme un serveur proxy qui transmettra les requêtes HTTP vers le serveur Zimbra.

Il suffit donc de paramétrer le routeur afin de rediriger le port 80 et 443 vers la machine 10.10.0.20. Le serveur virtuel zimbra.mondomaine.com se chargera de transmettre les requêtes vers la machine 10.10.0.10 et affichera, par conséquent, le webmail de Zimbra.

Cliquer pour la configuration d'un serveur virtuel en mode proxy.


Importer un certificat SSL auto-signé dans les appareils mobiles utilisant le serveur Z-PUSH en HTTPS

  1. Avec un navigateur (Firefox par exemple), se rendre à l'adresse HTTPS du serveur Z-PUSH (https://push.mondomaine.com)
  2. Accepter l'exception de sécurité pour le certificat SSL auto-signé
  3. Dans Firefox, cliquer sur l'icone de sécurité située à gauche de l'adresse puis sur "Plus d'informations".
  4. Cliquer sur "Afficher le certificat" puis dans l'onglet "Détails", cliquer sur exporter.
  5. Enregistrer le certificat avec le type "Certificat X.509 (DER)"
  6. Transférer le certificat sur l'appareil mobile (Bluetooth, USB etc etc ...)
  7. Dans l'appareil mobile, se rendre dans le dossier où a été enregistré le certificat puis l'ouvrir afin de l'enregistrer dans la base des certificats d'autorité.
Etiquettes: 

Z-PUSH : Installer un système de gestion des téléphones

Pouvoir gérer les téléphones qui se connectent au serveur Z-PUSH, c'est ce que propose de faire cette application PHP "zpush-zimbra-provisioning"

Avec "zpush-zimbra-provisioning", il est possible de lister tous les appareils mobiles qui se connectent au serveur, de les supprimer et enfin, il est également possible de les réinitialiser (retour à la config usine).

La version en cours est "zpush-zimbra-provisioning-1.02.tgz"
Pour vérifier la dernière version, http://sourceforge.net/projects/zimbrabackend/files/Provisioning%20%28Remote%20Wipe%29%20Support%20-%20Needs%20Release%2045%20or%20later/

Pour l'installer, rien de plus simple :

$ sudo su
$ cd /var/www
$ wget http://freefr.dl.sourceforge.net/project/zimbrabackend/Provisioning%20%2...
$ tar -xvzf zpush-zimbra-provisioning-1.02.tgz

L'installation est terminée, il faut maintenant créer la base de données :

$ sudo su
$ cd /var/www/zimbra-provisioning
$ mysql -u root -p < zpush_zimbra.sql

Dans MySql, créer un user avec password ayant tous les droits sur la base de données "zpush_zimbra"

Modification du fichier "zimbradb.php" :

Il faut modifier le fichier /var/www/zimbra-provisioning/zimbradb.php et renseigner les paramètres de connexion à la base de données MySql. Y inscrire le user et le password créés précédement.

    $mysql_server = "localhost:3306";
    $mysql_username = "provuser";
    $mysql_password = "provpassword";
    $mysql_zimbradb =    "zpush_zimbra";

Une fois le fichier modifier, il faut le copier dans le dossier Backend de Z-PUSH.

Création du user admin :

Dans votre navigateur préféré, se rendre à l'adresse
http://adresse_ip_du_serveur/zimbra-provisioning/registerAdminGUIuser.php

Cette page va permettre de créer le compte admin.
Une fois le compte créé, il est recommandé de renommer le script registerAdminGUIuser.php ou de le supprimer.

$ sudo su
$ cd /var/www/zimbra-provisioning
$ mv registerAdminGUIuser.php registerAdminGUIuser.php.old
$ chown root:root registerAdminGUIuser.php.old
$ chmod 600 registerAdminGUIuser.php.old

Modifier la configuration de Z-PUSH :

Pour profiter de cette option, il faut activer le provisioning dans la configuration de Z-PUSH.
Pour cela, il suffit de modifier le fichier config.php (dans le dossier de Z-PUSH)

=== Enable Provisioning ===
define('PROVISIONING', true);

Et voilà, tout est terminé, vous pouvez vous rendre à l'adresse http://adresse_ip_du_serveur/zimbra-provisioning, vous authentifier et gérer tous les appareils mobiles.

La page d'accueil permet de lister tous les appareils.
En cliquant sur "Détail" d'un appareil, il est possible de supprimer l'appareil en cliquant sur "DELETE DEVICE" ou de le réinitialiser en cliquant sur "WIPE DEVICE"
Attention, en cliquant sur "WIPE DEVICE" le téléphone sera entièrement réinitialiser - Retour à la configuration usine - Très pratique pour se débarasser d'un téléphone sans laisser aucune trace des données.

Erreurs rencontrées

Erreur d'accès à la file d'attente dans l'interface d'administration de Zimbra

Code de l'erreur :

Mail : system failure: exception during auth {RemoteManager: zimbra.domain.fr->zimbra@zimbra.domain.fr:22} Code d'erreur : service.FAILURE Method: [unknown] Détails :soap:Receiver

Cette erreur indique un problème d'accès au serveur via SSH sur le port 22.

Pour résoudre cette erreur, j'ai simplement ajouter dans le fichier /etc/hosts.allow, le nom de mon serveur Zimbra.

$ hostname -f
zimbra.domain.fr
$ nano /etc/hosts.allow
# Ajout de la ligne :
ALL: zimbra.domain.fr
^O (enregistrer)
^X (quitter)
$

Charge CPU à 100%

Depuis le 01 juillet 2012, pour je ne sais quelle raison, java utilisait toutes les ressources CPU.
Ne trouvant pas la cause du problème, j'étais sur le point de réinstaller mon serveur Zimbra quand je suis tombé sur ce post :

http://www.zimbrafr.org/forum/topic/5003-java-100-cpu-et-seconde-interca...

La cause : L'ajustement de la seconde intercalaire.

Qu'est ce que c'est que ce truc là.

Explication sur Wikipedia :
Une seconde intercalaire, également appelée saut de seconde ou seconde additionnelle, est un ajustement d'une seconde du Temps universel coordonné (UTC). Et ce, afin qu'il reste assez proche du Temps universel (UT) défini quant à lui par l'orientation de la Terre par rapport aux étoiles.

http://fr.wikipedia.org/wiki/Seconde_intercalaire

Le remède : Tout simple. Il suffit de reparamétrer l'horloge du serveur avec la commande suivante :

$ date -s "`date`"

Tout est rentré dans l'ordre comme par magie.

Ce "bug" touche les 2 applications Java et MySql


Problème d'accès aux logs

Code de l'erreur :

Mail : system failure: Unable to read logger stats Code d'erreur : service.FAILURE Method: [unknown] Détails :soap:Receiver

Pour résoudre cette erreur, exécuter les commandes suivantes en tant que root.

$ /opt/zimbra/libexec/zmsyslogsetup
$ /etc/init.d/rsyslog restart
$ /opt/zimbra/libexec/zmfixperms

Pour terminer, en tant que user zimbra

$ zmloggerctl start
$ zmcontrol status

Tout devrait rentrer dans l'ordre.

Mise à jour de l'antivirus

Procédure de mise à jour:

# su - zimbra
$ mkdir /tmp/clamdb
$ mv /opt/zimbra/data/clamav/db/* /tmp/clamdb
$ /opt/zimbra/common/bin/freshclam --config-file=/opt/zimbra/conf/freshclam.conf
$ zmamavisdctl restart
$ zmcontrol status

Mise à jour de l'anti spam

Procédure de mise à jour:

# su - zimbra
$ /opt/zimbra/common/bin/sa-update -D
$ zmamavisdctl restart
$ mkdir /tmp/state
$ mv /opt/zimbra/data/spamassassin/state /tmp/state
$ zmamavisdctl restart
$ zmcontrol status

Configurer un relais MTA avec authentification

Pour ne pas avoir de problème lors de l'envoi des mails, il est conseillé d'utiliser un relais SMTP pour l'envoi des mails à l'extérieur du domaine.

En effet, il y a de grandes chances pour que tous les mails envoyés avec notre propre serveur soient considérés comme du SPAM.

C'est pour cette raison qu'il vaut mieux utiliser un relais SMTP de confiance, mais qui dit de confiance, dit forcément authentification surtout si notre serveur ne se trouve pas sur le même réseau que notre relais SMTP.

Dans l'exemple, je vais configurer sur mon serveur ZIMBRA, qui se trouve hébergé chez ONLINE, le relais SMTP de Free (fournisseur de ma box à la maison - j'ai donc le droit par conséquent d'utiliser leur serveur SMTP)

Toutes les commandes ci-dessous doivent être exécutées avec le user zimbra.

## Je me connecter en tant que user zimbra
root@vm-zimbra:~# su - zimbra
## Je renseigne mon relais SMTP
zimbra@vm-zimbra:~$ zmprov ms vm-zimbra.domain.fr zimbraMtaRelayHost smtp.free.fr:587
## Création du fichier relay_password contenant le user et le mot de passe pour le relais smtp
zimbra@vm-zimbra:~$ echo smtp.free.fr user:pass > /opt/zimbra/conf/relay_password
## Conversion du fichier relay_password en table postfix
zimbra@vm-zimbra:~$ postmap /opt/zimbra/conf/relay_password
## Ceci permet de tester que le user et le mot de passe est bien associé au relais smtp
zimbra@vm-zimbra:~$ postmap -q smtp.free.fr /opt/zimbra/conf/relay_password
## Configuration de postfix pour l'utilisation de la table d'identification du relais smtp
zimbra@vm-zimbra:~$ zmprov ms vm-zimbra.domain.fr zimbraMtaSmtpSaslPasswordMaps lmdb:/opt/zimbra/conf/relay_password
## Activation de l'authentification SSL
zimbra@vm-zimbra:~$ zmprov ms vm-zimbra.domain.fr zimbraMtaSmtpSaslAuthEnable yes
## Désactivation de l'utilisation du "canonical name" du relais smtp
zimbra@vm-zimbra:~$ zmprov ms vm-zimbra.domain.fr zimbraMtaSmtpCnameOverridesServername no
## Activation de la séc urité TLS
zimbra@vm-zimbra:~$ zmprov ms vm-zimbra.domain.fr zimbraMtaSmtpTlsSecurityLevel may

Normalement, tout est désormais opérationnel.
Tous les mails envoyés à l'extérieur du domaine seront transférés désormais via le relais SMTP.

Générer un nouveau certificat pour Zimbra

Erreurs rencontrées:

1. TLS: can't accept: error:14094415:SSL routines:ssl3_read_bytes:sslv3 alert certificate expired.
2. Unable to start TLS: SSL connect attempt failed error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed when connecting to ldap master.

Pas de panique, il suffit juste de suivre la procédure ci-dessous:

Pour générer un nouveau certificat dans Zimbra quand le précédent a expiré, suivre la procédure ci-dessous :

Via SSH, se connecter au serveur Zimbra puis saisir les commandes suivantes (en root)

$ sudo su
# Commencez par générer une nouvelle autorité de certification (CA).
$ /opt/zimbra/bin/zmcertmgr createca -new
...
# Puis générer un certificat signé par le CA qui expire dans 365 jours.
$ /opt/zimbra/bin/zmcertmgr createcrt -new -days 365
...
# Ensuite déployer le certificat.
$ /opt/zimbra/bin/zmcertmgr deploycrt self
...
# Ensuite déployer le CA.
$ /opt/zimbra/bin/zmcertmgr deployca
...
# Pour finir, vérifier que le certificat a été déployé à tous les services.
$ /opt/zimbra/bin/zmcertmgr viewdeployedcrt
::service mta::
...
::service proxy::
...
::service mailboxd::
...
::service ldap::
...
$

Etiquettes: 

Divers

Quelques petites idées trouvées par-ci, par-là ...

Alternatives open-source de vos applications favorites

Le site https://opensource.builders/ regroupe une liste, non exhaustive, d'applications open-source pouvant remplacer une bonne partie des applications propriétaires que nous utilisons tous les jours.

Un moteur de recherche est disponible pour nous aider à trouver notre bonheur.

CSV: gérer correctement les guillemets et les nombres longs

Dans le format CSV, les guillemets et les nombres longs peuvent générer des problèmes d'affichage dans Excel.

Exemple avec le CSV suivant:

Dans Excel, l'affichage ressemble à ceci:

Résultat, la ligne 2 a partiellement disparu, et le nombre sur 13 caractères est affiché au format scientifique.

En réalité, la fin de la ligne 1 et le début de la ligne 2 sont concaténés dans la cellule B.

Pour éviter ces petits désagréments, il est nécessaire:

Pour les chaines contenant des guillemets, de les échapper en les doublant et d'encapsuler toute la chaine avec des guillemets.
Exemple:
"une chaine avec des doubles guillemets ""
devient
"""une chaine avec des doubles guillemets """""

(en bleu, le doublement des guillemets pour l'échappement, en rouge l'encapsulation de toute la chaine)

Pour les nombres longs, il est nécessaire d'encapsuler toute la valeur avec des guillemets et de faire précéder le tout avec le signe "="
Exemple:
1256784962457
devient
="1256784962457"

Après correction, le fichier CSV ressemble à ceci:

Dans Excel:

Le résultat est parfaitement identique aux valeurs d'origine.

Etiquettes: 

Chiffrer un message avec des emoji (émoticônes)

Le site https://codemoji.org/#/encrypt de la fondation Mozilla, permet de chiffrer des messages à l'aide d'emojis.

Vous écrivez votre message, vous choisissez l'emoji qui vous plait pour le chiffrement et le message chiffré est disponible en ligne et une url courte est fournie afin de partager le message chiffré à qui vous voulez.

Des liens de partages automatiques vers les différents réseaux sociaux sont également disponibles.

Petite démonstration en image.

gif animé

Super sympa.

Seule contrainte, ne pas oublier l'emoji qui a servi au cryptage afin de pouvoir déchiffrer le message.

DBeaver: SqlServer: The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]

Depuis la dernière mise à jour de DBeaver en version 21.1.0, je ne peux plus me connecter à ma base de données SqlServer.

La faute à qui, au protocole de sécurité TLS1.0 qui est désormais désactivé dans la nouvelle version 11 de Java.

DBeaver utilisant sa propre version de Java, la dernière mise à jour embarque la version 11 de Java.

Afin de corriger cette "anomalie", il est nécessaire de modifier le fichier java.security dans le dossier jre de DBeaver.

Pour mon installation, il s'agit de C:\Program Files\DBeaver\jre\conf\security

La ligne contenant:

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves

doit être modifiée comme ceci:

jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves

Il suffit juste de supprimer les références aux protocoles TLSv1 et TLSv1.1

La connexion à la base de données SqlServer est à nouveau possible.

Effacer la console

Cette astuce fonctionne dans les consoles bash et python.

En bash

$ echo -e "\033[H\033[J"

En python

>>> print("\033[H\033[J")

Cette commande peut-être utilisée dans des scripts afin d'afficher du contenu à l'écran et le rafraichir à intervalles régulières sans surcharger l'écran.

Etiquettes: 

Faire des tests de requêtes HTTP

Le site https://httpbin.org/ propose tout un tas de méthodes HTTP pouvant servir à tester différents outils de requêtage HTTP.

Vraiment très pratique pour tester des outils comme curl, requests (python) etc ....

Fibonacci

Afficher la suite de Fibonacci en bash, php, python3

En bash:

#!/bin/bash

#set -x

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

function is_int {
        if grep -E -v -q "^-?[0-9]+$" <<< $1; then
                return 1
        fi
        return 0
}

if [ -z $1 ] || ! is_int $1 || (($1 <= 0)); then
        printf "\
Usage: %s nbr_int
    nbr_int: Un nombre entier supérieur à 0\n" $0
        exit 1
fi

a=0
b=1

while (($b < $1)); do
        printf "%d\n" $b
        ((b=a+b))
        ((a=b-a))
done

printf "b/a = %1.15f\n" $(echo $b/$a | bc -l)

exit 0

En php:

#!/usr/bin/env php

<?php

if(! isset($argv[1]) || ! is_numeric($argv[1]) || intval($argv[1]) <= 0){
        printf("Usage: %s nbr_int
    nbr_int: Un nombre entier supérieur à 0\n", $argv[0]);
        exit(1);
}

$nbr_int = intval($argv[1]);

$a = 0;
$b = 1;

while ($b < $nbr_int) {
        printf("%d\n", $b);
        $b = $a + $b;
        $a = $b - $a;
}

printf("b/a = %1.15f\n", $b/$a);

exit(0);

En python3:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, argparse

parser = argparse.ArgumentParser(description=u"Calcul et affiche les n premiers nombres de la suite de Fibonacci.")
parser.add_argument("number", help=u"Nombre maximum de calcul de la séquence. Ce nombre doit égal ou supérieur à 2.", type=int)
parser.add_argument("-g", "--display-golden-number", dest="golden", help=u"Afficher le nombre d'or.", action="store_true")
parser.add_argument("--log", default=sys.stdout, type=argparse.FileType('w'), help=u"Fichier où sera enregistré le résultat.")
args = parser.parse_args()

if args.number < 2:
    print(u"Indiquer un nombre supérieur ou égal à 2.")
    sys.exit(1)

a,b = 0,1

while b < args.number:
    print("{:d}".format(b), file=args.log)
    a,b = b,a+b

if args.golden:
    print(u"\nb/a = {:1.15f} (Nombre d'or)".format(b/a), file=args.log)

args.log.close()
sys.exit(0)

infoTous ces scripts prennent en paramètre un nombre entier positif.
En prime, le quotient des deux derniers termes est affiché et représente le nombre d'or si le nombre passé en argument est supérieur ou égal à 1 000 000 (pour la précision du nombre d'or).
Le script Python est un peu plus développé avec une gestion des arguments plus pointue.

Framabin

Framabin

Transmettre des messages chiffrés.

Framabin est un service en ligne de Framasoft, libre et gratuit, qui permet de transmettre des messages, de façon sécurisée (les messages sont chiffrés en utilisant l’algorithme AES 256 bits).

Il est possible de définir une date d'expiration, et une option est diponible afin de supprimer le message dès la première lecture.

Une fois le message créé, 2 urls sont fournies pour le partage et la suppression du message.

Etiquettes: 

Framapic

Framapic

Partagez des images de façon anonyme.

Framapic est un service en ligne de Framasoft, libre et gratuit, qui permet de partager des images, de façon sécurisée (les images sont cryptées sur le serveur).

Il est possible d'y envoyer une image à partir de son poste ou d'une url.

Il est possible de définir une date d'expiration, et une option est diponible afin de supprimer l'image dès le premier accès.

Enfin, un bouton permet de partager le lien de l'image directement sur Twitter et 4 urls sont fournies pour l'affichage, le téléchargement, le partage et la suppression de l'image.

Etiquettes: 

Git: utiliser "git add" avec une liste de fichiers spécifiques

Quand on souhaite exclure des fichiers lors de l'utilisation de "git add", il est possible d'utiliser un fichier ".gitignore" dans lequel on indique tous les fichiers/dossiers que l'on souhaite exclure.

Très pratique quand la liste des fichiers/dossiers à exclure n'est pas très importante.

Mais comment faire quand c'est l'inverse, un dossier contenant de nombreux fichiers, et seulement quelques fichiers à ajouter à un dépôt git.

L'idéal serait de pouvoir utiliser un fichier ".gitaccept"

Voici une petite astuce qui permet de le faire.

$ xargs -a .gitaccept -d '\n' git add

Le principe consiste à utiliser la commande xargs.
La paramètre -a indique le fichier à lire sur l'entrée standard, ce fichier contient la liste des fichiers à ajouter au dépôt git, et le paramètre -d indique le caractère à utiliser comme délimiteur.

Pour chaque fichier lu dans le fichier ".gitaccept", la commande xargs exécute la commande "git add".

Etiquettes: 

LessPass

LessPass est un outil, en ligne, qui permet de gérer ses différents mots de passe en les calculant, à la demande, à l'aide d'informations fournies, à l'inverse des différents gestionnaires de mots de passe qui eux les enregistrent dans une base.

Le gros avantage de cette solution, tous vos mots de passe sont disponibles n'importe quand, n'importe où, sur n'importe quelle machine et n'importe quel navigateur.

Deux informations importantes ne doivent pas être oubliées.
L'adresse du site web LessPass ainsi que votre mot de passe maître.
Ah oui, il faut quand même se souvenir d'un mot de passe.

Une extension pour Chrome et Firefox est également disponible. Cela évite d'aller sur le site web.

Comment cela fonctionne-t-il ?

Pour fonctionner, l'algorithme a besoin de trois informations:

  1. L'adresse du site sur lequel vous voulez vous connecter
  2. Votre nom d'utilisateur
  3. Le mot de passe maître

A l'aide de ces informations, l'algorithme calcul un mot de passe, de la longueur souhaitée et contenant ou pas des caractères spécifiques.
C'est ce mot de passe qui doit être utilisé pour vous connecter sur le site indiqué.

A utiliser, bien sûr, lors de la création d'un compte, ou alors si le compte est déjà existant, le mot de passe correspondant doit être changé par celui-ci.

LessPass est Open Source. Ce qui signifie que le code est accessible.
Les sources sont disponibles sur GitHub https://github.com/lesspass/lesspass/

LessPass peut également être auto-hébergé. (container docker)

L'algorithme est consultable ici https://github.com/lesspass/core/blob/master/index.js

Grâce à cet outil, fini les mots de passe identiques pour tous les sites sur lesquels vous avez un compte.

Etiquettes: 

Letsencrypt: certbot & dns ovh

Procédure de création/mise à jour des certificats letsencrypt avec certbot et certbot-dns-ovh

Toutes les infos ont été trouvées sur ce site: https://buzut.net/certbot-challenge-dns-ovh-wildcard/

Installations des outils

# apt-get install python3-pip
# python3 -m pip install --upgrade certbot certbot-dns-ovh

Configuration des journaux

Création du fichier "/etc/logrotate.d/certbot" avec le contenu suivant

/var/log/letsencrypt/*.log {
    monthly
    rotate 6
    compress
    delaycompress
    notifempty
    missingok
    create 640 root adm
}

Création des clés API OVH

Pour que le script puisse vérifier que le domaine vous appartient, il doit avoir l'autorisation de modifier des enregistrements TXT dans les DNS.

Il faut donc créer les clés API avec une date de validité illimitée et pour plus de sécurité, avec une restriction au niveau de l'adresse IP du serveur où sera exécuté le script.

Les droits doivent être renseignés de cette manière:

    GET /domain/zone/*
    PUT /domain/zone/*
    POST /domain/zone/*
    DELETE /domain/zone/*

Une fois les clés créées, il est nécessaire de créer un fichier INI pour y stocker les différentes clés générées.

Créer le fichier ~/.secrets/certbot/ovh.ini avec le contenu suivant:

dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = xxx
dns_ovh_application_secret = xxx
dns_ovh_consumer_key = xxx

Ne pas oublier de fixer les droits sur le fichier :

# chmod 600 ~/.secrets/certbot/ovh.ini

Génération des certificats

Par exemple, pour mon domaine

# certbot certonly --dns-ovh --dns-ovh-credentials ~/.secrets/certbot/ovh.ini -d quennec.fr -d *.quennec.fr

Automatiser le renouvellement

A renseigner dans crontab pour un contrôle tous les lundi à 9:00 et une mise à jour si nécessaire.

00 9 * * 1 /usr/local/bin/certbot certonly --dns-ovh --dns-ovh-credentials /root/.secrets/certbot/ovh.ini --non-interactive --agree-tos --email mon@email.fr -d quennec.fr -d *.quennec.fr && systemctl reload nginx

Configuration vhost nginx

Modifier le fichier vhost correspondant au domaine pour y intégrer les directives liées au certificat letsencrypt.

server {
    listen 443 ssl;
    server_name quennec.fr www.quennec.fr;
    ...
    ssl_certificate /etc/letsencrypt/live/quennec.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/quennec.fr/privkey.pem;
    ...
}

Ninja Cookie

Marre de cliquer à tout bout de champ sur les bandeaux cookies.

Cette extension, Ninja Cookie, pour Firefox et Chrome se charge à votre place de les refuser automatiquement.

Pour Firefox: https://addons.mozilla.org/fr/firefox/addon/ninja-cookie/

Pour Chrome: https://chrome.google.com/webstore/detail/ninja-cookie/jifeafcpcjjgnlcnk...

Site officiel du projet: https://ninja-cookie.com/fr/

Table ASCII (0, 127)

ascii
Table ASCII ( 0 - 127 )

       Décimal   Octal   Hex  Binaire   Caractère
       -------   -----   ---  --------    ------
         000      000    00   00000000      NUL    (Null char.)
         001      001    01   00000001      SOH    (Start of Header)
         002      002    02   00000010      STX    (Start of Text)
         003      003    03   00000011      ETX    (End of Text)
         004      004    04   00000100      EOT    (End of Transmission)
         005      005    05   00000101      ENQ    (Enquiry)
         006      006    06   00000110      ACK    (Acknowledgment)
         007      007    07   00000111      BEL    (Bell)
         008      010    08   00001000       BS    (Backspace)
         009      011    09   00001001       HT    (Horizontal Tab)
         010      012    0A   00001010       LF    (Line Feed)
         011      013    0B   00001011       VT    (Vertical Tab)
         012      014    0C   00001100       FF    (Form Feed)
         013      015    0D   00001101       CR    (Carriage Return)
         014      016    0E   00001110       SO    (Shift Out)
         015      017    0F   00001111       SI    (Shift In)
         016      020    10   00010000      DLE    (Data Link Escape)
         017      021    11   00010001      DC1    (XON)(Device Control 1)
         018      022    12   00010010      DC2    (Device Control 2)
         019      023    13   00010011      DC3    (XOFF)(Device Control 3)
         020      024    14   00010100      DC4    (Device Control 4)
         021      025    15   00010101      NAK    (Negative Acknowledgement)
         022      026    16   00010110      SYN    (Synchronous Idle)
         023      027    17   00010111      ETB    (End of Trans. Block)
         024      030    18   00011000      CAN    (Cancel)
         025      031    19   00011001       EM    (End of Medium)
         026      032    1A   00011010      SUB    (Substitute)
         027      033    1B   00011011      ESC    (Escape)
         028      034    1C   00011100       FS    (File Separator)
         029      035    1D   00011101       GS    (Group Separator)
         030      036    1E   00011110       RS    (Request to Send)(Record Separator)
         031      037    1F   00011111       US    (Unit Separator)
         032      040    20   00100000       SP    (Space)
         033      041    21   00100001        !    (exclamation mark)
         034      042    22   00100010        "    (double quote)
         035      043    23   00100011        #    (number sign)
         036      044    24   00100100        $    (dollar sign)
         037      045    25   00100101        %    (percent)
         038      046    26   00100110        &    (ampersand)
         039      047    27   00100111        '    (single quote)
         040      050    28   00101000        (    (left opening parenthesis)
         041      051    29   00101001        )    (right closing parenthesis)
         042      052    2A   00101010        *    (asterisk)
         043      053    2B   00101011        +    (plus)
         044      054    2C   00101100        ,    (comma)
         045      055    2D   00101101        -    (minus or dash)
         046      056    2E   00101110        .    (dot)
         047      057    2F   00101111        /    (forward slash)
         048      060    30   00110000        0
         049      061    31   00110001        1
         050      062    32   00110010        2
         051      063    33   00110011        3
         052      064    34   00110100        4
         053      065    35   00110101        5
         054      066    36   00110110        6
         055      067    37   00110111        7
         056      070    38   00111000        8
         057      071    39   00111001        9
         058      072    3A   00111010        :    (colon)
         059      073    3B   00111011        ;    (semi-colon)
         060      074    3C   00111100        <    (less than sign)
         061      075    3D   00111101        =    (equal sign)
         062      076    3E   00111110        >    (greater than sign)
         063      077    3F   00111111        ?    (question mark)
         064      100    40   01000000        @    (AT symbol)
         065      101    41   01000001        A
         066      102    42   01000010        B
         067      103    43   01000011        C
         068      104    44   01000100        D
         069      105    45   01000101        E
         070      106    46   01000110        F
         071      107    47   01000111        G
         072      110    48   01001000        H
         073      111    49   01001001        I
         074      112    4A   01001010        J
         075      113    4B   01001011        K
         076      114    4C   01001100        L
         077      115    4D   01001101        M
         078      116    4E   01001110        N
         079      117    4F   01001111        O
         080      120    50   01010000        P
         081      121    51   01010001        Q
         082      122    52   01010010        R
         083      123    53   01010011        S
         084      124    54   01010100        T
         085      125    55   01010101        U
         086      126    56   01010110        V
         087      127    57   01010111        W
         088      130    58   01011000        X
         089      131    59   01011001        Y
         090      132    5A   01011010        Z
         091      133    5B   01011011        [    (left opening bracket)
         092      134    5C   01011100        \    (back slash)
         093      135    5D   01011101        ]    (right closing bracket)
         094      136    5E   01011110        ^    (caret cirumflex)
         095      137    5F   01011111        _    (underscore)
         096      140    60   01100000        `
         097      141    61   01100001        a
         098      142    62   01100010        b
         099      143    63   01100011        c
         100      144    64   01100100        d
         101      145    65   01100101        e
         102      146    66   01100110        f
         103      147    67   01100111        g
         104      150    68   01101000        h
         105      151    69   01101001        i
         106      152    6A   01101010        j
         107      153    6B   01101011        k
         108      154    6C   01101100        l
         109      155    6D   01101101        m
         110      156    6E   01101110        n
         111      157    6F   01101111        o
         112      160    70   01110000        p
         113      161    71   01110001        q
         114      162    72   01110010        r
         115      163    73   01110011        s
         116      164    74   01110100        t
         117      165    75   01110101        u
         118      166    76   01110110        v
         119      167    77   01110111        w
         120      170    78   01111000        x
         121      171    79   01111001        y
         122      172    7A   01111010        z
         123      173    7B   01111011        {    (left opening brace)
         124      174    7C   01111100        |    (vertical bar)
         125      175    7D   01111101        }    (right closing brace)
         126      176    7E   01111110        ~    (tilde)
         127      177    7F   01111111      DEL    (delete)
Etiquettes: 

TestDisk & PhotoRec

TestDisk & PhotoRec sont deux utilitaires qui permettent de récupérer des données effaçées sur des supports tels que des cartes mémoires et des clés usb.

Disponibles pour Linux, Mac et Windows.

Pour le téléchargement et la documentation complète: http://www.cgsecurity.org/wiki/Main_Page

Pour une meilleur sécurité, il est préférable d'utiliser ces utilitaires sur une copie du support à analyser.

Pour ce faire, l'utilitaire dd disponible sous Linux fera très bien l'affaire.

Pour connaitre le fichier /dev/xxx correspondant au support à analyser, la commande dmesg sous Linux permet de fournir l'information après avoir, bien sûr, inséré le support dans l'ordinateur.

Ou alors, à l'aide du fichier /proc/partitions:

# cat /proc/partitions
major minor  #blocks  name

   8        0  976762584 sda
   8        1     194560 sda1
   8        2    1000448 sda2
   8        3          1 sda3
   8        5  975564800 sda5
 253        0  975560704 dm-0

Pour effectuer une image du support:

# dd if=/dev/sdx of=/tmp/image.dd

Attention, avec la commande dd, de ne pas inverser les fichiers des paramètres if et of. Les conséquences sont terribles et irréversibles.

Pour installer TestDisk et PhotoRec sous Linux:

# sudo apt-get update

# sudo apt-get install testdisk

Pour les autres systèmes, il est possible de le télécharger ici: http://www.cgsecurity.org/wiki/TestDisk_Download

Pour analyser l'image du support et rechercher les fichiers effaçés avec l'utilitaire testdisk:

# testdisk /tmp/image.dd

TestDisk permet de restaurer des fichiers à partir d'une liste créée suite à une analyse.

Pour analyser l'image du support et rechercher les fichiers effaçés avec l'utilitaire photorec:

# photorec /tmp/image.dd

PhotoRec permet la restauration de tous les fichiers trouvés. Le nombre de fichiers restaurés est plus important qu'avec l'utilitaire TestDisk.

Plus d'info pour TestDisk: http://www.cgsecurity.org/wiki/TestDisk

Plus d'info pour PhotoRec: http://www.cgsecurity.org/wiki/PhotoRec

Etiquettes: 

Utiliser les moteurs de recherche sur un site en particulier

Sur tous les moteurs de recherche (Google, Bing, QWant ...), il existe une fonction bien particulière qui permet d'effectuer des recherches sur un site en particulier (le site doit être indexé par le ou les moteurs de recherche bien sûr).

Pour cela, il suffit d'indiquer dans la zone de recherche, en plus du ou des termes recherchés, la mention "site:monsite.fr"

Exemple avec Google:

Pour lister toutes les pages d'un site indexées par le moteur de recherche, il suffit d'indiquer seulement la mention "site:monsite.fr"

Exemple avec QWant:

Il existe d'autres fonctions mais celles-ci sont vraiment les plus intéressantes.

Visioconférence en toute liberté

Le site meet.jit.si propose de la visioconférence libre et opensource.

Le service est utilisable en ligne via un navigateur, aucune installation sur les postes clients n'est nécessaire.

Il est également possible de créer son propre serveur mee.jit.si en téléchargeant les sources à cette adresse https://jitsi.org/jitsi-meet/

Sur le site meet.jit.si il est possible, via l'icone (en haut à droite) de configurer différents paramètres tels que son profil (pseudo et adresse mail), connecter son agenda Google et/ou Microsoft et la langue d'utilisation.

Comment créer une nouvelle visioconférence, tout simplement en saisissant un nom personnalisé dans la zone de texte "Démarrer une nouvelle réunion" ...

... et cliquer sur créer.

C'est tout bête.

Cette action génère une adresse HTTP du style https://meet.jit.si/MaNouvelleRéunion

Vous l'aurez compris, le nom de votre réunion est tout simplement ajouté à l'url principale https://meet.jit.si/

Partant de ce principe, il est extrêmement facile de générer des visioconférences en automatique.
Il faut tout de même penser à créer un nom de réunion pas trop facile à deviner pour éviter que n'importe qui s'invite à la réunion.

Pour cela, autant générer un nom de réunion avec des outils tels que:

En Bash

$ uuidgen
8eabdf78-6841-4f6f-9e35-bddd06b0c774

En Python

>>> import uuid
>>> print(uuid.uuid4())
d201619e-2245-4fba-bed2-57b5c2326f04

Une fois la réunion créée, il est possible de modifier les options de la réunion via les icones en bas à droite

Via l'icone , il est possible de modifier les options de sécurité de la réunion.

Il est possible d'activer le mode "lobby" qui permet d'autoriser les personnes à accéder à la réunion qu'après y avoir été autorisé.
Il est possible également de créer un mot de passe pour l'accès à la réunion.

L'icone , permet d'inviter des participants

tout simplement en copiant le lien HTTP de la réunion, en envoyant des mails ou en contactant un numéro de téléphone avec un code pin.

Il est même possible d'embarquer la réunion dans une iframe.

Enfin, l'icone permet d'accéder à différentes options

Comme options intéressantes il y a notamment la possibilité d'enregistrer la réunion, de couper les micros de tous les participants...

La barre d'icones en bas au milieu , permet de couper son micro, de couper sa vidéo et de quitter la réunion.

Pour terminer, la barre d'icones en bas à gauche permet de discuter avec tous les participants via un chat, de partager son écran et de lever la main si on souhaite intervenir.

L'outil est vraiment très complet et surtout très facile à utiliser.

A consommer sans modération.

Langages

HTML

HTML

Tous mes trucs & astuces en HTML

Etiquettes: 

Comment bien gérer les caractères accentués dans les pages HTML

ISO-8859-1 ou UTF-8/16.

Quel encodage choisir pour afficher correctement tous les caractères accentués qu'il y a dans la langue française ?

Tous les caractères accentués de la langue française sont gérés par la norme ISO-8859-1.

En HTML, il y a une astuce qui consiste à ne pas s'occuper du type de l'encodage des caractères mais utiliser à la place le nom d'entité des caractères (entity name).

Grâce à cette technique, tous les caractères seront toujours affichés correctement.

Source http://www.w3schools.com/


Par exemple, au lieu d'écrire ceci dans une page HTML

<html>
<body>
<p>Ceci est un caractère accentué</p>
</body>
</html>

Qui pourrait provoquer un problème à l'affichage si le codage des caractères est mal défini (comme ceci)

Ceci est un caractère accentué

Il vaut mieux écrire cela

<html>
<body>
<p>Ceci est un caract
&egrave;re accentu&eacute;</p>
</body>
</html>

Ce qui donne comme résultat

Ceci est un caractère accentué

Plus besoin de se soucier de l'encodage du fichier.

Autres caractères supportés en HTML

Autres caractères supportés en HTML

Character Entity Number Entity Name Description
Œ &#338; &OElig; capital ligature OE
œ &#339; &oelig; small ligature oe
Š &#352; &Scaron; capital S with caron
š &#353; &scaron; small S with caron
Ÿ &#376; &Yuml; capital Y with diaeres
ƒ &#402; &fnof; f with hook
ˆ &#710; &circ; modifier letter circumflex accent
˜ &#732; &tilde; small tilde
&#8194; &ensp; en space
&#8195; &emsp; em space
&#8201; &thinsp; thin space
&#8204; &zwnj; zero width non-joiner
&#8205; &zwj; zero width joiner
&#8206; &lrm; left-to-right mark
&#8207; &rlm; right-to-left mark
&#8211; &ndash; en dash
&#8212; &mdash; em dash
&#8216; &lsquo; left single quotation mark
&#8217; &rsquo; right single quotation mark
&#8218; &sbquo; single low-9 quotation mark
&#8220; &ldquo; left double quotation mark
&#8221; &rdquo; right double quotation mark
&#8222; &bdquo; double low-9 quotation mark
&#8224; &dagger; dagger
&#8225; &Dagger; double dagger
&#8226; &bull; bullet
&#8230; &hellip; horizontal ellipsis
&#8240; &permil; per mille 
&#8242; &prime; minutes
&#8243; &Prime; seconds
&#8249; &lsaquo; single left angle quotation
&#8250; &rsaquo; single right angle quotation
&#8254; &oline; overline
&#8364; &euro; euro
&#8482; &trade; trademark
&#8592; &larr; left arrow
&#8593; &uarr; up arrow
&#8594; &rarr; right arrow
&#8595; &darr; down arrow
&#8596; &harr; left right arrow
&#8629; &crarr; carriage return arrow
&#8968; &lceil; left ceiling
&#8969; &rceil; right ceiling
&#8970; &lfloor; left floor
&#8971; &rfloor; right floor
&#9674; &loz; lozenge
&#9824; &spades; spade
&#9827; &clubs; club
&#9829; &hearts; heart
&#9830; &diams; diamond

 

Etiquettes: 

Caractères Grec supportés en HTML

Caractères Grec supportés en HTML

Character Entity Number Entity Name Description
Α &#913; &Alpha; Alpha
Β &#914; &Beta; Beta
Γ &#915; &Gamma; Gamma
Δ &#916; &Delta; Delta
Ε &#917; &Epsilon; Epsilon
Ζ &#918; &Zeta; Zeta
Η &#919; &Eta; Eta
Θ &#920; &Theta; Theta
Ι &#921; &Iota; Iota
Κ &#922; &Kappa; Kappa
Λ &#923; &Lambda; Lambda
Μ &#924; &Mu; Mu
Ν &#925; &Nu; Nu
Ξ &#926; &Xi; Xi
Ο &#927; &Omicron; Omicron
Π &#928; &Pi; Pi
Ρ &#929; &Rho; Rho
Σ &#931; &Sigma; Sigma
Τ &#932; &Tau; Tau
Υ &#933; &Upsilon; Upsilon
Φ &#934; &Phi; Phi
Χ &#935; &Chi; Chi
Ψ &#936; &Psi; Psi
Ω &#937; &Omega; Omega
α &#945; &alpha; alpha
β &#946; &beta; beta
γ &#947; &gamma; gamma
δ &#948; &delta; delta
ε &#949; &epsilon; epsilon
ζ &#950; &zeta; zeta
η &#951; &eta; eta
θ &#952; &theta; theta
ι &#953; &iota; iota
κ &#954; &kappa; kappa
λ &#955; &lambda; lambda
μ &#956; &mu; mu
ν &#957; &nu; nu
ξ &#958; &xi; xi
ο &#959; &omicron; omicron
π &#960; &pi; pi
ρ &#961; &rho; rho
ς &#962; &sigmaf; sigmaf
σ &#963; &sigma; sigma
τ &#964; &tau; tau
υ &#965; &upsilon; upsilon
φ &#966; &phi; phi
χ &#967; &chi; chi
ψ &#968; &psi; psi
ω &#969; &omega; omega
ϑ &#977; &thetasym; theta symbol
ϒ &#978; &upsih; upsilon symbol
ϖ &#982; &piv; pi symbol

 

Etiquettes: 

Caractères ISO 8859-1

Caractères ISO 8859-1

Character Entity Number Entity Name Description
À &#192; &Agrave; capital a, grave accent
Á &#193; &Aacute; capital a, acute accent
 &#194; &Acirc; capital a, circumflex accent
à &#195; &Atilde; capital a, tilde
Ä &#196; &Auml; capital a, umlaut mark
Å &#197; &Aring; capital a, ring
Æ &#198; &AElig; capital ae
Ç &#199; &Ccedil; capital c, cedilla
È &#200; &Egrave; capital e, grave accent
É &#201; &Eacute; capital e, acute accent
Ê &#202; &Ecirc; capital e, circumflex accent
Ë &#203; &Euml; capital e, umlaut mark
Ì &#204; &Igrave; capital i, grave accent
Í &#205; &Iacute; capital i, acute accent
Î &#206; &Icirc; capital i, circumflex accent
Ï &#207; &Iuml; capital i, umlaut mark
Ð &#208; &ETH; capital eth, Icelandic
Ñ &#209; &Ntilde; capital n, tilde
Ò &#210; &Ograve; capital o, grave accent
Ó &#211; &Oacute; capital o, acute accent
Ô &#212; &Ocirc; capital o, circumflex accent
Õ &#213; &Otilde; capital o, tilde
Ö &#214; &Ouml; capital o, umlaut mark
Ø &#216; &Oslash; capital o, slash
Ù &#217; &Ugrave; capital u, grave accent
Ú &#218; &Uacute; capital u, acute accent
Û &#219; &Ucirc; capital u, circumflex accent
Ü &#220; &Uuml; capital u, umlaut mark
Ý &#221; &Yacute; capital y, acute accent
Þ &#222; &THORN; capital THORN, Icelandic
ß &#223; &szlig; small sharp s, German
à &#224; &agrave; small a, grave accent
á &#225; &aacute; small a, acute accent
â &#226; &acirc; small a, circumflex accent
ã &#227; &atilde; small a, tilde
ä &#228; &auml; small a, umlaut mark
å &#229; &aring; small a, ring
æ &#230; &aelig; small ae
ç &#231; &ccedil; small c, cedilla
è &#232; &egrave; small e, grave accent
é &#233; &eacute; small e, acute accent
ê &#234; &ecirc; small e, circumflex accent
ë &#235; &euml; small e, umlaut mark
ì &#236; &igrave; small i, grave accent
í &#237; &iacute; small i, acute accent
î &#238; &icirc; small i, circumflex accent
ï &#239; &iuml; small i, umlaut mark
ð &#240; &eth; small eth, Icelandic
ñ &#241; &ntilde; small n, tilde
ò &#242; &ograve; small o, grave accent
ó &#243; &oacute; small o, acute accent
ô &#244; &ocirc; small o, circumflex accent
õ &#245; &otilde; small o, tilde
ö &#246; &ouml; small o, umlaut mark
ø &#248; &oslash; small o, slash
ù &#249; &ugrave; small u, grave accent
ú &#250; &uacute; small u, acute accent
û &#251; &ucirc; small u, circumflex accent
ü &#252; &uuml; small u, umlaut mark
ý &#253; &yacute; small y, acute accent
þ &#254; &thorn; small thorn, Icelandic
ÿ &#255; &yuml; small y, umlaut mark

 

Etiquettes: 

Les caractères réservés en HTML

Les caractères réservés en HTML

Character Entity Number Entity Name Description
" &#34; &quot; quotation mark
' &#39; &apos; (does not work in IE) apostrophe 
& &#38; &amp; ampersand
< &#60; &lt; less-than
> &#62; &gt; greater-than

 

Etiquettes: 

Symbols ISO 8859-1

Symbols ISO 8859-1

Character Entity Number Entity Name Description
  &#160; &nbsp; non-breaking space
¡ &#161; &iexcl; inverted exclamation mark
¢ &#162; &cent; cent
£ &#163; &pound; pound
¤ &#164; &curren; currency
¥ &#165; &yen; yen
¦ &#166; &brvbar; broken vertical bar
§ &#167; &sect; section
¨ &#168; &uml; spacing diaeresis
© &#169; &copy; copyright
ª &#170; &ordf; feminine ordinal indicator
« &#171; &laquo; angle quotation mark (left)
¬ &#172; &not; negation
  &#173; &shy; soft hyphen
® &#174; &reg; registered trademark
¯ &#175; &macr; spacing macron
° &#176; &deg; degree
± &#177; &plusmn; plus-or-minus 
² &#178; &sup2; superscript 2
³ &#179; &sup3; superscript 3
´ &#180; &acute; spacing acute
µ &#181; &micro; micro
&#182; &para; paragraph
· &#183; &middot; middle dot
¸ &#184; &cedil; spacing cedilla
¹ &#185; &sup1; superscript 1
º &#186; &ordm; masculine ordinal indicator
» &#187; &raquo; angle quotation mark (right)
¼ &#188; &frac14; fraction 1/4
½ &#189; &frac12; fraction 1/2
¾ &#190; &frac34; fraction 3/4
¿ &#191; &iquest; inverted question mark
× &#215; &times; multiplication
÷ &#247; &divide; division

 

Symbols mathématiques supportés en HTML

Symbols mathématiques supportés en HTML

Character Entity Number Entity Name Description
&#8704; &forall; for all
&#8706; &part; part
&#8707; &exist; exists
&#8709; &empty; empty
&#8711; &nabla; nabla
&#8712; &isin; isin
&#8713; &notin; notin
&#8715; &ni; ni
&#8719; &prod; prod
&#8721; &sum; sum
&#8722; &minus; minus
&#8727; &lowast; lowast
&#8730; &radic; square root
&#8733; &prop; proportional to
&#8734; &infin; infinity
&#8736; &ang; angle
&#8743; &and; and
&#8744; &or; or
&#8745; &cap; cap
&#8746; &cup; cup
&#8747; &int; integral
&#8756; &there4; therefore
&#8764; &sim; similar to
&#8773; &cong; congruent to
&#8776; &asymp; almost equal
&#8800; &ne; not equal
&#8801; &equiv; equivalent
&#8804; &le; less or equal
&#8805; &ge; greater or equal
&#8834; &sub; subset of
&#8835; &sup; superset of
&#8836; &nsub; not subset of
&#8838; &sube; subset or equal
&#8839; &supe; superset or equal
&#8853; &oplus; circled plus
&#8855; &otimes; circled times
&#8869; &perp; perpendicular
&#8901; &sdot; dot operator

 

Etiquettes: 

Templates HTML

Appréhender le html et le javascript n'est pas toujours simple pour mettre en place un site internet avec un joli design et surtout "user friendly".

Le site suivant propose quelques templates html facilement adaptables

https://uidesigndaily.com/

ainsi que celui-ci https://html5up.net/

Etiquettes: 

Un squelette de page HTML

Voici un petit squelette afin de mettre en place une page HTML facilement et rapidement.

Elle inclut le CSS et le javascript Bootstrap ainsi que le javascript JQuery.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>My Page</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  </head>
  <body>
    <h1>My Page</h1>
    <h2>Start here</h2>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/latest/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  </body>
</html>

Un simple copier/coller dans un fichier "index.html" et le tour est joué.

Etiquettes: 

Java

Logo Java

Tous mes trucs et astuces pour utiliser Java

Ajouter / retrancher 1 année à une date en JAVA

package fr.quennec.date.custom;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;	

public class customDate {

	public static void dateMoinsUneAnnee(){

		GregorianCalendar calStr1 = new GregorianCalendar(); // Création d'un nouveau calendrier
		calStr1.setTime(new Date()); // Initialisation du calendrier avec la date du jour
		calStr1.add(GregorianCalendar.YEAR, -1); // On retranche 1 année
		String formatDate = "yyyy"; // Création du format pour afficher l'année sur 4 chiffres
		SimpleDateFormat sdf = new SimpleDateFormat(formatDate); // Initialisation du format de l'année sur 4 chiffres
		System.out.println(sdf.format(calStr1.getTime())); // Affichage du résultat

	}

}
Avec cette méthode, il est également possible d'ajouter une année au lieu de la retrancher :
calStr1.add(GregorianCalendar.YEAR, +1);
Il est également possible de manipuler les jours :
calStr1.add(GregorianCalendar.DAY_OF_YEAR, +1);
Et les mois :
calStr1.add(GregorianCalendar.MONTH, +1);
etc, etc ...
Etiquettes: 

Calculer l'écart entre 2 dates

Ce code calcul l'écart en années / mois / jours  (complets) entre 2 dates :

Ajouter les imports suivants :

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class ecartDate {

    static int UN = 1;
    static int DOUZE = 12;

    public static void main(String[] args) {
  
        Calendar calStr1 = Calendar.getInstance();
        Calendar calStr2 = Calendar.getInstance();
        Calendar calStr0 = Calendar.getInstance();
  
        Date date1 = null;
        Date date2 = null;
   
        int nbMois = 0;
        int nbAnnees = 0;
        long nbJours = 0;
   
        try {
                date1 = new SimpleDateFormat("dd/MM/yyyy").parse("25/01/2006");
        } catch (ParseException e) {
                e.printStackTrace();
        }
        try {
                date2 = new SimpleDateFormat("dd/MM/yyyy").parse("11/02/2014");
        } catch (ParseException e) {
                e.printStackTrace();
        }

        if (date1.equals(date2)) {
                return;
        }

        calStr1.setTime(date1);
        calStr2.setTime(date2);

        nbMois = 0;
        while (calStr1.before(calStr2)) {
                calStr1.add(GregorianCalendar.MONTH, UN);
                if (calStr1.before(calStr2) || calStr1.equals(calStr2)) {
                        nbMois++;
                }
        }
        nbAnnees = (nbMois / DOUZE);
        nbMois = (nbMois - (nbAnnees * DOUZE));

        calStr0 = Calendar.getInstance();
        calStr0.setTime(date1);
        calStr0.add(GregorianCalendar.YEAR, nbAnnees);
        calStr0.add(GregorianCalendar.MONTH, nbMois);
        nbJours = (calStr2.getTimeInMillis() - calStr0.getTimeInMillis()) / 86400000;

        System.out.print("Nb Annees : "+nbAnnees+"\n");
        System.out.print("Nb Mois : "+nbMois+"\n");
        System.out.print("Nb Jours : "+nbJours+"\n");

        }
}
Nb Annees : 8
Nb Mois : 0
Nb Jours : 17
Etiquettes: 

Décompiler des classes JAVA

Java Decompiler

Java Decompiler est un programme sous Windows / Linux / Mac qui permet de décompiler des classes JAVA.

Java Decompiler

Télécharger la version Windows

Télécharger la version Linux

 

Java: Connaitre la version de java utilisée pour la compilation d'une classe

Pour connaitre la version de java utilisée pour la compilation d'une classe, il suffit d'utiliser la commande javap présente dans le répertoire bin de java.

$ javap -verbose maclasse.class | grep version
  minor version: 0
  major version: 46

La version majeure permet de connaitre la version de java ayant compilé le fichier .class

Java 1.2 uses major version 46
Java 1.3 uses major version 47
Java 1.4 uses major version 48
Java 5 uses major version 49
Java 6 uses major version 50
Java 7 uses major version 51
Java 8 uses major version 52
Java 9 uses major version 53
Java 10 uses major version 54
Java 11 uses major version 55

Etiquettes: 

Java: Trier une ArrayList suivant les attributs d'un objet

Exemple avec l'objet personnalisé suivant

class Personne {
    
    String Nom;
    String Prenom;
    int Age;
    char Sexe;
    
    public Personne(String Nom, String Prenom, int Age, char Sexe) {
        this.Nom = Nom;
        this.Prenom = Prenom;
        this.Age = Age;
        this.Sexe = Sexe;
    }
    
    public boolean equals(Object o) {
        if (o instanceof Personne) {
            Personne tocompare = (Personne) o;
            if (tocompare.Nom.equals(this.Nom) && tocompare.Prenom.equals(this.Prenom)) {
                return true;
            }
        }
        return false;
    }
    
    public String toString(){
        return String.format("Nom: %-15sPrénom: %-15sAge: %-4dSexe: %1s", this.Nom, this.Prenom, this.Age, this.Sexe);
    }

    public String getNom() {
        return Nom;
    }

    public void setNom(String nom) {
        Nom = nom;
    }

    public String getPrenom() {
        return Prenom;
    }

    public void setPrenom(String prenom) {
        Prenom = prenom;
    }

    public int getAge() {
        return Age;
    }

    public void setAge(int age) {
        Age = age;
    }

    public char getSexe() {
        return Sexe;
    }

    public void setSexe(char sexe) {
        Sexe = sexe;
    }

}

Je vais maintenant créer 10 objets "Personne" et les ajouter dans une ArrayList.

import java.util.ArrayList;

public class TriPersoPersonne {

    public static void main(String[] args) {
        ArrayList<Personne> Personnes = new ArrayList<Personne>();
        Personnes.add(new Personne("Martine", "Merle", 38, 'F'));
        Personnes.add(new Personne("Martine", "Boutin", 22, 'M'));
        Personnes.add(new Personne("Claire", "Lombard", 28, 'M'));
        Personnes.add(new Personne("Eugène", "Faivre", 53, 'F'));
        Personnes.add(new Personne("Susanne", "Gérard", 91, 'F'));
        Personnes.add(new Personne("Zoé", "Lefebvre", 47, 'F'));
        Personnes.add(new Personne("Julien", "Ollivier", 86, 'M'));
        Personnes.add(new Personne("Thomas", "Lambert", 38, 'M'));
        Personnes.add(new Personne("Michel", "Gay", 69, 'M'));
        Personnes.add(new Personne("Bernadette", "Clément", 15, 'F'));
        for (Personne personne : Personnes) {
            System.out.println(personne);
        }
    }

}

Voici la sortie du System.out:

Nom: Martine        Prénom: Merle          Age: 38  Sexe: F
Nom: Martine        Prénom: Boutin         Age: 22  Sexe: M
Nom: Claire         Prénom: Lombard        Age: 28  Sexe: M
Nom: Eugène         Prénom: Faivre         Age: 53  Sexe: F
Nom: Susanne        Prénom: Gérard         Age: 91  Sexe: F
Nom: Zoé            Prénom: Lefebvre       Age: 47  Sexe: F
Nom: Julien         Prénom: Ollivier       Age: 86  Sexe: M
Nom: Thomas         Prénom: Lambert        Age: 38  Sexe: M
Nom: Michel         Prénom: Gay            Age: 69  Sexe: M
Nom: Bernadette     Prénom: Clément        Age: 15  Sexe: F

J'aimerais maintenant trier mon ArrayList en fonction de l'age et du sexe de la personne et mettre la priorité sur le sexe "F" en cas d'égalité de l'age.

import java.util.Comparator;

class TriParAgeEtSexe implements Comparator<Personne> {
    
    public int compare(Personne a, Personne b) {
        int compA = a.Age * 10 + sexeToInt(a.Sexe);
        int compB = b.Age * 10 + sexeToInt(b.Sexe);
        return compA - compB;
    }
    
    public static int sexeToInt(char sexe) {
        if (sexe == 'F') return 0;
        return 1;
    }
    
}

Exécutons maintenant le tri de l'ArrayList avec mon tri personnalisé.

import java.util.ArrayList;
import java.util.Collections;

public class TriPersoPersonne {

    public static void main(String[] args) {
        ArrayList<Personne> Personnes = new ArrayList<Personne>();
        Personnes.add(new Personne("Martine", "Merle", 38, 'F'));
        Personnes.add(new Personne("Martine", "Boutin", 22, 'M'));
        Personnes.add(new Personne("Claire", "Lombard", 28, 'M'));
        Personnes.add(new Personne("Eugène", "Faivre", 53, 'F'));
        Personnes.add(new Personne("Susanne", "Gérard", 91, 'F'));
        Personnes.add(new Personne("Zoé", "Lefebvre", 47, 'F'));
        Personnes.add(new Personne("Julien", "Ollivier", 86, 'M'));
        Personnes.add(new Personne("Thomas", "Lambert", 38, 'M'));
        Personnes.add(new Personne("Michel", "Gay", 69, 'M'));
        Personnes.add(new Personne("Bernadette", "Clément", 15, 'F'));
        for (Personne personne : Personnes) {
            System.out.println(personne);
        }
        System.out.println("\nAprès le tri par age et sexe\n");
        Collections.sort(Personnes, new TriParAgeEtSexe());
        for (Personne personne : Personnes) {
            System.out.println(personne);
        }
    }

}

Ce qui donne ceci:

Nom: Martine        Prénom: Merle          Age: 38  Sexe: F
Nom: Martine        Prénom: Boutin         Age: 22  Sexe: M
Nom: Claire         Prénom: Lombard        Age: 28  Sexe: M
Nom: Eugène         Prénom: Faivre         Age: 53  Sexe: F
Nom: Susanne        Prénom: Gérard         Age: 91  Sexe: F
Nom: Zoé            Prénom: Lefebvre       Age: 47  Sexe: F
Nom: Julien         Prénom: Ollivier       Age: 86  Sexe: M
Nom: Thomas         Prénom: Lambert        Age: 38  Sexe: M
Nom: Michel         Prénom: Gay            Age: 69  Sexe: M
Nom: Bernadette     Prénom: Clément        Age: 15  Sexe: F

Après le tri par age et sexe

Nom: Bernadette     Prénom: Clément        Age: 15  Sexe: F
Nom: Martine        Prénom: Boutin         Age: 22  Sexe: M
Nom: Claire         Prénom: Lombard        Age: 28  Sexe: M
Nom: Martine        Prénom: Merle          Age: 38  Sexe: F
Nom: Thomas         Prénom: Lambert        Age: 38  Sexe: M
Nom: Zoé            Prénom: Lefebvre       Age: 47  Sexe: F
Nom: Eugène         Prénom: Faivre         Age: 53  Sexe: F
Nom: Michel         Prénom: Gay            Age: 69  Sexe: M
Nom: Julien         Prénom: Ollivier       Age: 86  Sexe: M
Nom: Susanne        Prénom: Gérard         Age: 91  Sexe: F

Le résultat est parfait.
L'ArrayList est triée par age croissant et si égalité, alors le sexe "F"  est positionné avant le sexe "M".

Et pour un tri décroissant, il suffit de modifier le "return" de la méthode "compare" de la classe "TriParAgeEtSexe" par:

return compB - compA;

Pour info, la méthode "equals" de la classe "Personne" permet d'utiliser la méthode "contains" de la classe "ArrayList" afin de vérifier si un objet "Personne" n'est pas déjà dans la liste avant de l'ajouter afin d'éviter les doublons.

 

Remplacer les caractères accentués d'une chaine par des caractères simples

Exemple d'une méthode permettant de remplacer tous les caractères accentués d'une chaine (String) par des caractères simples :

public static String translate(String src) {
        StringBuffer result = new StringBuffer();
        if(src!=null && src.length()!=0) {
            int index = -1;
            char c = (char)0;
            String chars= "àâäéèêëîïôöùûüç";
            String replace= "aaaeeeeiioouuuc";
            for(int i=0; i<src.length(); i++) {
                c = src.charAt(i);
                if( (index=chars.indexOf(c))!=-1 )
                    result.append(replace.charAt(index));
                else
                    result.append(c);
            }
        }
        return result.toString();
    }
Etiquettes: 

Tester si un nombre est un Double

1 - Créer une classe "Char.java" contenant les différentes variables utilisées :

public class Char {

    public static final char SPACE = (char)32;

    public static final char LOWER_N = (char)110;

    public static final char LOWER_U = (char)117;

    public static final char LOWER_L = (char)108;

    public static final char DOT = (char)46;

    public static final char COMMA = (char)44;

    public static final int DIGIT_BEGIN = 48;

    public static final int DIGIT_END = 57;

}
2 - Créer une classe "TestUtil.java" contenant les différentes fonctions utilisées :
public class TestUtil {

    public static boolean isNull(String s) {
        if (s == null) {
            return true;
        }

        int counter = 0;

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c == Char.SPACE) {
                continue;
            }
            else if (counter > 3) {
                return false;
            }

            if (counter == 0) {
                if (c != Char.LOWER_N) {
                    return false;
                }
            }
            else if (counter == 1) {
                if (c != Char.LOWER_U) {
                    return false;
                }
            }
            else if ((counter == 2) || (counter == 3)) {
                if (c != Char.LOWER_L) {
                    return false;
                }
            }

            counter++;
        }

        if ((counter == 0) || (counter == 4)) {
            return true;
        }

        return false;
    }
    
    public static boolean isDouble(String number){
        
//        On test si la valeur est nulle
        if (isNull(number)) {
            return false;
        }
        
//        On remplace les virgules par les points (séparateur décimal)
        number = number.replace(Char.COMMA, Char.DOT);
        
        boolean firstDot = true;
        
        
//        On test chaque caractère
//        Si le caractère est un chiffre on test le suivant
//        Si le caractère est un "point" on initialise la variable "firstDot" à "false" et on test le suivant
//        Si le caractère n'est pas un chiffre ou un autre "point" on retourne "false"
//        Sinon, la valeur est un "Double" et on retourne "true"
        for (char c : number.toCharArray()) {
            if (!isDigit(c)) {
                if (c == Char.SPACE) {
                    continue;
                }
                if(c == Char.DOT && firstDot){
                    firstDot = false;
                    continue;
                } else {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    public static boolean isDigit(char c) {
        int x = c;

        if ((x >= Char.DIGIT_BEGIN) && (x <= Char.DIGIT_END)) {
            return true;
        }

        return false;
    }
}

3 - Créer une classe "testDouble.java" afin de tester les différentes fonctions :

public class testDouble {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String s = "12,5689";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
        s = "12.56456456";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
        s = null;
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi false
        
        s = "a56456.5646";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi false
        
        s = "23156.86798,546";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi false
        
        s = "etet.ert47d6t";
            
        System.out.println(TestUtil.isDouble(s));    // Renvoi false
        
        s = "8989797";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
        s = "8989 797";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
        s = "89 897.97";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
        s = "89 897,97";
        
        System.out.println(TestUtil.isDouble(s));    // Renvoi true
        
    }

}
Etiquettes: 

JavaScript

Logo JavaScript

 

 

 

 

 

Tous mes trucs et astuces pour utiliser JavaScript

Mettre automatiquement le focus dans un champ de formulaire

<script type="text/javascript">

    function PMA_focusInput(){
        var objet = document.getElementById('id_element_formulaire');
        objet.focus();
    }
    window.setTimeout('PMA_focusInput()', 500);
</script>
Etiquettes: 

Modifier automatiquement la valeur du paramètre maxlength d'un ou plusieurs éléments textarea

Voici une petite fonction javascript qui permet de modifier la valeur du paramètre "maxlength" d'un élément "textarea".

Dans l'exemple suivant, la fonction modifie la valeur du paramètre "maxlength" à illimité (-1) uniquement si la valeur est inférieur à 1000 caractères.

function updMaxLthTxt () {
    var txts = document.getElementsByTagName('textarea');
    for(var i = 0, l = txts.length; i < l; i++) {
        var len = parseInt(txts[i].getAttribute("maxlength"), 10);
        if(len < 1000) {
            txts[i].setAttribute("maxlength", -1);
        }
    }
}

Pour exécuter cette fonction automatiquement après le chargement de la page html:

<html>
<head>
</head>
<body onload = "updMaxLthTxt();">
.....
</body>
</html>

Modifier le CSS avec JavaScript

Ajouter une classe à une balise en fonction de son contenu :

Ce script javascript parcourt toutes les balises <p> de la page web et si le contenu de chaque balise commence par "$ ", une balise <span> est automatiquement ajoutée avec une classe "code"

<script language=javascript type="text/javascript">
function addCode()
{
var parag = document.getElementsByTagName('p');
var myRegex = /^\$ /;
for (var i=0, c=parag.length; i<c; i++)
{
if(myRegex.test(parag[i].innerHTML))
{
parag[i].innerHTML = '<span class="code">'+(parag[i].innerHTML)+'</span>';
}
}
}
</script>

Pour activer la fonction javascript il suffit simplement de rajouter onload = "addCode();" dans la balise <body> de chaque page web.

<html>
<head>
</head>
<body onload = "addCode();">
.....
</body>
</html>

javascript: mettre en surbrillance des mots dans un contenu html

Avec l'arrivée du HTML5, de nouvelles balises ont fait leur apparition.

Pour la mise en surbrillance de mots/textes dans un contenu html, il existe dorénavant la balise <mark>

Voici une fonction javascript (jquery) qui permet de modifier le contenu html afin d'y insérer ces fameuses balises <mark>

        function searchKeyword(keyword){
            var content = $("#content").html();
            var re = new RegExp(keyword, "gi");
            var new_content = content.replace(re, "<mark>" + keyword + "</mark>");
            $("#content").html(new_content);
        }

Cette fonction prend en paramètre le mot clé à mettre en surbrillance.
Une nouvelle expression Regex est créée avec le mot clé et les paramètres "g" pour une recherche globale, et "i" pour une recherche ignorant la casse.
La méthode "replace" utilise la regex afin d'y insérer la nouvelle balise <mark>

Exécution de la fonction avec un mot clé ou une liste de mots clé

        $( document ).ready(function() {
            // avec un mot clé
            searchKeyword("foo");
            // avec une liste de mots clé
            var keywords = "foo bar baz qux quux corge grault".split(" ");
            keywords.forEach(searchKeyword);
        });

 

PHP

PHP: Obtenir le numéro de la dernière semaine de l'année en cours

Voici une petite fonction PHP qui permet de retourner le numéro de la dernière semaine de l'année en cours.

<?php

function lastWeekNumberOfYear(){
    $year = date('Y');
    $week_count = date('W', strtotime($year . '-12-31'));
    if ($week_count == '01'){
        $week_count = date('W', strtotime($year . '-12-24'));
    }
    return intval($week_count);
}

 

Etiquettes: 

PHP: REGEX

Regex

Des exemples de REGEX PCRE avec PHP

 

La fonction preg_match permet d'effectuer une recherche dans une chaine de caractères et renvoie TRUE si le texte cherché est trouvé sinon FALSE.

Cela permet d'effectuer des controles sur des saisies clavier.

Vérifier que la saisie effectuée correspond bien à un nombre de 6 chiffres (123456).

<?php preg_match("#^[0-9]{6}$#","123456"); ?>
// Renvoie TRUE car 123456 est bien un nombre de 6 chiffres.

Explications :
#^[0-9]{6}$#
Le caractère # correspond aux délimiteurs du texte à chercher.
Le caractère ^ indique que la recherche doit s'effectuer à partir du début de la chaine de texte.
Le caractère $ indique que la recherche doit s'effectuer jusqu'à la fin de la chaine de texte.
La classe de caractères [0-9] indique que les caractères recherchés dans la chaine de texte doivent être des chiffres allant de 0 à 9.
Le quantificateur {6} indique le nombre exact de caractères à rechercher dans la chaine de texte.

Autres exemples :

<?php preg_match("#^[0-9]{6}$#","123A56"); ?>
// Renvoie FALSE car il y a un 'A' dans la chaine de caractères.
<?php preg_match("#^[0-9]{6}$#","12345"); ?>
// Renvoie également FALSE car il n'y a que 5 chiffres dans la chaine de caractères '12345'
<?php preg_match("#^[0-9]{6}#","546781A"); ?>
// Renvoie TRUE car la recherche demandée s'effectue uniquement à partir du début de la chaine de caractères (suppression du caractère $) et que celle çi doit donc commencer par 6 chiffres allant de 0 à 9.

Vérifier que la saisie effectuée correspond bien à une adresse mail

<?php preg_match("#^[-.\w]{1,}@[-.\w]{2,}\.[a-zA-Z]{2,4}$#","toto@gmail.com"); ?>
// Renvoie TRUE car le texte 'toto@gmail.com' correspond bien à une adresse mail.

Explications :
#^[-.\w]{1,}@[-.\w]{2,}\.[a-zA-Z]{2,4}$#
Nous retrouvons toujours nos délimiteurs #
Nous retrouvons également nos symboles de dédut ^ et de fin $ de recherche
Notre adresse mail doit être de la forme xxxx@xxxx.xxxx
Le début de l'adresse (à la gauche de l' @) peut contenir des caractères allant de 'A' à 'Z' (minuscule et majuscule), un point '.', un tiret haut '-', un tiret bas '_' et des chiffres allant de 0 à 9 et doit contenir au minimum 1 caractère. Pour indiquer tout cela, nous inscrivons donc :
[-.\w]{1,}
La classe de caractères contient donc le '-' (toujours le placer en première position dans une classe de caractères) suivi du '.' et enfin du raccourci '\w' correspondant à la même chose que [a-zA-Z0-9_], c'est à dire toutes les lettres de l'alphabet minuscule et majuscule, tous les chiffres et le tiret bas '_'. Le tout suivi du quantificateur {1,} indiquant qu'il doit y avoir au minimum 1 caractère.
Il faut ensuite indiquer le caractère @
Ensuite, le nom de domaine doit avoir les mêmes caractéristiques que le début de l'adresse mais doit comporter au minimum 2 caractères, nous inscrivons donc :
[-.\w]{2,}
Nous retrouvons la même classe de caractères que pour le début de l'adresse mais nous modifions le quantificateur {2,} pour indiquer qu'il doit y avoir au minimum 2 caractères.
Nous indiquons ensuite le point '.' mais celui çi doit être échappé car en PCRE, il correspond à un métacaractère indiquant 'n'importe quel caractère'.
\.
Enfin, nous controlons l'extension du nom de domaine. Celui çi ne doit contenir que des lettres et avoir au minimum 2 caractères et au maximum 4. Nous indiquons donc :
[a-zA-Z]{2,4}
Nous retrouvons la classe [a-zA-Z] indiquant toutes les lettres de l'alphabet en minuscule et en majuscule ainsi que le quantificateur {2,4} indiquant un minimum de caractères de 2 et un maximum de 4.

Etiquettes: 

PHP: Requête MySql

Connexion à une base de données MySql

<?php
try
{
     $bdd = new PDO('mysql:host=localhost;dbname=test','user','password');
}
catch(Exception $e)
{
     die('Erreur :'.$e->getMessage());
}
?>

Exécuter une requête simple

<?php
$req = $bdd->query('SELECT id, nom, prenom FROM carnet WHERE id >= 1 AND id <= 10 ORDER BY id') or die (print_r($bdd->errorInfo()));
?>

Préparer et exécuter une requête avec des variables

<?php
$req = $bdd->prepare('SELECT id, nom, prenom FROM carnet WHERE id >= :idMin AND id <= :idMax ORDER BY id') or die (print_r($bdd->errorInfo()));
$req->execute(array(
                     'idMin'=> $_POST['postIdMin'],
                     'idMax' => $_GET['getIdMax']
          ));
?>

Lire le résultat d'une requête

<?php
while($donnees = $req->fetch())
{
     echo $donnees['id'].'<br />';
     echo $donnees['nom'].'<br />';
     echo $donnees['prenom'];
}
?>

Fermer la requête

<?php
$req->closeCursor();
?>
Etiquettes: 

PHP: Utiliser la bibliothèque Monolog dans vos scripts

La bibliothèque Monolog permet de journaliser différentes informations lors de l'exécution de scripts PHP.

Son intégration via un framework de développement tel que Symfony, CakePHP se fait de manière quasi automatique mais s'il s'agit de l'utiliser dans un script fait à la main, c'est un petit peu plus compliqué.

Voici ma méthode (qui peut certainement être améliorée):

Cette bibliothèque pouvant être utilisée dans plusieurs projets, j'ai donc décidé de l'installer dans un dossier commun à tous mes projets web (/var/www/).
Je vais donc l'installer dans le dossier /var/www/commun

$ cd /var/www
$ mkdir commun
$ chown -R www-data:www-data commun
$ cd commun

Cette bibliothèque utilisant les namespaces, il est nécessaire d'installer la bibliothèque ClassLoader qui va nous permettre d'utiliser tout simplement les namespaces dans nos différents scripts.

attention Toutes les installations seront faites à l'aide de la commande git

$ apt-get install git

$ git clone https://github.com/symfony/ClassLoader.git
$ git clone https://github.com/Seldaek/monolog.git
$ git clone https://github.com/php-fig/log.git

Trois projets sont donc installés dans le dossier commun

  • ClassLoader
  • log
  • monolog

Pour protéger l'accès à ce dossier (/var/www/commun):

$ echo "Deny From All" > .htaccess

Pour l'utilisation des namespaces, il est nécessaire de créer un fichier de paramétrage des namespaces (toujours dans le dossier /var/www/commun):

$ cat monolog.php
<?php
 
require_once __DIR__.'/ClassLoader/ClassLoader.php';
 
use Symfony\Component\ClassLoader\ClassLoader;
 
$loader = new ClassLoader();
$loader->register();
 
$loader->addPrefix('Monolog', __DIR__.'/monolog/src');
$loader->addPrefix('Psr', __DIR__.'/log');

Ce fichier intègre donc la classe ClassLoader.php et configure les namespaces Monolog & Psr nécessaire à l'utilisation de la bibliothèque monolog.

L'installation et le paramétrage est terminé, passons à la manière d'utiliser cette bibliothèque.

Je vais créer un projet test ainsi que deux fichiers index.php et init_log.php.

Le fichier index.php contiendra mon projet et le fichier init_log.php la configuration du fichier de log de mon projet.

$ cd /var/www
$ mkdir test
$ chown -R www-data:www-data test
$ cd test

infoPour la documentation de monolog, tout est expliqué sur la page github du projet https://github.com/Seldaek/monolog

Détail de ma configuration:

$ nl init_log.php
     1  <?php
     2      require_once '/var/www/commun/monolog.php';
     3      use Monolog\Logger;
     4      use Monolog\Handler\RotatingFileHandler;
     5      use Monolog\Handler\StreamHandler;
     6      use Monolog\Formatter\LineFormatter;
     7      $dateFormat = "Y-m-d H:i:s";
     8      $output     = "[%datetime%] %channel% %level_name%: %message% %context% %extra%\n";
     9      $formatter  = new LineFormatter($output, $dateFormat);
    10      $stream     = new StreamHandler(__DIR__.'/test.log', Logger::DEBUG); // Pour obtenir un fichier de log global
    11      // $stream     = new RotatingFileHandler(__DIR__.'/test.log', Logger::DEBUG); // Pour obtenir un fichier de log par jour (fichier horodaté)
    12      $stream->setFormatter($formatter);
    13      $logger     = new Logger('test');
    14      $logger->pushHandler($stream);

Ligne 2, j'inclus le fichier monolog.php qui contient toute la configuration de monolog.
Ligne 3 à 6, déclaration des différents use comme indiqué surla page du projet.
Ligne 7 à 9, je redéfini le format des données dans les logs.
Ligne 10 ou 11, permet d'indiquer l'emplacement du fichier de log ainsi que la manière dont il sera nommé et le niveau de log (DEBUG, INFO, WARNING etc etc...). Comme indiqué dans les commentaires, soit un fichier de log global (test.log), soit un fichier de log journalier et horodaté (test-2015-08-14.log).
Ligne 12, affectation du format défini au fichier de log.
Ligne 13 à 14, création d'un nouveau logger nommé test (avec le nom du projet par exemple).

infoIl est possible d'avoir un seul fichier de log commun pour plusieurs projets. Du coup, il sera facile de parser le fichier de log en se basant sur le nom du projet indiqué lors de la création du logger (ligne 13)

Utilisation dans mon fichier index.php:

$ cat index.php
<?php
    require_once __DIR__.'/init_log.php';
    ...
    $logger->addInfo('Connexion', array(
                'User' => $user,
                'Nom' => $nom,
                'Prénom' => $prenom,
            ));

J'inclus mon fichier init_log.php et j'écris tout ce que je veux dans mon fichier de log à l'aide de la commande $logger->addInfo().
Le premier paramètre est une chaine de texte et le second un tableau.

Difficile de faire plus simple.

Etiquettes: 

PHP: igalerie - scan automatique des albums (ftp)

igalerie est une application php qui permet de créer des galeries d'images en ligne.

L'ajout d'images dans la galerie peut se faire via ftp (très pratique) mais il faut obligatoirement exécuter une fonction pour que les images soient affichées dans la galerie.

Cette fonction est disponible via un bouton accessible uniquement dans la section administration du site.

Il est donc nécessaire d'être authentifié pour exécuter cette fonction.

Pour effectuer ce scan automatiquement à intervalle régulière, j'ai donc mis au point le script php suivant avec l'extension curl.

Ce script est disponible ici .

$ nl curl.igalerie.php
     1    <?php
     2    session_start();
       
     3    $site = "https://adresse.de.mon.site.igalerie.fr";
     4    $lien = "/login";
     5    $lien3 = "/admin/?q=ftp";
       
     6    $path_cookie = '/tmp/cookie.'.session_id().'.txt';
     7    if (file_exists(realpath($path_cookie))) unlink($path_cookie);
     8    if (!file_exists(realpath($path_cookie))) touch($path_cookie);
       
     9    $curl = curl_init();
       
    10    $postfields = array();
       
    11    $postfields['auth_login'] = 'mon.user';
    12    $postfields['auth_password'] = 'mon.password';
    13    $postfields['submit'] = 'Valider';
       
    14    $postfields = http_build_query($postfields);
       
    15    curl_setopt($curl, CURLOPT_URL, $site.$lien);
    16    curl_setopt($curl, CURLOPT_COOKIEFILE, realpath($path_cookie));
    17    curl_setopt($curl, CURLOPT_COOKIEJAR, realpath($path_cookie));
    18    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    19    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    20    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    21    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    22    curl_setopt($curl, CURLOPT_HEADER, true);
    23    curl_setopt($curl, CURLOPT_POST, true);
    24    curl_setopt($curl, CURLOPT_POSTFIELDS, $postfields);
    25    curl_setopt($curl, CURLOPT_COOKIESESSION, true);
       
    26    $return = curl_exec($curl);
    27    $headers = curl_getinfo($curl);
       
    28    if (!$headers['http_code'] == '200'){
    29        echo "Erreur Step 1";
    30        exit(1);
    31    }
       
    32    curl_setopt($curl, CURLOPT_URL, $site.$lien3);
    33    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    34    curl_setopt($curl, CURLOPT_COOKIEFILE, realpath($path_cookie));
    35    curl_setopt($curl, CURLOPT_COOKIEJAR, realpath($path_cookie));
    36    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    37    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    38    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    39    curl_setopt($curl, CURLOPT_HEADER, true);
    40    curl_setopt($curl, CURLOPT_COOKIESESSION, true);
       
    41    $return = curl_exec($curl);
    42    $headers = curl_getinfo($curl);
       
    43    if (!$headers['http_code'] == '200'){
    44        echo "Erreur Step 2";
    45        exit(1);
    46    }
       
    47    $dom = new DOMDocument;
    48    @$dom->loadHTML($return);
    49    $inputs = $dom->getElementsByTagName('input');
    50    foreach ($inputs as $input) {
    51        $cle = $input->getAttribute('name');
    52        $valeur = "";
    53        if($cle=="anticsrf"){
    54            $valeur = $input->getAttribute('value');
    55        }
    56        if(!$valeur=="") break;
    57    }
       
    58    $postfields = array();
       
    59    $postfields['publish_images'] = 'on';
    60    $postfields['anticsrf'] = "$valeur";
    61    $postfields['action'] = 'scan';
       
    62    $postfields = http_build_query($postfields);
       
    63    curl_setopt($curl, CURLOPT_URL, $site.$lien3);
    64    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    65    curl_setopt($curl, CURLOPT_COOKIEFILE, realpath($path_cookie));
    66    curl_setopt($curl, CURLOPT_COOKIEJAR, realpath($path_cookie));
    67    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    68    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    69    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    70    curl_setopt($curl, CURLOPT_HEADER, true);
    71    curl_setopt($curl, CURLOPT_COOKIESESSION, true);
    72    curl_setopt($curl, CURLOPT_POST, true);
    73    curl_setopt($curl, CURLOPT_POSTFIELDS, $postfields);
       
    74    $return = curl_exec($curl);
    75    $headers = curl_getinfo($curl);
       
    76    if (!$headers['http_code'] == '200'){
    77        echo "Erreur Step 3";
    78        exit(1);
    79    }
       
    80    @$dom->loadHTML($return);
    81    $div = $dom->getElementById('ftp_report');
    82    $ps = $div->getElementsByTagName('p');
       
    83    foreach ($ps as $p => $value) {
    84        echo "$value->nodeValue\n";
    85    }
       
    86    curl_close($curl);
       
    87    exit(0);

Quelques petites explications :

Ligne 4 : adresse http(s) de la galerie.

Ligne 5 : lien qui permet d'accéder à la page d'authentification.

Ligne 6 : lien qui permet d'accéder à la page du scan ftp.

Ligne 8 à 10 : initialisation du cookie de session.

Ligne 12 : initialisation de l'extension curl.

Ligne 14 à 20 : initialisation du formulaire d'authentification.

Ligne 22 à 35 : validation du formulaire d'authentification.

Le formulaire permettant le scan des albums étant protégé par un champ anti-csrf wikipedia, il est nécessaire de récupérer la valeur de ce champ avant de poster le formulaire.

Ligne 42 à 53 : récupération du contenu de la page contenant le formulaire permettant le scan des albums.

Ligne 60 à 70 : on parse le contenu de la page afin de récupérer la valeur du champ "anticsrf".

Ligne 72 à 78 : initialisation du formulaire de scan.

Ligne 80 à 93 : validation du formulaire de scan.

Ligne 100 à 106 : on parse le contenu de la page, après validation du formulaire, afin de récupérer le rapport du scan et on l'affiche sur la sortie standard.

Ligne 108 à 110 : on ferme la connexion curl et on quitte le script.

Pour l'exécuter :

$ php curl.igalerie.php

Pour ne pas afficher le rapport :

$ php curl.igalerie.php >/dev/null

Pour envoyer le rapport par mail :

$ php curl.igalerie.php | mail -s "Rapport Scan Auto Igalerie" moi@domain.com

info Une entrée crontab pour l'automatisation.

PHP: phpSysInfo

phpSysInfo est un script PHP qui permet d'afficher, dans un navigateur, l'état complet du serveur sur lequel il est exécuté.

Une démo est disponible ici http://phpsysinfo.sourceforge.net/phpsysinfo/index.php?disp=bootstrap

Adresse du site officiel http://phpsysinfo.github.io/phpsysinfo/

Pour le téléchargement, les sources sont disponibles sur GitHub https://github.com/phpsysinfo/phpsysinfo

L'installation est ultra simple.
Il suffit de cloner les sources GIT dans un répertoire du serveur accessible en web.
De faire une copie du fichier phpsysinfo.ini.new en phpsysinfo.ini
D'éditer le fichier phpsysinfo.ini afin d'effectuer les réglages désirés
Et enfin, ouvrir son navigateur et ouvrir l'url correspondante

# cd /var/www
# git clone https://github.com/phpsysinfo/phpsysinfo.git
# cd phpsysinfo
# cp -a phpsysinfo.ini.new phpsysinfo.ini

Les données sont également accessible en XML /xml.php?plugin=complete ou en JSON /xml.php?plugin=complete&json

Une application pour Android est également disponible http://rk4an.github.io/psiandroid/

Etiquettes: 

Python

logo python

Etiquettes: 

Python: Liste des paquets et des modules indispensables

Pour éviter tous problèmes lors des installations des modules externes pour Python, il est nécessaire d'installer les paquets suivants:

# apt-get install build-essential python-dev python3-dev python3-pip

# apt-get install libmysqlclient-dev gcc 

J'essaie de maintenir cette liste le plus à jour possible

Concernant les modules externes:

# pip install bpython httpie mysqlclient lxml requests virtualenv numpy bs4 pandas logger clipboard matplotlib pathlib pyftpdlib python-dateutil faker

 

Etiquettes: 

Python: Mettre à jour tous les paquets obsolètes

La commande pip list permet d'afficher la liste complète de tous les paquets python installés avec la version.

# pip list
asn1crypto (0.22.0)
backports.ssl-match-hostname (3.5.0.1)
batinfo (0.4.2)
blessings (1.6)
bottle (0.12.13)
bpython (0.16)
certifi (2017.4.17)
cffi (1.10.0)
chardet (3.0.4)
...
requests (2.18.1)
setuptools (36.2.0)
six (1.10.0)
statsd (3.2.1)
urllib3 (1.21.1)
wcwidth (0.1.7)
websocket-client (0.44.0)
zeroconf (0.19.1)

La commande suivante affiche les principales options disponibles pour la commande pip list:

# pip list -h
Usage:
  pip list [options]

Description:
  List installed packages, including editables.

  Packages are listed in a case-insensitive sorted order.

List Options:
  -o, --outdated              List outdated packages
  -u, --uptodate              List uptodate packages
  -e, --editable              List editable projects.
  -l, --local                 If in a virtualenv that has global access, do not list globally-installed packages.
  --user                      Only output packages installed in user-site.
  --pre                       Include pre-release and development versions. By default, pip only finds stable versions.
  --format <list_format>      Select the output format among: legacy (default), columns, freeze or json.
  --not-required              List packages that are not dependencies of installed packages.

Donc, pour obtenir la liste complète de tous les paquets Python obsolètes, il suffit d'utiliser l'option -o

# pip list -o
Package    Version Latest Type
---------- ------- ------ -----
decorator  4.0.11  4.1.1  wheel
setuptools 36.0.1  36.2.0 wheel

Ou alors directement avec la commande python et le module pip

# python3 -m pip list -o

La commande suivante va effectuer la mise à jour complète des paquets obsolètes.

# for x in $(pip list -o --format=columns | sed -n '3,$p' | cut -d' ' -f1); do pip install $x --upgrade; done
Collecting decorator
  Downloading decorator-4.1.1-py2.py3-none-any.whl
Installing collected packages: decorator
  Found existing installation: decorator 4.0.11
    Uninstalling decorator-4.0.11:
      Successfully uninstalled decorator-4.0.11
Successfully installed decorator-4.1.1
Collecting setuptools
  Downloading setuptools-36.2.0-py2.py3-none-any.whl (477kB)
    100% |████████████████████████████████| 481kB 1.1MB/s
Installing collected packages: setuptools
  Found existing installation: setuptools 36.0.1
    Uninstalling setuptools-36.0.1:
      Successfully uninstalled setuptools-36.0.1
Successfully installed setuptools-36.2.0

Attention à la version de pip utilisée.

En général, pip concerne Python2 et pip3 Python3

Une autre méthode:

$ python3 -m pip list -o > outdated
$ for x in $(sed -n '3,$p' outdated | awk '{print $1}'); do python3 -m pip install --upgrade --user $x; done

 

Python: Ajouter ou retrancher des mois à une date

A ma connaissance, en Python, à l'aide de la méthode timedelta du module datetime, il est très facile d'ajouter ou de retrancher des jours à une date mais quand il s'agit de le faire avec des mois, les choses se compliquent.
En effet tous les mois n'ayant pas le même nombre de jours, il devient incertain d'utiliser cette méthode.

J'ai donc développé cette fonction getNMonthsLessOrMore qui utilise à la fois le module datetime et le module calendar pour arriver au résultat souhaité.

from datetime import datetime
import calendar

def getNMonthsLessOrMore(startDate : datetime, nbMonths : int) -> list:

    class DateTimeFormat(Exception):
        pass

    class NbMonthsFormat(Exception):
        pass

    def getDayOfMonth(year, month, day):
        tmpCal = list(calendar.Calendar().itermonthdays(year, month))
        for x in range(day, 0, -1):
            if x in tmpCal: return x
        return 1

    try:
        if not isinstance(startDate, datetime): raise DateTimeFormat()
        if not isinstance(nbMonths, int): raise NbMonthsFormat()
    except DateTimeFormat:
        return False, "La date de départ doit être au format datetime.datetime"
    except NbMonthsFormat:
        return False, "Le nombre de mois doit être un entier positif ou négatif"
    else:
        if nbMonths == 0: return [startDate]
        L = []
        tmpDate = datetime(startDate.year, startDate.month, 1)
        if nbMonths < 0:
            for i in range(0, nbMonths, -1):
                y, m = calendar.prevmonth(tmpDate.year, tmpDate.month)
                tmpDate = datetime(y, m, getDayOfMonth(y, m, startDate.day))
                L.append(tmpDate)

        else:
            for i in range(0, nbMonths, 1):
                y, m = calendar.nextmonth(tmpDate.year, tmpDate.month)
                tmpDate = datetime(y, m, getDayOfMonth(y, m, startDate.day))
                L.append(tmpDate)
        return L

Exécution de la fonction:

>>> getNMonthsLessOrMore(datetime.now(), -6)
[datetime.datetime(2019, 12, 23, 0, 0), datetime.datetime(2019, 11, 23, 0, 0), datetime.datetime(2019, 10, 23, 0, 0), datetime.datetime(2019, 9, 23, 0, 0), datetime.datetime(2019, 8, 23, 0, 0), datetime.datetime(2019, 7, 23, 0, 0)]
>>> getNMonthsLessOrMore(datetime.now(), 8)
[datetime.datetime(2020, 2, 23, 0, 0), datetime.datetime(2020, 3, 23, 0, 0), datetime.datetime(2020, 4, 23, 0, 0), datetime.datetime(2020, 5, 23, 0, 0), datetime.datetime(2020, 6, 23, 0, 0), datetime.datetime(2020, 7, 23, 0, 0), datetime.datetime(2020, 8, 23, 0, 0), datetime.datetime(2020, 9, 23, 0, 0)]
>>> getNMonthsLessOrMore(datetime.now(), 0)
[datetime.datetime(2020, 1, 23, 7, 31, 43, 913244)]

Python: Ajouter/Mettre à jour facilement des données à un dictionnaire

En Python, l'objet dict est très utile pour indexer du contenu avec une clé.

Pour ajouter des données à un dictionnaire, la méthode est très simple.

>>> d = dict()
>>> d['a'] = 1
>>> d
{'a': 1}

Ajout de la valeur '1' au dictionnaire 'd' avec la lettre 'a' comme clé.

Pour mettre à jour la valeur de la clé 'a':

>>> d['a'] = 2
>>> d
{'a': 2}

Il est également possible d'indexer une liste dans un dictionnaire:

>>> d['l'] = list()
>>> d
{'a': 2, 'l': []}

et pour ajouter des données à ma liste:

>>> type(d['l'])
<class 'list'>
>>> d['l'].append(1)
>>> d
{'a': 2, 'l': [1]}

d['l'] étant une liste, je peux utliser la méthode append de la liste pour y ajouter des données

mais avant de pouvoir ajouter des données à une liste, il faut s'assurer que la clé existe, sinon une erreur est renvoyée

>>> d['ll'].append(1)
Traceback (most recent call last):
  File "<pyshell#12>", line 1, in <module>
    d['ll'].append(1)
KeyError: 'll'

C'est pour cette raison que j'utilise la fonction suivante pour la gestion de mes dictionnaires.
Je ne me soucie plus de savoir si la clé existe ou pas.
Tout est controlé dans la fonction.

>>> def AddValueToDict(k, d, v, i):
    # k = key - d = dict - v = value - i = type value
    # si le dictionnaire 'd' contient la clé 'k'
    # on récupère la valeur
    if k in d: i = d[k]
    # détermination du type de la valeur
    # si la valeur est de type set()
    if   isinstance(i, set):   i.add(v)
    # si la valeur est de type list()
    elif isinstance(i, list):  i.append(v)
    # si la valeur est de type str()
    elif isinstance(i, str):   i += str(v)
    # si la valeur est de type int()
    elif isinstance(i, int):   i += int(v)
    # si la valeur est de type float()
    elif isinstance(i, float): i += float(v)
    # on met à jour l'objet 'i' pour la clé 'k' dans le dictionnaire 'd'
    d[k] = i
    # on retourne le dictionnaire 'd'
    return d

>>> d
{'a': 2, 'l': [1]}
>>> # Je veux ajouter au dictionnaire 'd'
>>> # la clé 'll' contenant la valeur '33' 
>>> # dans un objet de type list()
>>> d = AddValueToDict('ll', d, '33', list())
>>> d
{'a': 2, 'll': ['33'], 'l': [1]}
>>> # Ajout de la valeur 'aa' dans un objet de type list() pour la clé 'l'
>>> d = AddValueToDict('l', d, 'aa', list())
>>> d
{'a': 2, 'll': ['33'], 'l': [1, 'aa']}
>>> # Ajout de la valeur 'x' dans un objet de type set() pour la clé 's'
>>> d = AddValueToDict('s', d, 'x', set())
>>> d
{'a': 2, 'll': ['33'], 'l': [1, 'aa'], 's': {'x'}}
>>> # Ajout de la valeur 'x' dans un objet de type str() pour la clé 'a'
>>> d = AddValueToDict('a', d, ';x', str())
>>> d
{'a': 2, 'll': ['33'], 'l': [1, 'aa'], 's': {'x'}}
>>> d = AddValueToDict('a', d, 3, int())
>>> d
{'a': 5, 'll': ['33'], 'l': [1, 'aa'], 's': {'x'}}
>>> d = AddValueToDict('b', d, 'bb', str())
>>> d
{'a': 5, 'll': ['33'], 'b': 'bb', 'l': [1, 'aa'], 's': {'x'}}
>>> d = AddValueToDict('b', d, ';cc', str())
>>> d
{'a': 5, 'll': ['33'], 'b': 'bb;cc', 'l': [1, 'aa'], 's': {'x'}}

Dans le cas où la clé est inexistante dans le dictionnaire, elle est automatiquement créée avec la valeur 'v' dans un objet de type 'i'.
Si le type 'i' de la valeur 'v' est un set() ou une list(), la valeur est automatiquement ajoutée.
Si le type 'i' de la valeur 'v' est une chaine de texte str(), elle est concaténée à celle déjà existante.
Si le type 'i' de la valeur 'v' est un entier int() ou un float float(), elle est ajoutée à celle déjà existante.

Qu'en pensez-vous ?
Super simple !

Etiquettes: 

Python: Analyser des données avec Pandas et Matplotlib

Pandas et Matplotlib sont deux modules Python qui permettent d'analyser des données et de les représenter sous forme de graphiques.

Ce sont deux modules très complets et par conséquent très complexes.

Je vais présenter ici une analyse complète regroupant différentes fonctions utiles permettant de retourner le résultat souhaité.

Pour l'exemple, j'ai décidé d'analyser la consommation de carburant de ma Golf VII Hybride.

Mon analyse va porter sur les 16 dernières fois où je me suis rendu à la station service.

Voici les données brutes que je vais utiliser.
A l'origine mes données sont stockées dans un fichier XML.

<ope date="737309" amount="-29.449999999999999" category="185" wording="v=20.04 d=36773"/>

Ci-dessous une représentation sous forme de tableau.
Pour chaque date (ordinal), il y a le montant et une colonne contenant le volume d'essence acheté ainsi que le kilométrage du compteur au moment du plein.

 

  date amount wording
0 737309 -29.449999999999999 v=20.04 d=36773
1 737317 -43.170000000000002 v=30.02 d=37269
2 737333 -43.100000000000001 v=30.12 d=38061
3 737348 -44.240000000000002 v=31.31 d=38850
4 737358 -50.710000000000001 v=36.48 d=39632
5 737374 -50.149999999999999 v=34.16 d=40408
6 737387 -56.600000000000001 v=37.86 d=41032
7 737398 -37.369999999999997 v=25.49 d=41548
8 737403 -32.289999999999999 v=21.54 d=41880
9 737414 -49.340000000000003 v=32.57 d=42469
10 737427 -50.920000000000002 v=33.52 d=43104
11 737441 -53.57 v=34.90 d=43815
12 737455 -30.199999999999999 v=20.70 d=44353
13 737469 -39.240000000000002 v=27.08 d=44737
14 737484 -49.450000000000003 v=33.30 d=45403
15 737500 -29.850000000000001 v=22.98 d=45940

Voici donc mon script Python, complet, entièrement détaillé à l'aide des commentaires et le résultat final à la fin de cet article.

Ce script a été développé avec Python 3.7.5 et peut être exécuté avec JupyterLab

Je commence par importer les différents modules dont j'ai besoin.
Pour l'installation des modules nécessaires, tout est expliqué ici.

from pathlib import Path
import pandas as pd
from matplotlib import pyplot as plt
from collections import defaultdict
from datetime import datetime as dt
from bs4 import BeautifulSoup as bs
import numpy as np
from IPython.core.display import HTML
from scipy import stats
# La ligne ci-dessous permet d'afficher les graphiques dans jupyterlab
%matplotlib inline

Différentes fonctions qui vont m'être utiles pour transformer les données

def toDICT(KEYS, OPES, DICT):
    for OPE in OPES:
        for KEY in KEYS:
            DICT[KEY].append(OPE.get(KEY))
    return DICT

def toDF(DICT):
    # Je créé le dataframe Pandas à partir des données du dictionnaire DICT
    DF = pd.DataFrame(data=DICT)
    # Je converti la date (ordinal) au format datetime à l'aide de la fonction "convDATE"
    DF['date'] = DF['date'].apply(convDATE)
    # Je converti le montant en "float" et je récupère la valeur absolue
    DF['amount'] = DF['amount'].apply(float).apply(abs)
    # A l'aide de la fonction "getVolDist" je récupère le volume d'essence dans le texte de la colonne "wording"
    # Pour rappel, le texte contenu dans le champ "wording": v=25.49 d=41548
    # Le volume d'essence est converti en "float" à l'aide du module numpy
    DF['vol'] = DF['wording'].apply(getVolDist, args=['v']).apply(np.float32)
    # Le volume d'essence est arrondi à deux chiffres après la virgule
    DF['vol'] = DF['vol'].apply(lambda x: round(x, 2))
    # A l'aide de la fonction "getVolDist" je récupère le kilométrage dans le texte de la colonne "wording"
    # Le kilométrage est converti en "int" à l'aide du module numpy
    DF['dist'] = DF['wording'].apply(getVolDist, args=['d']).apply(np.int32)
    # Je supprime la colonne "wording"
    DF.drop(columns=['wording'], inplace=True)
    # Je trie mon dataframe par date croissante
    DF.sort_values('date', inplace=True)
    # A l'aide de la fonction "diff", appliquée sur la colonne "dist", je calcul la différence de kilométrage entre date et date+1
    DF['kms'] = DF['dist'].diff(periods=-1)
    # Je conserve la valeur absolue pour la colonne "kms"
    DF['kms'] = DF['kms'].apply(abs)
    # Je calcul le prix au litre arrondi à trois chiffres après la virgule
    DF['px/l'] = round(DF['amount'] / DF['vol'], 3)
    # Je calcul le cumul des kilomètres à l'aide la fonction "cumsum" appliquée sur la colonne "kms"
    DF['cum kms'] = DF['kms'].cumsum()
    # Je calcul le cumul du volume à l'aide la fonction "cumsum" appliquée sur la colonne "vol"
    DF['cum vol'] = DF['vol'].cumsum()
    # Je calcul la consommation arrondi à deux chiffres après la virgule
    DF['conso'] = round(DF['cum vol'] * 100 / DF['cum kms'], 2)
    # Je remplace toutes les valeurs nulles par le chiffre 0
    DF.fillna(value=0, inplace=True)
    return DF

def convDATE(i):
    return dt.fromordinal(int(i))

def getVolDist(s, k):
    s = s.split(' ')
    d = {x.split('=')[0]: x.split('=')[1] for x in s}
    return d.get(k)

Je récupère les donées au format XML et j'y applique les différentes transformation dont j'ai besoin.

# A l'aide du module Path, j'initialise mon fichier XML
FILE = Path('operations.xml')
# A l'aide du module BeautifulSoup, je parse mon fichier XML
XML = bs(FILE.read_text(), features='xml')
# Je recherche ensuite toutes les balises "ope" ayant un attribut "category" égal à 185
# Ceci est un exemple de mon fichier XML
# <ope date="737309" amount="-29.449999999999999" category="185" wording="v=20.04 d=36773"/>
# OPES est une liste qui contiendra toutes les balises recherchées
OPES = XML.findAll(name='ope', attrs={'category': '185'})
# J'initialise une liste avec le nom des colonnes que je souhaite avoir
KEYS = ['date','amount','vol','dist','wording']
# J'initialise un dictionnaire Python dont les valeurs seront par défaut des listes
DICT = defaultdict(list)
# J'exécute la fonction "toDict" qui va ajouter à mon dictionnaire "DICT" toutes les valeurs extraites dans le XML
DICT = toDICT(KEYS, OPES, DICT)
# J'exécute la fonction "toDF" pour créer un dataframe Pandas
DF = toDF(DICT)
# J'affiche le dataframe
HTML(DF.to_html())
  date amount vol dist kms px/l cum kms cum vol conso
0 2019-09-07 29.45 20.04 36773 496.0 1.470 496.0 20.04 4.04
1 2019-09-15 43.17 30.02 37269 792.0 1.438 1288.0 50.06 3.89
2 2019-10-01 43.10 30.12 38061 789.0 1.431 2077.0 80.18 3.86
3 2019-10-16 44.24 31.31 38850 782.0 1.413 2859.0 111.49 3.90
4 2019-10-26 50.71 36.48 39632 776.0 1.390 3635.0 147.97 4.07
5 2019-11-11 50.15 34.16 40408 624.0 1.468 4259.0 182.13 4.28
6 2019-11-24 56.60 37.86 41032 516.0 1.495 4775.0 219.99 4.61
7 2019-12-05 37.37 25.49 41548 332.0 1.466 5107.0 245.48 4.81
8 2019-12-10 32.29 21.54 41880 589.0 1.499 5696.0 267.02 4.69
9 2019-12-21 49.34 32.57 42469 635.0 1.515 6331.0 299.59 4.73
10 2020-01-03 50.92 33.52 43104 711.0 1.519 7042.0 333.11 4.73
11 2020-01-17 53.57 34.90 43815 538.0 1.535 7580.0 368.01 4.86
12 2020-01-31 30.20 20.70 44353 384.0 1.459 7964.0 388.71 4.88
13 2020-02-14 39.24 27.08 44737 666.0 1.449 8630.0 415.79 4.82
14 2020-02-29 49.45 33.30 45403 537.0 1.485 9167.0 449.09 4.90
15 2020-03-16 29.85 22.98 45940 0.0 1.299 0.0 472.07 0.00

Mes données sont enfin bien formatées.

Passons ensuite au graphique...

# J'initialise mon graphique
fig, axe = plt.subplots(figsize=(10.6,8))
# Je définis mon axe "x" avec les valeurs de la colonne "date" exceptée la dernière valeur
x = DF.iloc[:-1]['date'].dt.strftime('%Y-%m-%d')
# Je définis mon axe "y" avec les valeurs de la colonne "conso" exceptée la dernière valeur
y = DF.iloc[:-1]['conso']
# Je créé un graphique de type barres et de couleurs bleues 
axe.bar(x, y, label='Consommation', color='#1B80EA')
# J'ajoute un label à l'axe "y"
axe.set_ylabel('Consommation')
# J'ajoute un label à l'axe "x"
axe.set_xlabel('Période')
# Je définis la limite minimum de l'axe "y" en prenant en compte la valeur entière minimum de ma consommation
axe.set_ylim(int(DF.iloc[:-1]['conso'].min()))
# J'ajoute un titre au graphique en y indiquant la consommation moyenne calculée à l'aide de la fonction "mean" appliquée sur la colonne "conso" et arrondi à deux chiffres après la virgule 
axe.set_title('Golf VII: Conso moyenne: {} l/100kms'.format(round(DF.iloc[:-1].conso.mean(), 2)))
# Je formate automatiquement l'affichage des dates pour l'axe "x"
fig.autofmt_xdate()
# A partir d'ici, je créé un courbe de tendance sur la consommation
# Je définis un axe "x" temporaire contenant uniquement les valeurs de l'index. De 0 à 14
_x = DF.iloc[:-1].index
# A l'aide de la fonction "linregress" du module "scipy", je calcul la tendance de la consommation
lr = stats.linregress(_x, y)
# J'ajoute au graphique, sur les mêmes axes "x" & "y" la courbe de tendance de couleur rouge
axe.plot(x, lr.intercept + lr.slope * _x, marker='.', color='r', label='Tendance Consommation')
# Je positionne la légende dans le coin supérieur gauche
axe.legend(loc='upper left')
# Je créé un nouvel axe "y" 
ax2 = axe.twinx()
# Je définis mon nouvel axe "y" avec les valeurs de la colonne "px/l" exceptée la dernière valeur
y = DF.iloc[:-1]['px/l']
# Je crée un graphique de type ligne et de couleur or
ax2.plot(x, y, color='#C7A986', marker='o', label='px/l')
# J'ajoute un label au second axe "y"
ax2.set_ylabel('Prix au litre')
# Je positionne la légende en bas à droite
ax2.legend(loc='lower right')
# J'affiche le graphique
plt.show()

Je vais maintenant faire une synthèse de mes données.
Je souhaite avoir une représentation de ma consommation à chaque fin de mois.

# La fonction "resample" appliquée sur un dataframe permet de redéfinir les données
# Je souhaite regrouper mes données pour chaque fin de mois "M" appliqué sur la colonne "date
R = DF.resample('M',on='date')
# Je créé un nouveau dataframe à partir de ma synthèse et en agrégeant les données 
DF2 = R.agg({'amount':sum,'vol':sum,'dist':max,'kms':sum,'px/l':np.mean,'cum kms':max,'cum vol':max,'conso':np.mean})
# Je formate les différents nombres
DF2['conso'] = DF2['conso'].apply(lambda x: round(x, 2))
DF2['px/l'] = DF2['px/l'].apply(lambda x: round(x, 3))
DF2['kms'] = DF2['kms'].apply(int)
DF2['cum kms'] = DF2['cum kms'].apply(int)
# J'affiche le dataframe
HTML(DF2.to_html())
  amount vol dist kms px/l cum kms cum vol conso
date                
2019-09-30 72.62 50.06 37269 1288 1.454 1288 50.06 3.96
2019-10-31 138.05 97.91 39632 2347 1.411 3635 147.97 3.94
2019-11-30 106.75 72.02 41032 1140 1.482 4775 219.99 4.45
2019-12-31 119.00 79.60 42469 1556 1.493 6331 299.59 4.74
2020-01-31 134.69 89.12 44353 1633 1.504 7964 388.71 4.82
2020-02-29 88.69 60.38 45403 1203 1.467 9167 449.09 4.86
2020-03-31 29.85 22.98 45940 0 1.299 0 472.07 0.00

Intéressant comme synthèse.
Les dates de fin de mois ont été automatiquement calculées.

Et maintenant, le petit graphique qui va bien.

fig, axe = plt.subplots(figsize=(10.6,8))
x = DF2.iloc[:-1].index.strftime('%Y-%m-%d')
y = DF2.iloc[:-1]['conso']
axe.bar(x, y, label='conso', color='#1B80EA')
axe.set_ylabel('Consommation')
axe.set_xlabel('Période')
axe.set_ylim(int(y.min()))
_x = range(len(DF2.iloc[:-1].index))
lr = stats.linregress(_x, y)
axe.plot(x, lr.intercept + lr.slope * _x, marker='.', color='r', label='Tendance Consommation')
axe.legend(loc='upper left')
axe.set_title('Golf VII: Conso moyenne: {} l/100kms'.format(round(y.mean(), 2)))
fig.autofmt_xdate()
ax2 = axe.twinx()
y = DF2.iloc[:-1]['px/l']
ax2.plot(x, y, color='#C7A986', marker='o', label='px/l')
ax2.set_ylabel('Prix au litre')
ax2.legend(loc='lower right')
fig.savefig(FILE.parent / 'Analyse Conso Fin De Mois.png', format='png')
plt.show()

Vraiment bluffant Pandas et Matplotlib

Les dataframes Pandas offre tout un tas de possibilités qu'il est impossible de résumé en un seul article.

Les pages de documentation des projets sont très bien fournies.

Python: Arrêter un script proprement

J'utilise de temps en temps des scripts Python qui sont exécutés en tâche de fond sur mon système.

Le principe, une fonction main contenant le code à exécuter et une boucle while infinie qui exécute la fonction main.

#!python3
# -*- coding: UTF-8 -*-
import time

def main():
    ....

if __name__ == '__main__':
    while True:
        main()
        time.sleep(3)

Toutes les 3 sec, la fonction main est exécutée.

Le seul moyen d'arrêter le script, ctrl+c dans la console sans savoir où le script en est vraiement, au risque de l'arrêter pendant l'écriture de données dans un fichier et de corrompre ledit fichier.

Petite astuce, à mettre dans tous ses scripts exécutés en tâche de fond:

#!python3
# -*- coding: UTF-8 -*-
import time
from pathlib import Path
import sys

def main():
    ....

if __name__ == '__main__':
    while True:
        main()
        if Path('.kill').exists() and Path('.kill').read_text() == Path(sys.argv[0]).name:
            break
        time.sleep(3)
    sys.exit(0)

L'astuce consiste à utiliser un fichier .kill, dans le même répertoire que le script Python, et d'y écrire le nom exact du script concerné.

Python: Calculer la somme de contrôle d'un fichier

Calculer la somme MD5/SHA256/SHA512 de contrôle d'un fichier:

>>> import hashlib
>>> hashlib.md5(open('.bashrc','rb').read()).hexdigest()
'fc0db75cc50c25e8984fa2a958dac042'
>>> hashlib.sha256(open('.bashrc','rb').read()).hexdigest()
'2a684f93356c0cb229d6ee2e464d52e21f6dfbdafd3eef4d44d2f80d66bc20a1'
>>> hashlib.sha512(open('.bashrc','rb').read()).hexdigest()
'e5754975dad2512f024401d7f542a86b266dae7e301ff1f0a19c391e3494b1556544ed2de465da903c9978d7848e8e07fbcd32b03a2b47abbef96b80f4d30a52'
>>>

 

Python: Calculer le nombre PI

Le nombre π (3,1415.........), peut être calculé à l'aide de la formule suivante:

π = (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) etc etc ...

On démarre les opérations avec le numérateur "4", que l'on conserve jusqu'à la fin, et le dénominateur 1 que l'on incrémente de 2 à chaque opération en alternant soustraction et addition.

Plus le nombre d'itération est important, plus la précision sera importante.

Une petite boucle en Python permet de calculer π avec une bonne précision.

Dans l'exemple suivant, je vais boucler 100 millions de fois afin d'avoir une valeur de π avec 27 chiffres après la virgule, mais comme le souligne Matt, avec une précision de seulement 7 chiffres après la virgule, et cela en quelques minutes seulement.

>>> from decimal import Decimal
>>> P = 100000000 # attention, nombre pair obligatoire (voir commentaire)
>>> C = Decimal(4) / 1
>>> I = 1
>>> for i in range(P, 0, -1):
    z = Decimal(4) / (I+2)
    if i%2 == 0:
        z = Decimal.copy_negate(z)
    C += z
    I += 2

 >>> print(C)
3.141592663589793138462645118

Mais il est quand même plus rapide d'utiliser la fonction suivante:

>>> from math import pi
>>> print(pi)
3.141592653589793

C'était juste pour le fun

Etiquettes: 

Python: Calculer les classes ABC grâce à Pandas

Pandas propose une fonction qui permet de calculer très facilement les classes ABC.
Cette fonction se nomme cut

>>> import pandas as pd
>>> import numpy as np

Pour générer un dataframe avec des données aléatoires, voici une petite astuce:

>>> import random
>>> random.seed()
>>> df = pd.DataFrame(np.random.randint(0,1000,100), index=random.sample(range(100000, 999999), 100), columns=['stocks'])

Ca permet de générer un dataframe de 100 lignes avec une colonne "stocks" et contenant des valeurs comprises entre 0 et 1000 et en index, une liste d'articles uniques compris entre 100000 et 999999.

Exemple avec le dataframe suivant:

>>> df
             stocks
761920     965
636899     147
835336     324
366511     536
852098     544
        ...
170837     319
886380      98
676491     201
639583     233
854389     292

[100 rows x 1 columns]

En index, les références de mes articles et les quantités en stock dans la colonne 'stocks'.

J'ai au total 100 articles dans mon dataframe.

Premièrement, trier les données par stocks décroissant:

>>> df = df.sort_values('stocks', ascending=False)
>>> df
              stocks
506289     979
549641     977
351719     967
761920     965
874506     962
        ...
332012      67
797080      41
347642      38
417762      28
284562      13
[100 rows x 1 columns]

Ensuite, calculer le cumul des stocks:

>>> df['cumulStocks'] = df['stocks'].cumsum()
>>> df
              stocks  cumulStocks
506289     979          979
549641     977         1956
351719     967         2923
761920     965         3888
874506     962         4850
        ...          ...
332012      67        49118
797080      41        49159
347642      38        49197
417762      28        49225
284562      13        49238
[100 rows x 2 columns]

Après, il faut calculer le pourcentage du cumul du stock par rapport à la somme totale du stock:

>>> df['%cumulStocks'] = round(100 * df['cumulStocks'] / df['stocks'].sum(), 3)
>>> df
              stocks  cumulStocks  %cumulStocks
506289     979          979         1.988
549641     977         1956         3.973
351719     967         2923         5.936
761920     965         3888         7.896
874506     962         4850         9.850
        ...          ...           ...
332012      67        49118        99.756
797080      41        49159        99.840
347642      38        49197        99.917
417762      28        49225        99.974
284562      13        49238       100.000
[100 rows x 3 columns]

Nous ajoutons une colonne afin d'indiquer le rang des articles:

>>> df['rang'] = df.rank(ascending=False)
>>> df
               stocks  cumulStocks  %cumulStocks  rang
506289     979          979         1.988     1.0
549641     977         1956         3.973     2.0
351719     967         2923         5.936     3.0
761920     965         3888         7.896     4.0
874506     962         4850         9.850     5.0
        ...          ...           ...   ...
332012      67        49118        99.756    96.0
797080      41        49159        99.840    97.0
347642      38        49197        99.917    98.0
417762      28        49225        99.974    99.0
284562      13        49238       100.000   100.0
[100 rows x 4 columns]

Pour finir, calculons les classes ABC avec les pourcentages suivants:
Je veux que ma classe A concerne 80% des stocks cumulés, ma classe B les 15% suivants et ma classe C les 5 % restants.

>>> classes = ['A', 'B', 'C']
>>> pourcent = [0, 80, 95, 100]
>>> df['classe'] = pd.cut(df['%cumulStocks'], bins=pourcent, labels=classes)
>>> df
        stocks  cumulStocks  %cumulStocks  rang classe
506289     979          979         1.988     1.0      A
549641     977         1956         3.973     2.0      A
351719     967         2923         5.936     3.0      A
761920     965         3888         7.896     4.0      A
874506     962         4850         9.850     5.0      A
        ...          ...           ...   ...    ...
332012      67        49118        99.756    96.0      C
797080      41        49159        99.840    97.0      C
347642      38        49197        99.917    98.0      C
417762      28        49225        99.974    99.0      C
284562      13        49238       100.000   100.0      C
[100 rows x 5 columns]

Je me retrouve avec la répartition suivante, 56 articles en classe A, 22 articles en classe B et 22 articles en classe C

>>> df['classe'].value_counts(sort=False)
A    56
B    22
C    22
Name: classe, dtype: int64

La classe A contient bien tous les articles dont le pourcentage du stock cumulé est inférieur ou égal à 80%

>>> df[df['classe']=='A']['%cumulStocks'].describe()[['min','max']]
min       1.988000
max      79.776000
Name: %cumulStocks, dtype: float64

La classe B contient tous les articles supérieur à 80% et inférieur ou égal à 95%

>>> df[df['classe']=='B']['%cumulStocks'].describe()[['min','max']]
min      80.584000
max      94.549000
Name: %cumulStocks, dtype: float64

Enfin, la classe C contient tous les articles supérieur à 95% et inférieur ou égal à 100%

>>> df[df['classe']=='C']['%cumulStocks'].describe()[['min','max']]
min       95.022000
max      100.000000
Name: %cumulStocks, dtype: float64

Ajoutons une colonne indiquant le pourcentage du rang:

>>> df['%rang'] = round(100 * df['rang'] / len(df), 3)
>>> df
        stocks  cumulStocks  %cumulStocks  rang classe  %rang
506289     979          979         1.988     1.0      A    1.0
549641     977         1956         3.973     2.0      A    2.0
351719     967         2923         5.936     3.0      A    3.0
761920     965         3888         7.896     4.0      A    4.0
874506     962         4850         9.850     5.0      A    5.0
        ...          ...           ...   ...    ...    ...
332012      67        49118        99.756    96.0      C   96.0
797080      41        49159        99.840    97.0      C   97.0
347642      38        49197        99.917    98.0      C   98.0
417762      28        49225        99.974    99.0      C   99.0
284562      13        49238       100.000   100.0      C  100.0
[100 rows x 6 columns]

Vérifions maintenant si Pareto a toujours raison, à savoir 20% de mes articles doivent représenter 80% de mon stock (plus haut, la répartition indiquait 56 articles dans la classe A, par conséquent 56% puisque j'ai un échantillon de 100 articles):

>>> df[df['classe']=='A'].describe().loc['max',['%cumulStocks','%rang']]
%cumulStocks    79.776
%rang           56.000
Name: max, dtype: float64

On voit bien que 80% de mon stock est constitué par 56% de mes articles.

Sacré Pareto...

Python: Calculer les nombres premiers

class LowerThanTwo(Exception):
    pass

def isPrime(num):
    try:
        num = int(num)
        if num < 2: raise LowerThanTwo()
    except ValueError:
        return False, "Saisir un nombre valide !!!"
    except LowerThanTwo:
        return False, "Saisir un nombre supérieur égal à 2 !!!"
    else:
        if num > 2 and num % 2 == 0:
            return False, "{:>5d} n'est pas un nombre premier!".format(num)
        for x in range(2, num // 2):
            if num % x == 0:
                return False, "{:>5d} n'est pas un nombre premier!".format(num)
        return True, "{:>5d} est un nombre premier!".format(num)

Exécution de la fonction:

for x in range(100):
    a, b = isPrime(x)
    if a: print(b)

  2 est un nombre premier!
  3 est un nombre premier!
  5 est un nombre premier!
  7 est un nombre premier!
 11 est un nombre premier!
 13 est un nombre premier!
 17 est un nombre premier!
 19 est un nombre premier!
 23 est un nombre premier!
 29 est un nombre premier!
 31 est un nombre premier!
 37 est un nombre premier!
 41 est un nombre premier!
 43 est un nombre premier!
 47 est un nombre premier!
 53 est un nombre premier!
 59 est un nombre premier!
 61 est un nombre premier!
 67 est un nombre premier!
 71 est un nombre premier!
 73 est un nombre premier!
 79 est un nombre premier!
 83 est un nombre premier!
 89 est un nombre premier!
 97 est un nombre premier!

Un autre possibilité, consiste à utiliser une regex.

Astuce trouvée sur http://montreal.pm.org/tech/neil_kandalgaonkar.shtml?s=09

La négation de cette regex permet de savoir si le nombre est premier.

import re
r1 = re.compile(r'^1?$|^(11+?)\1+$')
not r1.match("1"*9) # False car 9 n'est pas un nombre premier
not r1.match("1"*13) # True car 13 est un nombre premier
not r1.match("1"*32002) # False car 32002 n'est pas un nombre premier
not r1.match("1"*32003) # True car 32003 est un nombre premier

Intéressant comme regex

Python: Choisir une date aléatoire ...

... entre le 01/01/1970 et aujourd'hui.

>>> import time
>>> import random
>>> time.strftime('%d/%m/%Y', time.gmtime(random.randint(0, int(time.time()))))
'20/09/1992'
>>> time.strftime('%d/%m/%Y', time.gmtime(random.randint(0, int(time.time()))))
'18/11/2007'

Ca peut toujours servir

Etiquettes: 

Python: Comment trouver dans une liste de nombres ceux dont la somme est égale à nombre défini

Comme indiqué dans le titre, j'ai une liste de nombre, par exemple 5, 10, 25, 30, 45, 60 et j'aimerais savoir quels sont ceux, quand on les additionne, qui me donne le résultat 35 par exemple.

Avec ces nombres, facile, il y a 10 + 25 mais également 5 + 30, mais quand on a une liste de nombres comme celle-ci 8.42, 6.94, 5.40, 1.77, 4.32, 4.26, 3.49, 2.33, 3.90, 1.09 (avec des décimales, c'est plus fun) et que je veux que la somme soit égale à 29.44, c'est un peu plus compliqué.

J'ai imaginé cette fonction en Python qui me permet de trouver le résultat.
Bien évidemment, plus il y a de nombres dans la liste de départ, plus le temps de calcul est long.
Pour m'aider, j'utilise les classes permutations et accumulate du module itertools ainsi que la classe Decimal du module decimal.

Peut-être auriez vous des idées d'amélioration ?

Voici la fonction.
Toutes les lignes sont commentées pour une meilleure compréhension.

from itertools import permutations
from itertools import accumulate
from decimal import Decimal


def trouve_les_arguments_de_la_somme():
    t = Decimal('29.44') # la somme à trouver
    l = [8.42, 6.94, 5.40, 1.77, 4.32, 4.26, 3.49, 2.33, 3.90, 1.09] # la liste des arguments, 10 au total
    l = [Decimal(str(x)) for x in l] # je convertis les arguments en objet "Decimal"
    l = [x for x in l if x <= t] # je conserve uniquement les valeurs inférieures ou égales à la somme à trouver
    z = permutations(l, len(l)) # j'utilise la classe permutations afin de générer toutes les combinaisons possible, 3 628 800 dans mon cas [factorial(10)]
    se = set() # je créé un set afin d'y stocker toutes les solutions possible
        for x in z: # je parcours toutes les combinaisons possible
            s = list(accumulate(x)) # pour chaque combinaison, je calcul la somme cumulée à l'aide de la classe accumulate
            if t in s: # si ma somme à trouver est dans la liste des sommes cumulées
                i = s.index(t) # je recherche la position de ma somme dans la liste
                se.add(tuple(sorted(x[:i + 1]))) # je récupère dans ma combinaison tous les arguments concernés grâce à mon index i et je les trie par ordre croissant avant de les ajouter à mon set
    for i in se:
            print(str(i)) # j'affiche les résultats

(Decimal('1.09'), Decimal('1.77'), Decimal('2.33'), Decimal('3.49'), Decimal('5.4'), Decimal('6.94'), Decimal('8.42'))

Dans mon cas, je n'ai qu'une seule solution possible (trouvée en 10 sec max).

Python: Comparaison de dossiers

Comparer le contenu de fichiers communs à deux dossiers (par exemple pour comparer les sources JAVA entre un environnement de DEV et un environnement de PROD) est relativement simple avec Python et le module Path.

La classe Path du module pathlib est indispensable pour tout ce qui touche au système de fichiers.
Il contient tout un tas d'outils très simple d'utilisation et qui facilite grandement toutes les opérations à faire.

J'utilise également la classe md5 du module hashlib afin de calculer la somme md5 de chaque fichier.

Import de la classe Path du module pathlib et de la classe md5 du module hashlib

>>> from pathlib import Path
>>> from hashlib import md5

Initialisation des deux dossiers à comparer.

>>> D1 = Path(r'd:\dossier1')
>>> D2 = Path(r'd:\dossier2')

A l'aide de la méthode rglob, je parcours récursivement tous les fichiers JAVA présents dans le dossier.
La méthode relative_to permet d'obtenir le chemin du fichier relatif à la racine (passée en paramètre).
La méthode exists permet de tester l'existence du fichier.
La méthode read_bytes permet d'accéder directement au contenu du fichier en mode binaire.
La méthode hexdigest de la classe md5 permet de calculer en hexadécimal la somme md5 du contenu du fichier.

>>> for file in D1.rglob('*.java'):
    file2 = D2/ file.relative_to(D1)
    if file2.exists():
        if md5(file.read_bytes()).hexdigest() != md5(file2.read_bytes()).hexdigest():
            print(f'Le fichier {file.relative_to(D1)} est différent entre {D1.as_posix()} et {D2.as_posix()}')
    else:
        print(f'Le fichier {file.relative_to(D1)} n\'existe pas dans {D2.as_posix()}')

infoUtilisation des f-strings pour l'affichage des messages.

Python: Comparer deux archives ZIP

Comparer deux fichiers archives ZIP afin d'ajouter dans le premier tous les fichiers présents dans le second sans prendre en compte les fichiers communs aux deux fichiers archives ZIP.

from pathlib import Path
from zipfile import ZipFile as zf

DOSSIER = Path('/mondossier')
filename1 = DOSSIER / 'archive1.zip'
# on ouvre la première archive en mode 'append'
zip1 = zf(filename1, mode='a')
# on fait un 'set' de tous les fichiers de la première archive
s1 = set([(x.filename, x.file_size) for x in zip1.filelist])
filename2 = DOSSIER / 'archive2.zip'
# on ouvre la seonde archive en mode 'read'
zip2 = zf(filename2, mode='r')
# on fait un 'set' de tous les fichiers de la seconde archive
s2 = set([(x.filename, x.file_size) for x in zip2.filelist])
# on parcourt tous les fichiers de la seconde archive qui ne sont pas dans la première archive
for f, s in s2 - s1:
    # on écrit dans la première archive le contenu du fichier de la seconde archive
    zip1.writestr(f, zip2.read(f))
zip1.close()
zip2.close()

On peut améliorer le script en vérifiant la taille du fichier (variable 's' dans la boucle 'for') à ajouter dans le cas où un fichier commun aurait été modifié entre les deux archives ZIP.

Python: Comparer le contenu de deux éléments

En Python, il existe un type d'éléments qui permet de faire différentes comparaisons.

Ce type, c'est le set.

Exemples avec les deux éléments suivants:

>>> s1 = {1,2,3,4,5}
>>> s2 = {3,4,5,6,7}

Afficher les éléments présents uniquement dans s1:

>>> s1 - s2
{1, 2}

Afficher les éléments présents uniquement dans s2:

>>> s2 - s1
{6, 7}

Afficher les éléments communs à s1 et à s2:

>>> s1 & s2
{3, 4, 5}

Afficher les éléments présents dans s1 et dans s2 (sans les doublons):

>>> s1 | s2
{1, 2, 3, 4, 5, 6, 7}

Vraiment très pratique pour comparer deux contenus.

Fonctionne également avec des chaines de textes.

Etiquettes: 

Python: Compter le nombre de jours ouvrés dans un mois

Pour calculer le nombre de jours ouvrés dans un mois, je vais utiliser les modules calendar et numpy

>>> import numpy as np
>>> import calendar

La méthode monthcalendar de la classe calendar permet d'obtenir un array avec tous les jours du mois classés par semaine (du lundi au dimanche)

>>> calendar.monthcalendar(2019,10)
[[0, 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, 0, 0, 0]]

Je le convertis en array numpy

>>> a = np.array(calendar.monthcalendar(2019,10))
>>> a
array([[ 0,  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,  0,  0,  0]])

J'affiche uniquement les 5 premiers jours de chaque semaine

>>> a[:,:-2]
array([[ 0,  1,  2,  3,  4],
       [ 7,  8,  9, 10, 11],
       [14, 15, 16, 17, 18],
       [21, 22, 23, 24, 25],
       [28, 29, 30, 31,  0]])

Enfin, je compte le nombre d'éléments supérieurs à 0

>>> len(a[np.where(a[:,:-2] > 0)])
23

Le mois d'octobre de l'année 2019 compte 23 jours ouvrés.

Il est également possible d'utiliser la méthode nonzero de la classe numpy

>>> len(a[np.nonzero(a[:,:5])])
23

Une autre méthode consiste à utiliser le puissant module dateutil

>>> from dateutil.parser import *
>>> from dateutil.rrule import *
>>> from dateutil.relativedelta import *
>>> dtstart = parse('20200601')
>>> len(list(rrule(DAILY, dtstart=dtstart, until=dtstart+relativedelta(months=+1, days=-1), byweekday=[MO, TU, WE, TH, FR])))
22

La classe parser permet de convertir une date d'un format texte à un format datetime.datetime
La classe relativedelta permet d'incrémenter une date ou de calculer l'écart entre deux dates
La classe rrule permet de générer toute une liste de dates suivant différents critères

Python: Connaitre le nom du système et le nom d'hôte de la machine

Exécuter un script Python sur plusieurs machines différentes, que ce soit au niveau du système d'exploitation et/ou de son nom d'hôte, peut engendrer des problèmes concernant certaines tâches à exécuter.

Par exemple, une variable initialisée différement en fonction du système d'exploitation et/ou du nom d'hôte de la machine sur laquelle le script est exécuté.

Les deux modules suivants permettent de résoudre ces problèmes.

Pour le nom du système d'exploitation:

>>> import platform
>>> print(platform.system())

Les différents retours en fonction du système:

  1. Linux > Linux
  2. Mac > Darwin
  3. Windows > Windows

Pour le nom d'hôte de la machine:

>>> import socket
>>> print(socket.gethostname())

 

Python: Convertir des secondes en heures, minutes et secondes

Pour convertir des secondes en heures, minutes et secondes, il existe une librairie Python, dateutil, qui permet de le faire tout simplement.

Pour ce faire, il suffit d'utiliser la méthode relativedelta de la classe dateutil.relativedelta.

Par exemple, pour convertir 16433.9 secondes:

>>> from dateutil.relativedelta import relativedelta
>>> relativedelta(seconds=16433.9)
relativedelta(hours=+4, minutes=+33, seconds=+53.9)
>>>

facile, non !

Python: Convertir un flux JSON en XML

Voici un script Python qui permet de convertir en XML un contenu JSON et/ou un dictionnaire de données Python.

linkToXml.py

Quelques exemples d'utilisations:

En ligne de commande (BASH par exemple) avec un pipe et un flux JSON via CURL:

# curl "http://api.geonames.org/citiesJSON?formatted=true&north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo&style=full" -o - -s | python3 ToXml.py
<?xml version="1.0" ?>
<XML>
        <geonames>
                <name>Mexiko-Stadt</name>
                <countrycode>MX</countrycode>
                <geonameId>3530597</geonameId>
                <toponymName>Mexico City</toponymName>
                <wikipedia>en.wikipedia.org/wiki/Mexico_City</wikipedia>
                <fclName>city, village,...</fclName>
                <fcode>PPLC</fcode>
                <lat>19.428472427036</lat>
                <lng>-99.12766456604</lng>
                <fcl>P</fcl>
                <fcodeName>capital of a political entity</fcodeName>
                <population>12294193</population>
        </geonames>
        <geonames>
                <name>Peking</name>
                <countrycode>CN</countrycode>
                <geonameId>1816670</geonameId>
                <toponymName>Beijing</toponymName>
                <wikipedia>en.wikipedia.org/wiki/Beijing</wikipedia>
                <fclName>city, village,...</fclName>
                <fcode>PPLC</fcode>
                <lat>39.9074977414405</lat>
                <lng>116.397228240967</lng>
                <fcl>P</fcl>
                <fcodeName>capital of a political entity</fcodeName>
                <population>11716620</population>
        </geonames>
</XML>

infoUtilisé avec un pipe, seul un flux JSON est autorisé.

Sinon, en important la classe dans un script Python:

A partir d'un fichier:

# cat flux.json
{
    "un": "one",
    "deux": "two",
    " 3": "three",
    "4": "four"
}

Code Python:

>>> from ToXml import ToXml
>>> toxml = ToXml()
>>> toxml.fromjson('flux.json')
>>> print(toxml.topretty())
<?xml version="1.0" ?>
<XML>
    <XMLNODE v="3">three</XMLNODE>
    <XMLNODE v="4">four</XMLNODE>
    <un>one</un>
    <deux>two</deux>
</XML>

>>>

A partir d'une chaine de texte représentant un contenu JSON:

>>> from ToXml import ToXml
>>> s = '{" 3": "three", "4": "four", "un": "one", "deux": "two"}'
>>> toxml = ToXml()
>>> toxml.fromjsons(s)
>>> print(toxml.topretty())
<?xml version="1.0" ?>
<XML>
    <XMLNODE v="3">three</XMLNODE>
    <XMLNODE v="4">four</XMLNODE>
    <un>one</un>
    <deux>two</deux>
</XML>

>>>

A partir d'un dictionnaire de données:

>>> from ToXml import ToXml
>>> d = {' 3': 'three', '4': 'four', 'un': 'one', 'deux': 'two'}
>>> type(d)
<class 'dict'>
>>> toxml = ToXml()
>>> toxml.fromdict(d)
>>> print(toxml.topretty())
<?xml version="1.0" ?>
<XML>
    <XMLNODE v="3">three</XMLNODE>
    <XMLNODE v="4">four</XMLNODE>
    <un>one</un>
    <deux>two</deux>
</XML>

>>>

Et enfin, une fonction permettant d'enregistrer le contenu XML dans un fichier:

>>> toxml.tofile('flux.xml')
# cat flux.xml
<?xml version="1.0" ?>
<XML>
    <XMLNODE v="3">three</XMLNODE>
    <XMLNODE v="4">four</XMLNODE>
    <un>one</un>
    <deux>two</deux>
</XML>

N'hésitez pas à laisser vos avis.

J'ai essayé de documenter le code au maximum mais au cas où ...

Etiquettes: 

Python: Créer un fichier CSV

#!/usr/bin/env python
# -*-coding: utf-8 -*

entetes = [
     u'Colonne1',
     u'Colonne2',
     u'Colonne3',
     u'Colonne4',
     u'Colonne5'
]

valeurs = [
     [u'Valeur1', u'Valeur2', u'Valeur3', u'Valeur4', u'Valeur5'],
     [u'Valeur6', u'Valeur7', u'Valeur8', u'Valeur9', u'Valeur10'],
     [u'Valeur11', u'Valeur12', u'Valeur13', u'Valeur14', u'Valeur15']
]

f = open('monFichier.csv', 'w')
ligneEntete = ";".join(entetes) + "\n"
f.write(ligneEntete)
for valeur in valeurs:
     ligne = ";".join(valeur) + "\n"
     f.write(ligne)

f.close()

attentionLa fonction open() de Python2 utilise l'encodage du système pour encoder le fichier (contrairement à Python3, il n'est pas possible de préciser l'encodage lors de louverture du fichier).
Pour encoder le fichier dans un encodage différent du système, il est nécessaire d'utiliser la fonction open() de la librairie codecs.

#!/usr/bin/env python
# -*-coding: utf-8 -*

import codecs

f = codecs.open('monFichier.csv', mode='w', encoding='iso-8859-1')
f.write(u'Ecriture réussie.')
f.close()

Ce qui donne:

$ file --mime-encoding monFichier.csv
monFichier.csv: iso-8859-1

link Liste des principaux encodages

Python: Créer un fichier XML à l'aide de BeautifulSoup

Le module BeautifulSoup permet de parser un fichier XML (ou HTML) très facilement mais il peut, tout aussi facilement, créer du contenu XML de toute pièce.

Pour l'exemple je vais utiliser le module faker qui permet de générer des données aléatoires en tout genre.

Installation des modules nécessaires

# python3 -m pip install --upgrade bs4 faker

Import des modules

>>> from bs4 import BeautifulSoup as bs
>>> from faker import Faker

Initialisation du module faker

>>> f = Faker('fr_FR')

Je vais générer des profils aléatoires ressemblant à ceci:

>>> f.profile()
{'job': 'aide-soignant',
 'company': 'Dias',
 'ssn': '318-53-8242',
 'residence': '74, rue Boyer\n04256 Hoarau',
 'current_location': (Decimal('17.293354'), Decimal('165.196114')),
 'blood_group': 'A+',
 'website': ['http://boulay.com/'],
 'username': 'arnaudmonique',
 'name': 'Patrick Valette',
 'sex': 'M',
 'address': '86, boulevard Raymond\n67114 Sainte Honoré',
 'mail': 'nbesnard@bouygtel.fr',
 'birthdate': datetime.date(1944, 10, 31)}

Les données générées sont au format json.
Je vais utiliser les clés comme noms des balises (tags) XML.
Mon fichier XML contiendra une balise (tag) principale "<profiles>" qui contiendra toutes les balises (tag) "<profile>".
La clé "ssn" sera un attribut de la balise (tag) "<profile>".
La valeur de la clé "current_location" sera découpée en deux balises (tags) XML, "<latitude>" et "<longitude>" et ces deux balises (tags) seront intégrées à la balise (tag) "<current_location>".
Toutes les valeurs de la clé "website" auront leurs propres balises (tags) XML "<website>" et elles seront toutes regroupées dans une balise (tag) XML "<websites>".

C'est parti...

Création de mon contenu XML de base

# Création de mon enveloppe XML
>>> xml = bs(features='xml')
# Création de mon tag <profiles>
>>> profiles = xml.new_tag('profiles')
# Ajout du tag <profiles> à l'enveloppe XML
>>> xml.append(profiles)

Création du détail de mon XML

# Une boucle pour créer 10 profils aléatoires
>>> for x in range(10):
    # Génération d'un profil aléatoire
    prof = f.profile()
    # Création de mon tag avec l'attribut "ssn" <profile ssn="xxx-xx-xxxx">
    profile = xml.new_tag('profile', attrs={'ssn':prof.get('ssn')})
    # Suppression de la clé "ssn" (plus besoin)
    prof.pop('ssn')
    # Je parcours toutes les clés de mon profil généré
    for k, c in prof.items():
        # Création du tag correspond à la clé du json <xxxx>
        y = xml.new_tag(k)
        # Si ma clé est "current_location" je découpe la valeur en "latitude" et "longitude"
        if k == 'current_location':
            # Création du tag <latitude>
            z1 = xml.new_tag('latitude')
            # Je renseigne la valeur du tag <latitude> avec la première valeur du tuple "current_location"
            z1.string = str(c[0])
            # Création du tag <longitude>
            z2 = xml.new_tag('longitude')
            # Je renseigne la valeur du tag <longitude> avec la seconde valeur du tuple "current_location"
            z2.string = str(c[1])
            # On ajoute les deux tags <latitude> et <longitude> au tag <current_location>
            y.append(z1)
            y.append(z2)
        # Si ma clé est "website" je détaille toutes les valeurs
        elif k == 'website':
            # je renomme mon tag en <websites>
            y = xml.new_tag('websites')
            # je parcours toutes les valeurs de la clé "website"
            for z in c:
                # Création du tag <website>
                z1 = xml.new_tag('website')
                # Je renseigne la valeur du tag <website>
                z1.string = z
                # J'ajoute mon tag <website> au tag <websites>
                y.append(z1)
        else:
            # Sinon, je renseigne la valeur du tag <xxxx>
            y.string = str(c)
        # J'ajoute le tag <xxxx> au tag <profile>
        profile.append(y)
    # J'ajoute le tag <profile> au tag <profiles>
    profiles.append(profile)
   

Et voilà, mon contenu XML est terminé.
Il ressemble à ceci:

<?xml version="1.0" encoding="utf-8"?>
<profiles>
    <profile ssn="002-43-2880">
        <job>projectionniste</job>
        <company>Rossi</company>
        <residence>rue Gomez
11458 Francoisnec</residence>
        <current_location>
            <latitude>-25.928603</latitude>
            <longitude>169.931343</longitude>
        </current_location>
        <blood_group>B+</blood_group>
        <websites>
            <website>https://www.bonnin.com/</website>
        </websites>
        <username>caronmargaret</username>
        <name>David Joubert</name>
        <sex>M</sex>
        <address>avenue Geneviève Giraud
90573 Martineaudan</address>
        <mail>aubertjacqueline@wanadoo.fr</mail>
        <birthdate>1968-02-29</birthdate>
    </profile>
    <profile ssn="007-06-6107">
        <job>prototypiste en matériaux souples</job>
        <company>Maurice Blin S.A.S.</company>
        <residence>798, rue Françoise Ramos
76458 Saint MargaudBourg</residence>
        <current_location>
            <latitude>41.5629005</latitude>
            <longitude>15.774012</longitude>
        </current_location>
        <blood_group>B+</blood_group>
        <websites>
            <website>https://fischer.com/</website>
            <website>http://daniel.net/</website>
            <website>https://paul.com/</website>
            <website>https://www.costa.com/</website>
        </websites>
        <username>marie50</username>
        <name>Inès Daniel</name>
        <sex>F</sex>
        <address>705, rue Thérèse Collet
50313 Jacques-les-Bains</address>
        <mail>hortensehuet@wanadoo.fr</mail>
        <birthdate>1968-06-15</birthdate>
    </profile>
    <profile ssn="017-32-3783">
        <job>hydraulicien</job>
        <company>Letellier et Fils</company>
        <residence>9, rue Gilles
61321 Sainte Virginie</residence>
        <current_location>
            <latitude>23.7306235</latitude>
            <longitude>-59.305275</longitude>
        </current_location>
        <blood_group>A+</blood_group>
        <websites>
            <website>https://www.chevallier.com/</website>
            <website>https://www.hoareau.net/</website>
        </websites>
        <username>rene93</username>
        <name>Céline Blin</name>
        <sex>F</sex>
        <address>43, avenue Vidal
82960 Teixeira</address>
        <mail>davidmartine@laposte.net</mail>
        <birthdate>1954-05-08</birthdate>
    </profile>
    <profile ssn="023-75-0442">
        <job>électricien installateur installatrice</job>
        <company>Dupuis</company>
        <residence>7, rue Jacob
27949 Saint Yves</residence>
        <current_location>
            <latitude>-36.2609615</latitude>
            <longitude>-40.304003</longitude>
        </current_location>
        <blood_group>O-</blood_group>
        <websites>
            <website>http://www.jean.com/</website>
            <website>https://gregoire.org/</website>
            <website>http://www.payet.org/</website>
            <website>http://www.dumas.org/</website>
        </websites>
        <username>wlegrand</username>
        <name>Thierry Martins</name>
        <sex>M</sex>
        <address>33, boulevard Henri Pierre
00436 Georges</address>
        <mail>dumashelene@wanadoo.fr</mail>
        <birthdate>1966-07-04</birthdate>
    </profile>
    <profile ssn="031-65-0389">
        <job>ingénieur logiciel</job>
        <company>Masse SA</company>
        <residence>boulevard de Perrin
96676 Ferrandnec</residence>
        <current_location>
            <latitude>-27.915336</latitude>
            <longitude>161.219956</longitude>
        </current_location>
        <blood_group>A+</blood_group>
        <websites>
            <website>https://www.rousseau.com/</website>
            <website>http://lecomte.com/</website>
            <website>http://lopez.fr/</website>
        </websites>
        <username>gabriel61</username>
        <name>Patrick Delattre</name>
        <sex>M</sex>
        <address>4, rue de Levy
46800 Hamon-sur-Laporte</address>
        <mail>aliceleveque@dbmail.com</mail>
        <birthdate>1943-08-04</birthdate>
    </profile>
    <profile ssn="042-20-4813">
        <job>télévendeur</job>
        <company>Peltier Lacombe S.A.R.L.</company>
        <residence>5, avenue Briand
68000 Coste</residence>
        <current_location>
            <latitude>-56.7088745</latitude>
            <longitude>149.494499</longitude>
        </current_location>
        <blood_group>O+</blood_group>
        <websites>
            <website>https://www.lelievre.fr/</website>
            <website>https://bernier.fr/</website>
        </websites>
        <username>noel32</username>
        <name>Luc Meyer</name>
        <sex>M</sex>
        <address>684, boulevard Margaret Garnier
96838 Da Costa</address>
        <mail>nicole82@bouygtel.fr</mail>
        <birthdate>1921-04-27</birthdate>
    </profile>
    <profile ssn="064-95-7931">
        <job>contremaître</job>
        <company>Roche</company>
        <residence>35, avenue de Andre
80266 Jacob-les-Bains</residence>
        <current_location>
            <latitude>-79.434306</latitude>
            <longitude>-50.008269</longitude>
        </current_location>
        <blood_group>O-</blood_group>
        <websites>
            <website>http://www.parent.fr/</website>
            <website>https://dufour.fr/</website>
            <website>http://www.humbert.com/</website>
            <website>https://www.boyer.net/</website>
        </websites>
        <username>ghernandez</username>
        <name>Gilbert Poirier</name>
        <sex>M</sex>
        <address>60, rue de Collet
40531 Morvanboeuf</address>
        <mail>clemence22@club-internet.fr</mail>
        <birthdate>1968-08-18</birthdate>
    </profile>
    <profile ssn="065-83-0793">
        <job>critique d'art</job>
        <company>Meunier</company>
        <residence>91, avenue Bertin
66624 Guillou</residence>
        <current_location>
            <latitude>40.394601</latitude>
            <longitude>28.203459</longitude>
        </current_location>
        <blood_group>AB+</blood_group>
        <websites>
            <website>http://www.pichon.com/</website>
            <website>http://chauveau.com/</website>
        </websites>
        <username>gillessabine</username>
        <name>Dominique Ferrand</name>
        <sex>F</sex>
        <address>rue Arnaude Baron
22300 Barbe</address>
        <mail>utorres@voila.fr</mail>
        <birthdate>1912-10-02</birthdate>
    </profile>
    <profile ssn="066-39-4549">
        <job>ingénieur de la police technique et scientifique</job>
        <company>Perret</company>
        <residence>96, chemin de Perret
91066 Perrier-la-Forêt</residence>
        <current_location>
            <latitude>-67.974417</latitude>
            <longitude>-32.491563</longitude>
        </current_location>
        <blood_group>O-</blood_group>
        <websites>
            <website>http://richard.net/</website>
            <website>http://www.fischer.fr/</website>
        </websites>
        <username>williamalbert</username>
        <name>Alex Godard</name>
        <sex>F</sex>
        <address>85, boulevard Guichard
20974 Marionnec</address>
        <mail>stephanieneveu@club-internet.fr</mail>
        <birthdate>1993-02-27</birthdate>
    </profile>
    <profile ssn="070-76-9294">
        <job>assistant en architecture</job>
        <company>Aubert Georges S.A.</company>
        <residence>31, rue de Garcia
41968 Saint Sabine</residence>
        <current_location>
            <latitude>36.045194</latitude>
            <longitude>131.046682</longitude>
        </current_location>
        <blood_group>O-</blood_group>
        <websites>
            <website>http://poirier.fr/</website>
        </websites>
        <username>denisalix</username>
        <name>Christophe Coste-Baron</name>
        <sex>M</sex>
        <address>44, boulevard Bertrand
85123 Blondel</address>
        <mail>bouvierremy@ifrance.com</mail>
        <birthdate>1908-03-12</birthdate>
    </profile>

Super simple quand même !

Python: Différentes manières d'initialiser un dictionnaire

Avec Python, il existe différentes possibilités pour initialiser un dictionnaire (dict()).

En voici trois.

La première:

>>> d = dict(un=1, deux=2, trois=3)
>>> d
{'un': 1, 'deux': 2, 'trois': 3}

La seconde, avec deux listes, une pour les clés et une pour les valeurs et la fonction zip:

>>> l1 = ['un','deux','trois']
>>> l2 = [1,2,3]
>>> d1 = dict(zip(l1, l2))
>>> d1
{'un': 1, 'deux': 2, 'trois': 3}

La troisième, toujours avec les deux listes et la fonction zip mais en utilisant, en plus, la compréhension de dictionnaire:

>>> l1 = ['un','deux','trois']
>>> l2 = [1,2,3]
>>> d2 = {k: v for k, v in zip(l1, l2)}
>>> d2
{'un': 1, 'deux': 2, 'trois': 3}

Avec la compréhension de dictionnaire, il est possible d'appliquer des traitements supplémentaires sur les clés et les valeurs.

Etiquettes: 

Python: Effectuer une recherche textuelle dans des fichiers textes

Il suffit de quelques lignes Python pour faire une recherche textuelle dans des fichiers textes.

A l'aide des deux modules pathlib et re, vous allez voir comme c'est simple.

Voici un exemple qui permet de rechercher une portion de code dans des fichiers JAVA.

>>> from pathlib import Path
>>> import re
>>> D = Path(r'C:\Users\ronan\javasrc')
>>> R = re.compile('public double .+;')
>>> for F in D.rglob('*.java'):
    if R.search(F.read_text()):
        print(F)

Et voilà, à chaque fois que la chaine recherchée est trouvée dans le contenu du fichier, son nom est affiché dans la console.

infoPour rappel, la fonction rglob de la classe Path du module pathlib permet d'effectuer une recherche récursive dans le dossier concerné.
La classe Path du module pathlib permet de lire le contenu des fichiers très simplement à l'aide de la fonction read_text.

Etiquettes: 

Python: Encoder / Décoder une URL

Pour encoder une URL (python2):

import urllib
url = "http://www.quennec.fr"
urlCodee = urllib.quote_plus(url)
print(urlCodee)
'http%3A%2F%2Fwww.quennec.fr'

Pour décoder une URL (python2):

import urllib
urlCodee = "http%3A%2F%2Fwww.quennec.fr"
url = urllib.unquote_plus(urlCodee)
print(url)
'http://www.quennec.fr'

Pour encoder une URL (python3):

Il faut utiliser le module urllib.parse (idem pour le décodage).

import urllib
url = "http://www.quennec.fr"
urlCodee = urllib.parse.quote_plus(url)
print(urlCodee)
'http%3A%2F%2Fwww.quennec.fr'

Python: Encoder un fichier en base64

Il est parfois nécessaire d'encoder en base64 des fichiers binaires tels que des images pour pouvoir les envoyer par mail par exemple.

import base64
with open("image.png", "rb") as image_file:
    encoded_string = base64.b64encode(image_file.read())

Le contenu de la variable encoded_string peut être envoyé dans un mail, inséré dans un champ d'une table d'une base de données etc etc ...

Python: Envoyer un mail tout simplement

Envoi d'un mail via un serveur SMTP (sans authentification):

import smtplib
from email.utils import formatdate
server = smtplib.SMTP()
# server.set_debuglevel(1) # Décommenter pour activer le debug
server.connect('smtp.toto.fr')
# (220, 'toto ESMTP Postfix') # Réponse du serveur
server.helo()
# (250, 'toto\nPIPELINING\nSIZE 10240000\nVRFY\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN') # Réponse du serveur
fromaddr = 'TOTO <moi@toto.fr>'
toaddrs = ['lui@toto.fr', 'elle@toto.fr'] # On peut mettre autant d'adresses que l'on souhaite
sujet = "Un Mail avec Python"
message = u"""\
Velit morbi ultrices magna integer.
Metus netus nascetur amet cum viverra ve cum.
Curae fusce condimentum interdum felis sit risus.
Proin class condimentum praesent hendrer
it donec odio facilisi sit.
Etiam massa tempus scelerisque curae habitasse vestibulum arcu metus iaculis hac.
"""
msg = """\
From: %s\r\n\
To: %s\r\n\
Subject: %s\r\n\
Date: %s\r\n\
\r\n\
%s
""" % (fromaddr, ", ".join(toaddrs), sujet, formatdate(localtime=True), message)
try:
    server.sendmail(fromaddr, toaddrs, msg)
except smtplib.SMTPException as e:
    print(e)
# {} # Réponse du serveur
server.quit()
# (221, '2.0.0 Bye') # Réponse du serveur

Envoi d'un mail via un serveur SMTP en SSL (avec authentification):

import smtplib
from email.utils import formatdate
server = smtplib.SMTP_SSL() # On utilise SMTP_SSL() à la place de SMTP()
# server.set_debuglevel(1) # Décommenter pour activer le debug
server.connect('smtp.toto.fr')
# (220, 'toto ESMTP Postfix') # Réponse du serveur
server.ehlo() # ATTENTION, avec SSL, c'est la commande EHLO au lieu de HELO
#(250, 'toto\nPIPELINING\nSIZE 10240000\nVRFY\nETRN\nAUTH LOGIN PLAIN\nAUTH=LOGIN PLAIN\nENHANCEDSTATUSCODES\n8BITMIME\nDSN') # Réponse du serveur
server.login('user', 'pass') # On s'authentifie
# (235, '2.7.0 Authentication successful') # Réponse du serveur
fromaddr = 'TOTO <moi@toto.fr>'
toaddrs = ['lui@toto.fr', 'elle@toto.fr'] # On peut mettre autant d'adresses que l'on souhaite
sujet = "Un Mail avec Python"
message = u"""\
Velit morbi ultrices magna integer.
Metus netus nascetur amet cum viverra ve cum.
Curae fusce condimentum interdum felis sit risus.
Proin class condimentum praesent hendrer
it donec odio facilisi sit.
Etiam massa tempus scelerisque curae habitasse vestibulum arcu metus iaculis hac.
"""
msg = """\
From: %s\r\n\
To: %s\r\n\
Subject: %s\r\n\
Date: %s\r\n\
\r\n\
%s
""" % (fromaddr, ", ".join(toaddrs), sujet, formatdate(localtime=True), message)
try:
    server.sendmail(fromaddr, toaddrs, msg)
except smtplib.SMTPException as e:
    print(e)
# {} # Réponse du serveur
server.quit()
# (221, '2.0.0 Bye')

Envoi d'un mail via un serveur SMTP en SSL + TLS (avec authentification):

import smtplib
from email.utils import formatdate
server = smtplib.SMTP('smtp.toto.fr', 587) # Avec TLS, on utilise SMTP()
# server.set_debuglevel(1) # Décommenter pour activer le debug
server.connect('smtp.toto.fr', 587) # On indique le port TLS
# (220, 'toto ESMTP Postfix') # Réponse du serveur
server.ehlo() # On utilise la commande EHLO
# (250, 'toto\nPIPELINING\nSIZE 10240000\nVRFY\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN') # Réponse du serveur
server.starttls() # On appelle la fonction STARTTLS
# (220, '2.0.0 Ready to start TLS') # Réponse du serveur
server.login('user', 'pass')
# (235, '2.7.0 Authentication successful') # Réponse du serveur
fromaddr = 'TOTO <moi@toto.fr>'
toaddrs = ['lui@toto.fr', 'elle@toto.fr'] # On peut mettre autant d'adresses que l'on souhaite
sujet = "Un Mail avec Python"
message = u"""\
Velit morbi ultrices magna integer.
Metus netus nascetur amet cum viverra ve cum.
Curae fusce condimentum interdum felis sit risus.
Proin class condimentum praesent hendrer
it donec odio facilisi sit.
Etiam massa tempus scelerisque curae habitasse vestibulum arcu metus iaculis hac.
"""
msg = """\
From: %s\r\n\
To: %s\r\n\
Subject: %s\r\n\
Date: %s\r\n\
\r\n\
%s
""" % (fromaddr, ", ".join(toaddrs), sujet, formatdate(localtime=True), message)
try:
    server.sendmail(fromaddr, toaddrs, msg)
except smtplib.SMTPException as e:
    print(e)
# {} # Réponse du serveur
server.quit()
# (221, '2.0.0 Bye')

infoEn cas d'erreur, lors de l'exécution de la commande server.sendmail(fromaddr, toaddrs, msg), le serveur répond en indiquant l'erreur rencontrée

{'elle@toto.fr': (450, '4.2.0 <elle@toto.fr>: Recipient address rejected: User unknown in relay recipient table')}

Et en HTML, c'est plus sympa

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate

server = smtplib.SMTP()
# server.set_debuglevel(1) # Décommenter pour activer le debug
server.connect('smtp.toto.fr')

server.helo()

fromaddr = 'TOTO <moi@toto.fr>'
toaddrs = ['lui@toto.fr', 'elle@toto.fr'] # On peut mettre autant d'adresses que l'on souhaite
sujet = "Un Mail avec Python"
html = u"""\
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div>
Velit morbi ultrices magna integer.
Metus netus nascetur amet cum viverra ve cum.
Curae fusce condimentum interdum felis sit risus.
Proin class condimentum praesent hendrer
it donec odio facilisi sit.
Etiam massa tempus scelerisque curae habitasse vestibulum arcu metus iaculis hac.
</div>
</body
</html>
"""

msg = MIMEMultipart('alternative')
msg['Subject'] = sujet
msg['From'] = fromaddr
msg['To'] = ','.join(toaddrs)
msg["Date"] = formatdate(localtime=True)

part = MIMEText(html, 'html')
msg.attach(part)

try:
    server.sendmail(fromaddr, toaddrs, msg.as_string())
except smtplib.SMTPException as e:
    print(e)

server.quit()

A la demande d'Alain, un exemple de script qui permet d'envoyer un mail avec une pièce jointe:
 

source:
https://docs.python.org/3/library/email.examples.html#email-examples
https://docs.python.org/3.8/library/mimetypes.html

from email.message import EmailMessage
from email.utils import formatdate
import mimetypes
from pathlib import Path
import smtplib
msg = EmailMessage()
msg['Subject'] = 'Un Mail avec Python'
msg['From'] = 'TOTO <moi@toto.fr>'
msg['To'] = ', '.join(['lui@toto.fr', 'elle@toto.fr'])
msg["Date"] = formatdate(localtime=True)
msg.set_content("""\
Salut!
Ci joint le fichier demandé.
""")
cfile = Path('myfile.txt')
ctype, encoding = mimetypes.guess_type(cfile)
if ctype is None or encoding is not None:
    ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
msg.add_attachment(cfile.read_bytes(), maintype=maintype, subtype=subtype, filename=cfile.name)
with smtplib.SMTP('smtp.toto.fr') as csmtp:
    csmtp.send_message(msg)

Python 3.9 - Envoi d'un mail via le SMTP office365 (outlook):

import smtplib
from email.utils import formatdate
server = smtplib.SMTP('SMTP.office365.com', 587)
server.connect('SMTP.office365.com', 587)
server.ehlo()
server.starttls()
server.login('julieraymond@example.org', 'Q7!7KgR3+5')
fromaddr = 'Antoine-Marcel Weber <julieraymond@example.org>'
toaddrs = ['renee88@example.org', 'etienneguyot@example.net']
sujet = "Reculer voile finir arrêter complètement lever."
message = u"""\
Extraordinaire longtemps taille musique. Discussion expérience français anglais fleur toujours.
Professeur dieu agir ouvrir escalier facile supposer. Action maintenir contenter bois. Même puis autre discours de mince veille quarante.
Fille retomber air accuser. Suivant dans bien sous oncle vent. Travail jour refuser nuit réel."""
msg = f"""\
From: {fromaddr}\r\n\
To: {", ".join(toaddrs)}\r\n\
Subject: {sujet}\r\n\
Date: {formatdate(localtime=True)}\r\n\
\r\n\
{message}"""
try:
    server.sendmail(fromaddr, toaddrs, msg)
except smtplib.SMTPException as e:
    print(e)
server.quit()

Python: Exporter la liste des packages

Pour sauvegarder dans un fichier requirements.txt tous les packages installés dans l'environnement Python:

$ python -m pip list --format freeze > requirements.txt

Et pour restaurer la liste complète, pour une nouvelle installation, par exemple:

$ python -m pip install -U -r .\requirements.txt

 

Python: Générer un fichier gpx

Un fichier GPX est un ensemble de coordonnées géographiques qui permet de créer des traces ou des itinéraires.

Pour utiliser ce script il est nécessaire d'installer les modules python suivants:

# python3 -m pip install --upgrade geopy requests dateutil bs4 numpy
#!/usr/bin/python3
# -*- coding: UTF-8 -*-

from geopy.geocoders import Nominatim
import requests
from dateutil.relativedelta import relativedelta
from bs4 import BeautifulSoup as bs
import numpy as np

cities = ['nantes, france', 'ancenis, france', 'angers, france']
geolocator = Nominatim(user_agent="Python3")
LAT = []
LNG = []
url = "https://routing.openstreetmap.de/routed-car/route/v1/driving/{frlng},{frlat};{tolng},{tolat}"
params = dict(overview='false', geometries='polyline', steps='true')
xml = bs(features='xml')
gpx = xml.new_tag('gpx')
gpx.attrs = {'creator':"Script Python", 'version':"1.1",\
             'xmlns':"http://www.topografix.com/GPX/1/1", 'xmlns:xsi':"http://www.w3.org/2001/XMLSchema-instance",\
             'xsi:schemaLocation':"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"}
trkl = []

for idxcity in enumerate(cities[:-1]):
    idx, city = idxcity
    FROM = geolocator.geocode(city)
    TO = geolocator.geocode(cities[idx+1])
    datas = dict(frlat=FROM.latitude, frlng=FROM.longitude, tolat=TO.latitude, tolng=TO.longitude)
    req1 = requests.get(url.format(**datas), params)
    js = req1.json()
    steps = js.get('routes')[0].get('legs')[0].get('steps')
    distance = round(js.get('routes')[0].get('distance') / 1000, 0)
    duration = relativedelta(seconds=js.get('routes')[0].get('duration'))
    frname = FROM.raw.get('display_name')
    toname = TO.raw.get('display_name')
    print(f'Itinéraire entre {frname} et {toname}.')
    wpt = xml.new_tag('wpt')
    wpt.attrs = {'lat':FROM.latitude , 'lon':FROM.longitude}
    wptname = xml.new_tag('name')
    wptname.string = frname
    wpt.append(wptname)
    gpx.append(wpt)
    trk = xml.new_tag('trk')
    name = xml.new_tag('name')
    name.string = f'{frname} - {toname}'
    desc = xml.new_tag('desc')
    desc.string = f'{distance} km, {int(duration.hours)}:{int(duration.minutes)}'
    trkseg = xml.new_tag('trkseg')
    for x in steps:
        for y in x['intersections']:
            lon, lat = y['location']
            LAT.append(lat)
            LNG.append(lon)
            trkpt = xml.new_tag('trkpt')
            trkpt.attrs = dict(lat=lat, lon=lon)
            trkseg.append(trkpt)
    trk.append(name)
    trk.append(desc)
    trk.append(trkseg)
    trkl.append(trk)

wpt = xml.new_tag('wpt')
wpt.attrs = {'lat':TO.latitude , 'lon':TO.longitude}
wptname = xml.new_tag('name')
wptname.string = toname
wpt.append(wptname)
gpx.append(wpt)
for trk in trkl:
    gpx.append(trk)
xml.append(gpx)

print(xml.prettify())
print(f'Position centrale: {np.mean(LAT)},{np.mean(LNG)}')

Dans mon exemple, je souhaite créer un itinéraire Nantes - Ancenis - Angers

Ce script va générer un contenu xml comme ceci:

<?xml version="1.0" encoding="utf-8"?>
<gpx creator="Script Python" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
 <wpt lat="47.2186371" lon="-1.5541362">
  <name>
   Nantes, Loire-Atlantique, Pays de la Loire, France métropolitaine, France
  </name>
 </wpt>
 <wpt lat="47.3648141" lon="-1.1816088">
  <name>
   Ancenis, Châteaubriant-Ancenis, Loire-Atlantique, Pays de la Loire, France métropolitaine, 44150, France
  </name>
 </wpt>
 <wpt lat="47.4739884" lon="-0.5515588">
  <name>
   Angers, Maine-et-Loire, Pays de la Loire, France métropolitaine, France
  </name>
 </wpt>
 <trk>
  <name>
   Nantes, Loire-Atlantique, Pays de la Loire, France métropolitaine, France - Ancenis, Châteaubriant-Ancenis, Loire-Atlantique, Pays de la Loire, France métropolitaine, 44150, France
  </name>
  <desc>
   44.0 km, 0:37
  </desc>
  <trkseg>
   <trkpt lat="47.218536" lon="-1.554075"/>
   <trkpt lat="47.218427" lon="-1.55398"/>
   <trkpt lat="47.218355" lon="-1.553938"/>
   <trkpt lat="47.218337" lon="-1.553975"/>
   <trkpt lat="47.218143" lon="-1.553968"/>
   <trkpt lat="47.218345" lon="-1.553476"/>
   <trkpt lat="47.217903" lon="-1.553421"/>
   <trkpt lat="47.217333" lon="-1.553389"/>
   <trkpt lat="47.21738" lon="-1.552416"/>
   <trkpt lat="47.217183" lon="-1.55225"/>
   <trkpt lat="47.216706" lon="-1.551836"/>
   <trkpt lat="47.214756" lon="-1.550238"/>
   <trkpt lat="47.214525" lon="-1.55024"/>
   <trkpt lat="47.214364" lon="-1.55024"/>
   <trkpt lat="47.214335" lon="-1.550143"/>
   <trkpt lat="47.214401" lon="-1.549983"/>
   <trkpt lat="47.215347" lon="-1.547803"/>
   <trkpt lat="47.215441" lon="-1.547616"/>
   <trkpt lat="47.215485" lon="-1.547787"/>
   <trkpt lat="47.215777" lon="-1.547168"/>
   <trkpt lat="47.214924" lon="-1.546208"/>
   <trkpt lat="47.214733" lon="-1.546091"/>
   <trkpt lat="47.214651" lon="-1.546014"/>
   <trkpt lat="47.213826" lon="-1.545304"/>
   <trkpt lat="47.21329" lon="-1.54487"/>
   <trkpt lat="47.21323" lon="-1.544817"/>
   <trkpt lat="47.213195" lon="-1.544786"/>
   <trkpt lat="47.211943" lon="-1.543759"/>
   <trkpt lat="47.211785" lon="-1.543751"/>
   <trkpt lat="47.21172" lon="-1.543605"/>
   <trkpt lat="47.211718" lon="-1.543567"/>
   <trkpt lat="47.211794" lon="-1.543374"/>
   <trkpt lat="47.211883" lon="-1.543345"/>
   <trkpt lat="47.212079" lon="-1.542566"/>
   <trkpt lat="47.212058" lon="-1.542522"/>
   <trkpt lat="47.212074" lon="-1.542362"/>
   <trkpt lat="47.212166" lon="-1.542314"/>
   <trkpt lat="47.212658" lon="-1.541764"/>
   <trkpt lat="47.213442" lon="-1.540108"/>
   <trkpt lat="47.213524" lon="-1.539675"/>
   <trkpt lat="47.213516" lon="-1.539587"/>
   <trkpt lat="47.213565" lon="-1.539412"/>
   <trkpt lat="47.213187" lon="-1.537239"/>
   <trkpt lat="47.21313" lon="-1.53719"/>
   <trkpt lat="47.213121" lon="-1.537007"/>
   <trkpt lat="47.213037" lon="-1.536746"/>
   <trkpt lat="47.212985" lon="-1.536673"/>
   <trkpt lat="47.213028" lon="-1.536488"/>
   <trkpt lat="47.213094" lon="-1.536477"/>
   <trkpt lat="47.213187" lon="-1.534501"/>
   <trkpt lat="47.21322" lon="-1.533176"/>
   <trkpt lat="47.213208" lon="-1.532928"/>
   <trkpt lat="47.213599" lon="-1.529777"/>
   <trkpt lat="47.21352" lon="-1.529614"/>
   <trkpt lat="47.213549" lon="-1.529374"/>
   <trkpt lat="47.213601" lon="-1.52929"/>
   <trkpt lat="47.213848" lon="-1.527269"/>
   <trkpt lat="47.213891" lon="-1.527133"/>
   <trkpt lat="47.214444" lon="-1.525283"/>
   <trkpt lat="47.214487" lon="-1.525111"/>
   <trkpt lat="47.215749" lon="-1.521155"/>
   <trkpt lat="47.215802" lon="-1.521012"/>
   <trkpt lat="47.216484" lon="-1.519088"/>
   <trkpt lat="47.21657" lon="-1.518772"/>
   <trkpt lat="47.216602" lon="-1.518664"/>
   <trkpt lat="47.218048" lon="-1.515754"/>
   <trkpt lat="47.218123" lon="-1.515623"/>
   <trkpt lat="47.218259" lon="-1.515588"/>
   <trkpt lat="47.218354" lon="-1.515663"/>
   <trkpt lat="47.219157" lon="-1.516011"/>
   <trkpt lat="47.219943" lon="-1.515468"/>
   <trkpt lat="47.232161" lon="-1.485789"/>
   <trkpt lat="47.232229" lon="-1.485681"/>
   <trkpt lat="47.235333" lon="-1.482751"/>
   <trkpt lat="47.2354" lon="-1.482516"/>
   <trkpt lat="47.235546" lon="-1.482416"/>
   <trkpt lat="47.237493" lon="-1.479355"/>
   <trkpt lat="47.237476" lon="-1.479187"/>
   <trkpt lat="47.237571" lon="-1.47894"/>
   <trkpt lat="47.237679" lon="-1.478875"/>
   <trkpt lat="47.237841" lon="-1.478929"/>
   <trkpt lat="47.237865" lon="-1.478955"/>
   <trkpt lat="47.23968" lon="-1.47769"/>
   <trkpt lat="47.252684" lon="-1.477342"/>
   <trkpt lat="47.255057" lon="-1.47844"/>
   <trkpt lat="47.26823" lon="-1.479836"/>
   <trkpt lat="47.270611" lon="-1.479649"/>
   <trkpt lat="47.273086" lon="-1.479482"/>
   <trkpt lat="47.287258" lon="-1.479302"/>
   <trkpt lat="47.290543" lon="-1.476443"/>
   <trkpt lat="47.291107" lon="-1.471735"/>
   <trkpt lat="47.399341" lon="-1.200555"/>
   <trkpt lat="47.39931" lon="-1.195302"/>
   <trkpt lat="47.399266" lon="-1.192826"/>
   <trkpt lat="47.399277" lon="-1.192706"/>
   <trkpt lat="47.399304" lon="-1.192254"/>
   <trkpt lat="47.399387" lon="-1.191168"/>
   <trkpt lat="47.399341" lon="-1.190568"/>
   <trkpt lat="47.399294" lon="-1.190479"/>
   <trkpt lat="47.396962" lon="-1.190001"/>
   <trkpt lat="47.395567" lon="-1.189791"/>
   <trkpt lat="47.395442" lon="-1.189771"/>
   <trkpt lat="47.394798" lon="-1.189592"/>
   <trkpt lat="47.394688" lon="-1.189555"/>
   <trkpt lat="47.391687" lon="-1.188792"/>
   <trkpt lat="47.391615" lon="-1.18885"/>
   <trkpt lat="47.391446" lon="-1.188684"/>
   <trkpt lat="47.391328" lon="-1.188495"/>
   <trkpt lat="47.390855" lon="-1.18821"/>
   <trkpt lat="47.390459" lon="-1.188124"/>
   <trkpt lat="47.390307" lon="-1.188215"/>
   <trkpt lat="47.39019" lon="-1.18816"/>
   <trkpt lat="47.390099" lon="-1.187937"/>
   <trkpt lat="47.390099" lon="-1.18787"/>
   <trkpt lat="47.386784" lon="-1.185549"/>
   <trkpt lat="47.386387" lon="-1.185516"/>
   <trkpt lat="47.386278" lon="-1.185596"/>
   <trkpt lat="47.386194" lon="-1.185538"/>
   <trkpt lat="47.386165" lon="-1.185462"/>
   <trkpt lat="47.385743" lon="-1.185042"/>
   <trkpt lat="47.383247" lon="-1.183994"/>
   <trkpt lat="47.382652" lon="-1.183919"/>
   <trkpt lat="47.382623" lon="-1.183975"/>
   <trkpt lat="47.382522" lon="-1.184013"/>
   <trkpt lat="47.382465" lon="-1.18397"/>
   <trkpt lat="47.382435" lon="-1.18385"/>
   <trkpt lat="47.37964" lon="-1.182634"/>
   <trkpt lat="47.378879" lon="-1.182151"/>
   <trkpt lat="47.377554" lon="-1.181295"/>
   <trkpt lat="47.377402" lon="-1.181282"/>
   <trkpt lat="47.377354" lon="-1.181357"/>
   <trkpt lat="47.377255" lon="-1.181394"/>
   <trkpt lat="47.377135" lon="-1.181101"/>
   <trkpt lat="47.376946" lon="-1.180899"/>
   <trkpt lat="47.375527" lon="-1.179969"/>
   <trkpt lat="47.373767" lon="-1.178955"/>
   <trkpt lat="47.373402" lon="-1.178983"/>
   <trkpt lat="47.373302" lon="-1.179059"/>
   <trkpt lat="47.373222" lon="-1.178978"/>
   <trkpt lat="47.372174" lon="-1.178905"/>
   <trkpt lat="47.372112" lon="-1.178948"/>
   <trkpt lat="47.372092" lon="-1.178902"/>
   <trkpt lat="47.371988" lon="-1.178904"/>
   <trkpt lat="47.3704" lon="-1.178883"/>
   <trkpt lat="47.370202" lon="-1.178866"/>
   <trkpt lat="47.369961" lon="-1.178839"/>
   <trkpt lat="47.369467" lon="-1.178797"/>
   <trkpt lat="47.369026" lon="-1.178756"/>
   <trkpt lat="47.368278" lon="-1.178584"/>
   <trkpt lat="47.366732" lon="-1.178582"/>
   <trkpt lat="47.3667" lon="-1.178583"/>
   <trkpt lat="47.366514" lon="-1.178598"/>
   <trkpt lat="47.365991" lon="-1.1786"/>
   <trkpt lat="47.365403" lon="-1.178805"/>
   <trkpt lat="47.364949" lon="-1.178912"/>
   <trkpt lat="47.364547" lon="-1.178981"/>
   <trkpt lat="47.364474" lon="-1.17934"/>
   <trkpt lat="47.364424" lon="-1.17992"/>
   <trkpt lat="47.364136" lon="-1.180728"/>
   <trkpt lat="47.36416" lon="-1.180945"/>
   <trkpt lat="47.364268" lon="-1.181819"/>
   <trkpt lat="47.364763" lon="-1.18192"/>
  </trkseg>
 </trk>
 <trk>
  <name>
   Ancenis, Châteaubriant-Ancenis, Loire-Atlantique, Pays de la Loire, France métropolitaine, 44150, France - Angers, Maine-et-Loire, Pays de la Loire, France métropolitaine, France
  </name>
  <desc>
   57.0 km, 0:41
  </desc>
  <trkseg>
   <trkpt lat="47.364763" lon="-1.18192"/>
   <trkpt lat="47.364848" lon="-1.18196"/>
   <trkpt lat="47.365275" lon="-1.182104"/>
   <trkpt lat="47.365965" lon="-1.182286"/>
   <trkpt lat="47.36612" lon="-1.18113"/>
   <trkpt lat="47.366403" lon="-1.179826"/>
   <trkpt lat="47.366732" lon="-1.178582"/>
   <trkpt lat="47.368278" lon="-1.178584"/>
   <trkpt lat="47.369026" lon="-1.178756"/>
   <trkpt lat="47.369467" lon="-1.178797"/>
   <trkpt lat="47.369961" lon="-1.178839"/>
   <trkpt lat="47.370202" lon="-1.178866"/>
   <trkpt lat="47.3704" lon="-1.178883"/>
   <trkpt lat="47.371988" lon="-1.178904"/>
   <trkpt lat="47.372092" lon="-1.178902"/>
   <trkpt lat="47.37213" lon="-1.178835"/>
   <trkpt lat="47.372174" lon="-1.178905"/>
   <trkpt lat="47.373149" lon="-1.178876"/>
   <trkpt lat="47.373211" lon="-1.178881"/>
   <trkpt lat="47.373272" lon="-1.178766"/>
   <trkpt lat="47.373372" lon="-1.17878"/>
   <trkpt lat="47.373415" lon="-1.178882"/>
   <trkpt lat="47.373767" lon="-1.178955"/>
   <trkpt lat="47.375527" lon="-1.179969"/>
   <trkpt lat="47.376946" lon="-1.180899"/>
   <trkpt lat="47.377202" lon="-1.180991"/>
   <trkpt lat="47.377317" lon="-1.180973"/>
   <trkpt lat="47.377418" lon="-1.18115"/>
   <trkpt lat="47.378879" lon="-1.182151"/>
   <trkpt lat="47.37964" lon="-1.182634"/>
   <trkpt lat="47.382469" lon="-1.183726"/>
   <trkpt lat="47.38252" lon="-1.183687"/>
   <trkpt lat="47.382627" lon="-1.183736"/>
   <trkpt lat="47.382649" lon="-1.183773"/>
   <trkpt lat="47.383247" lon="-1.183994"/>
   <trkpt lat="47.385743" lon="-1.185042"/>
   <trkpt lat="47.386187" lon="-1.185301"/>
   <trkpt lat="47.386324" lon="-1.18524"/>
   <trkpt lat="47.386395" lon="-1.185331"/>
   <trkpt lat="47.386784" lon="-1.185549"/>
   <trkpt lat="47.390184" lon="-1.18765"/>
   <trkpt lat="47.390236" lon="-1.187609"/>
   <trkpt lat="47.390418" lon="-1.187636"/>
   <trkpt lat="47.390457" lon="-1.187677"/>
   <trkpt lat="47.390521" lon="-1.187884"/>
   <trkpt lat="47.390855" lon="-1.18821"/>
   <trkpt lat="47.391328" lon="-1.188495"/>
   <trkpt lat="47.391505" lon="-1.18847"/>
   <trkpt lat="47.391608" lon="-1.188436"/>
   <trkpt lat="47.391718" lon="-1.188566"/>
   <trkpt lat="47.394176" lon="-1.18923"/>
   <trkpt lat="47.394707" lon="-1.189419"/>
   <trkpt lat="47.394816" lon="-1.189455"/>
   <trkpt lat="47.396101" lon="-1.189755"/>
   <trkpt lat="47.396578" lon="-1.189826"/>
   <trkpt lat="47.398083" lon="-1.190038"/>
   <trkpt lat="47.39932" lon="-1.190194"/>
   <trkpt lat="47.399436" lon="-1.190105"/>
   <trkpt lat="47.399616" lon="-1.190257"/>
   <trkpt lat="47.399625" lon="-1.190297"/>
   <trkpt lat="47.399598" lon="-1.190518"/>
   <trkpt lat="47.399572" lon="-1.190561"/>
   <trkpt lat="47.399396" lon="-1.192017"/>
   <trkpt lat="47.399366" lon="-1.192722"/>
   <trkpt lat="47.399355" lon="-1.192841"/>
   <trkpt lat="47.39927" lon="-1.194329"/>
   <trkpt lat="47.399938" lon="-1.194827"/>
   <trkpt lat="47.401803" lon="-1.191068"/>
   <trkpt lat="47.434482" lon="-0.816724"/>
   <trkpt lat="47.433855" lon="-0.809441"/>
   <trkpt lat="47.46242" lon="-0.695047"/>
   <trkpt lat="47.467222" lon="-0.677305"/>
   <trkpt lat="47.466628" lon="-0.674247"/>
   <trkpt lat="47.462466" lon="-0.64317"/>
   <trkpt lat="47.4658" lon="-0.631566"/>
   <trkpt lat="47.466724" lon="-0.628554"/>
   <trkpt lat="47.46786" lon="-0.624756"/>
   <trkpt lat="47.469056" lon="-0.620722"/>
   <trkpt lat="47.470005" lon="-0.617478"/>
   <trkpt lat="47.469578" lon="-0.612166"/>
   <trkpt lat="47.468996" lon="-0.605593"/>
   <trkpt lat="47.469353" lon="-0.597749"/>
   <trkpt lat="47.469475" lon="-0.594958"/>
   <trkpt lat="47.468084" lon="-0.585639"/>
   <trkpt lat="47.464676" lon="-0.574428"/>
   <trkpt lat="47.465802" lon="-0.568425"/>
   <trkpt lat="47.46848" lon="-0.564903"/>
   <trkpt lat="47.469906" lon="-0.562375"/>
   <trkpt lat="47.470104" lon="-0.561996"/>
   <trkpt lat="47.471074" lon="-0.560347"/>
   <trkpt lat="47.471585" lon="-0.559436"/>
   <trkpt lat="47.472442" lon="-0.558075"/>
   <trkpt lat="47.474047" lon="-0.556018"/>
   <trkpt lat="47.475157" lon="-0.555216"/>
   <trkpt lat="47.476197" lon="-0.554405"/>
   <trkpt lat="47.476813" lon="-0.553653"/>
   <trkpt lat="47.476842" lon="-0.553634"/>
   <trkpt lat="47.477151" lon="-0.553118"/>
   <trkpt lat="47.476517" lon="-0.551532"/>
   <trkpt lat="47.476224" lon="-0.550865"/>
   <trkpt lat="47.47594" lon="-0.550269"/>
   <trkpt lat="47.475462" lon="-0.549161"/>
   <trkpt lat="47.475096" lon="-0.549804"/>
   <trkpt lat="47.475076" lon="-0.549856"/>
   <trkpt lat="47.474792" lon="-0.550535"/>
   <trkpt lat="47.474586" lon="-0.551368"/>
   <trkpt lat="47.474561" lon="-0.55151"/>
   <trkpt lat="47.474384" lon="-0.552383"/>
   <trkpt lat="47.473949" lon="-0.551608"/>
  </trkseg>
 </trk>
</gpx>

Ce contenu xml peut ensuite être chargé dans n'importe quelle application ou site internet utilisant ce genre de fichier.

Etiquettes: 

Python: Générer une liste des jours ouvrés sans les jours fériés

Pouvoir générer la liste de tous les jours ouvrés d'un mois, d'un trimestre, d'un semestre ou d'une année et en excluant les jours fériés existants.

Pour cela je vais également utiliser la classe JoursFeries créée dans un précédent article.

from dateutil.parser import parse
from dateutil.rrule import rrule, DAILY
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import MO, TU, WE, TH, FR

dtstart = parse('2022-01-01')
list_jours_ouvres = list(
    map(
        lambda x: x.date(),
        rrule(DAILY, dtstart=dtstart, until=dtstart + relativedelta(months=4, day=1, days=-1),
              byweekday=[MO, TU, WE, TH, FR])
    )
)

Je vais donc générer une liste de tous les jours ouvrés, du lundi au vendredi, du 1er janvier au 30 avril (4 mois).

>>> list_jours_ouvres
[datetime.date(2022, 1, 3),
 datetime.date(2022, 1, 4),
 datetime.date(2022, 1, 5),
 datetime.date(2022, 1, 6),
 datetime.date(2022, 1, 7),
 datetime.date(2022, 1, 10),
 datetime.date(2022, 1, 11),
 datetime.date(2022, 1, 12),
 datetime.date(2022, 1, 13),
 ...
 datetime.date(2022, 4, 18),
 datetime.date(2022, 4, 19),
 datetime.date(2022, 4, 20),
 datetime.date(2022, 4, 21),
 datetime.date(2022, 4, 22),
 datetime.date(2022, 4, 25),
 datetime.date(2022, 4, 26),
 datetime.date(2022, 4, 27),
 datetime.date(2022, 4, 28),
 datetime.date(2022, 4, 29)]
>>> len(list_jours_ouvres)
85

J'ai donc une liste comprenant 85 jours ouvrés du lundi au vendredi du 3 janvier au 29 avril.

Pour supprimer les jours fériés de la liste

>>> set(list_jours_ouvres) - set(JoursFeries(dtstart.year).to_list())
{datetime.date(2022, 1, 3),
 datetime.date(2022, 1, 4),
 datetime.date(2022, 1, 5),
 datetime.date(2022, 1, 6),
 datetime.date(2022, 1, 7),
 datetime.date(2022, 1, 10),
 datetime.date(2022, 1, 11),
 datetime.date(2022, 1, 12),
 datetime.date(2022, 1, 13),
 datetime.date(2022, 1, 14),
 datetime.date(2022, 1, 17),
 datetime.date(2022, 1, 18),
 datetime.date(2022, 1, 19),
 datetime.date(2022, 1, 20),
 datetime.date(2022, 1, 21),
 datetime.date(2022, 1, 24),
 ...
 datetime.date(2022, 4, 13),
 datetime.date(2022, 4, 14),
 datetime.date(2022, 4, 15),
 datetime.date(2022, 4, 19),
 datetime.date(2022, 4, 20),
 datetime.date(2022, 4, 21),
 datetime.date(2022, 4, 22),
 datetime.date(2022, 4, 25),
 datetime.date(2022, 4, 26),
 datetime.date(2022, 4, 27),
 datetime.date(2022, 4, 28),
 datetime.date(2022, 4, 29)}
>>> len(set(list_jours_ouvres) - set(JoursFeries(dtstart.year).to_list()))
84

Résultat, 1 jour a été supprimé de la liste.
Il s'agit du lundi de Pâques le 18 avril 2022.

>>> for x in JoursFeries(dtstart.year).proprietes:
    print(f"{x:<20} {getattr(JoursFeries(dtstart.year), x).strftime('%a %d %b %Y')}")

JOUR_DE_L_AN         Sat 01 Jan 2022
PAQUES               Sun 17 Apr 2022
LUNDI_DE_PAQUES      Mon 18 Apr 2022
FETE_DU_TRAVAIL      Sun 01 May 2022
VICTOIRE_1945        Sun 08 May 2022
ASCENSION            Thu 26 May 2022
PENTECOTE            Sun 05 Jun 2022
LUNDI_DE_PENTECOTE   Mon 06 Jun 2022
FETE_NATIONALE       Thu 14 Jul 2022
ASSOMPTION           Mon 15 Aug 2022
TOUSSAINT            Tue 01 Nov 2022
ARMISTICE_1918       Fri 11 Nov 2022
NOEL                 Sun 25 Dec 2022

 

Python: Indenter un contenu JSON

Voici un petit script Python qui me permet d'afficher à l'écran n'importe quel contenu JSON correctement indenté (afin de faciliter la lisibilité).

linkprettyJson.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import json, sys
from pprint import pprint

if not sys.stdin.isatty():
    JSON = json.loads(sys.stdin.read())
    pprint(JSON)
else:
    print("Use this script with a pipe command.")
    print("")
    print("echo '...' | python3 prettyJson.py")
    print("or")
    print("cat ... | python3 prettyJson.py")

Par exemple, avec un flux JSON non formaté:

# cat flux.json
{"time": "2016-06-29 15:54","countryName": "Austria","sunset": "2016-06-29 21:18","rawOffset": 1,"dstOffset": 2,"countryCode": "AT","gmtOffset": 1,"lng": 10.2,"sunrise": "2016-06-29 05:27","timezoneId": "Europe/Vienna","lat": 47.01}

Une fois traité par le script:

# cat flux.json | python3 prettyJson.py
{'countryCode': 'AT',
 'countryName': 'Austria',
 'dstOffset': 2,
 'gmtOffset': 1,
 'lat': 47.01,
 'lng': 10.2,
 'rawOffset': 1,
 'sunrise': '2016-06-29 05:27',
 'sunset': '2016-06-29 21:18',
 'time': '2016-06-29 15:54',
 'timezoneId': 'Europe/Vienna'}

Fonctionne également avec la commande CURL:

# curl "http://api.geonames.org/citiesJSON?formatted=true&north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo&style=full" -o - -s | python3 prettyJson.py
{'geonames': [{'countrycode': 'MX',
               'fcl': 'P',
               'fclName': 'city, village,...',
               'fcode': 'PPLC',
               'fcodeName': 'capital of a political entity',
               'geonameId': 3530597,
               'lat': 19.428472427036,
               'lng': -99.12766456604,
               'name': 'Mexiko-Stadt',
               'population': 12294193,
               'toponymName': 'Mexico City',
               'wikipedia': 'en.wikipedia.org/wiki/Mexico_City'},
              {'countrycode': 'CN',
               'fcl': 'P',
               'fclName': 'city, village,...',
               'fcode': 'PPLC',
               'fcodeName': 'capital of a political entity',
               'geonameId': 1816670,
               'lat': 39.9074977414405,
               'lng': 116.397228240967,
               'name': 'Peking',
               'population': 11716620,
               'toponymName': 'Beijing',
               'wikipedia': 'en.wikipedia.org/wiki/Beijing'}]}
Etiquettes: 

Python: Initialiser une liste à l'aide de la fonction INPUT

Voici une petite fonction permettant d'initialiser une liste en saisissant les valeurs à la volée à l'aide de la fonction input.

Certe inutile pour une courte liste [1,2,3,4], mais vraiment très pratique pour une longue liste et contenant surtout des données alpha-numériques (évite de saisir les quotes entourant la chaine de texte)

Compatible Python2 et Python3

>>> def initializeList():
    l = []
    # Pour la compatibilité Python2 et Python3
    _input = input
    if hasattr(__builtins__, 'raw_input'):
        _input = raw_input
    # Boucle infinie pour remplir la liste
    while True:
        d = _input("--> ").strip()
        if d == '':
            # On quitte si la valeur est vide
            break
        else:
            # On tente une conversion en int
            try:
                l.append(int(d))
            except ValueError:
                # Si erreur, on tente une conversion en float
                try:
                    l.append(float(d))
                except ValueError:
                    # Sinon, on laisse la valeur au format chaine de texte
                    l.append(d)
    # On retourne la liste
    return l

>>> myList = initializeList()
--> 1
--> 2
--> 3
--> 4
--> 5
--> 6
--> a
--> z
--> er
--> r
--> t
--> y
--> fg
--> d
-->
>>> print(myList)
[1, 2, 3, 4, 5, 6, 'a', 'z', 'er', 'r', 't', 'y', 'fg', 'd']
>>>

Ca peut toujours servir

Python: Insérer du contenu binaire dans un champ d'une table Sqlite3

Pour l'exemple, je vais utiliser un base de donnée sqlite3 en mémoire.

>>> import sqlite3
>>> from pathlib import Path
>>> con = sqlite3.connect(':memory:')
>>> cur = con.cursor()
>>> cur.execute('create table datas (ID INTEGER primary key, IMAGE BLOB)')
>>> p = Path(r'C:\Users\toto\Pictures')
>>> f = p / 'toto.png'
>>> cur.execute("insert into datas (IMAGE) values (?)", (sqlite3.Binary(f.read_bytes()), ))
>>> con.commit()

 

Etiquettes: 

Python: Inverser le contraste d'une image PNG

Python fournit tout un tas de modules complémentaires pour tous les domaines mêmes les plus improbables.

PIL est un module Python qui permet de travailler sur tous les types d'images.

L'astuce suivante permet d'afficher l'image avec le contraste inversé en seulement quatre lignes de script Python.

Tous les tests sont réalisés avec cette image:

olivier

>>> from PIL import Image
>>> img = Image.open('firefox_i9MPoqCoYe_2.png')
>>> arr = img.split()
>>> arr[-1].show()

Le résultat:

olivier

Comment ça marche ?

En règle générale le format PNG contient 4 canaux, les 3 canaux principaux "RGB" et un canal supplémentaire "A" pour la translucidité.

Grâce à la méthode split, on récupère un tuple contenant tous les canaux séparément.

>>> print(arr)
(<PIL.Image.Image image mode=L size=351x330 at 0x789953440550>, <PIL.Image.Image image mode=L size=351x330 at 0x7899545902E0>, <PIL.Image.Image image mode=L size=351x330 at 0x789954A7F4C0>, <PIL.Image.Image image mode=L size=351x330 at 0x789954A7FEB0>)

La commande précédente montre les canaux disponibles, 4 au total.

Si on affiche le contenu des canaux grâce au module numpy

>>> import numpy as np
>>> np.array(img)
array([[[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]],

       [[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]],

       [[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]],

       ...,

       [[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]],

       [[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]],

       [[255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        ...,
        [255, 255, 255,   0],
        [255, 255, 255,   0],
        [255, 255, 255,   0]]], dtype=uint8)

La différence entre les 3 premiers canaux "RGB" et le quatrième "A" est flagrante.

Si toutefois l'image ne contient pas de canal "A", il  est possible d'obtenir le même résultat en agissant sur le troisième canal, le "B".

>>> arr[2].point(lambda x: (256-x)**2).show()

olivier

 

Python: Jours fériés français

Voici une classe que j'ai développé afin de gérer efficacement les jours fériés français.

Elle pourra être utilisée ultérieurement dans différentes applications.

Voici le code:

from enum import IntEnum
from datetime import datetime, date
from dateutil.easter import easter, EASTER_WESTERN
from dateutil.relativedelta import relativedelta
import json
from collections import namedtuple
import serpy

class Mois(IntEnum):
    
    JANVIER   = 1
    FEVRIER   = 2
    MARS      = 3
    AVRIL     = 4
    MAI       = 5
    JUIN      = 6
    JUILLET   = 7
    AOUT      = 8
    SEPTEMBRE = 9
    OCTOBRE   = 10
    NOVEMBRE  = 11
    DECEMBRE  = 12
            
class JoursFeries(object):
    
    def __init__(self, annee: int = datetime.now().year):
        self.CURRENT_YEAR = int(annee)
            
    @property
    def CURRENT_YEAR(self) -> int:
        return self._annee

    @CURRENT_YEAR.setter
    def CURRENT_YEAR(self, value: int) -> None:
        self._annee = value
    
    @property
    def JOUR_DE_L_AN(self) -> date:
        return date(self.CURRENT_YEAR, Mois.JANVIER, 1)
    
    @property
    def PAQUES(self) -> date:
        return easter(self.CURRENT_YEAR, method=EASTER_WESTERN)
    
    @property
    def LUNDI_DE_PAQUES(self) -> date:
        return self.PAQUES + relativedelta(days=1)
    
    @property
    def FETE_DU_TRAVAIL(self) -> date:
        return date(self.CURRENT_YEAR, Mois.MAI, 1)
    
    @property
    def VICTOIRE_1945(self) -> date:
        return date(self.CURRENT_YEAR, Mois.MAI, 8)
    
    @property
    def ASCENSION(self) -> date:
        return self.PAQUES + relativedelta(days=39)
    
    @property
    def PENTECOTE(self) -> date:
        return self.PAQUES + relativedelta(days=49)
    
    @property
    def LUNDI_DE_PENTECOTE(self) -> date:
        return self.PENTECOTE + relativedelta(days=1)
    
    @property
    def FETE_NATIONALE(self) -> date:
        return date(self.CURRENT_YEAR, Mois.JUILLET, 14)
    
    @property
    def ASSOMPTION(self) -> date:
        return date(self.CURRENT_YEAR, Mois.AOUT, 15)
    
    @property
    def TOUSSAINT(self) -> date:
        return date(self.CURRENT_YEAR, Mois.NOVEMBRE, 1)
    
    @property
    def ARMISTICE_1918(self) -> date:
        return date(self.CURRENT_YEAR, Mois.NOVEMBRE, 11)
    
    @property
    def NOEL(self) -> date:
        return date(self.CURRENT_YEAR, Mois.DECEMBRE, 25)
    
    @property
    def proprietes(self) -> list:
        return list(self.dumps().keys())
    
    def to_list(self) -> list:
        return [getattr(self, x) for x in self.dumps().keys()]
    
    def __str__(self) -> str:
        return '\n'.join([f'{x:<20s}: {getattr(self, x)}' for x in self.dumps().keys()])
    
    def __repr__(self) ->str:
        return json.dumps(self.dumps(), indent=4, ensure_ascii=False)
        
    def dumps(self) -> dict:
        return JoursFeriesSerialize(self).data
    
    def to_namedtuple(self) -> namedtuple:
        return namedtuple('JourFeries', self.dumps().keys())(**self.dumps())
    
class JoursFeriesSerialize(serpy.Serializer):
    
    JOUR_DE_L_AN       = serpy.StrField()
    PAQUES             = serpy.StrField()
    LUNDI_DE_PAQUES    = serpy.StrField()
    FETE_DU_TRAVAIL    = serpy.StrField()
    VICTOIRE_1945      = serpy.StrField()
    ASCENSION          = serpy.StrField()
    PENTECOTE          = serpy.StrField()
    LUNDI_DE_PENTECOTE = serpy.StrField()
    FETE_NATIONALE     = serpy.StrField()
    ASSOMPTION         = serpy.StrField()
    TOUSSAINT          = serpy.StrField()
    ARMISTICE_1918     = serpy.StrField()
    NOEL               = serpy.StrField()

La classe Mois énumère tous les mois de l'année grâce au package enum.IntEnum.

La classe JoursFeriesSerialize permet de sérialiser les données de la classe JoursFeries grâce au package serpy.

Pour finir, la classe JoursFeries qui permet de générer tous les jours fériés de l'année en cours ou de celle passée en paramètre au constructeur de la classe.

Le jour férié correspondant à Pâques est récupéré grâce au package dateutil.easter.easter et tous les jours fériés ayant un rapport avec Pâques sont générés grâce au package dateutil.relativedelta.relativedelta.

Pour l'utiliser, rien de plus simple:

>>> jf = JoursFeries()
>>> jf
{
    "JOUR_DE_L_AN": "2022-01-01",
    "PAQUES": "2022-04-17",
    "LUNDI_DE_PAQUES": "2022-04-18",
    "FETE_DU_TRAVAIL": "2022-05-01",
    "VICTOIRE_1945": "2022-05-08",
    "ASCENSION": "2022-05-26",
    "PENTECOTE": "2022-06-05",
    "LUNDI_DE_PENTECOTE": "2022-06-06",
    "FETE_NATIONALE": "2022-07-14",
    "ASSOMPTION": "2022-08-15",
    "TOUSSAINT": "2022-11-01",
    "ARMISTICE_1918": "2022-11-11",
    "NOEL": "2022-12-25"
}

Sans paramètre, la classe retourne tous les jours fériés de l'année en cours.

La méthode __repr__ permet d'afficher les jours fériés au format json (str).

Avec l'année passée en paramètre:

>>> jf = JoursFeries(2024)
>>> jf
{
    "JOUR_DE_L_AN": "2024-01-01",
    "PAQUES": "2024-03-31",
    "LUNDI_DE_PAQUES": "2024-04-01",
    "FETE_DU_TRAVAIL": "2024-05-01",
    "VICTOIRE_1945": "2024-05-08",
    "ASCENSION": "2024-05-09",
    "PENTECOTE": "2024-05-19",
    "LUNDI_DE_PENTECOTE": "2024-05-20",
    "FETE_NATIONALE": "2024-07-14",
    "ASSOMPTION": "2024-08-15",
    "TOUSSAINT": "2024-11-01",
    "ARMISTICE_1918": "2024-11-11",
    "NOEL": "2024-12-25"
}

La méthode to_list retourne une liste contenant tous les jours fériés au format datetime.date

>>> jf.to_list()
[datetime.date(2022, 1, 1),
 datetime.date(2022, 4, 17),
 datetime.date(2022, 4, 18),
 datetime.date(2022, 5, 1),
 datetime.date(2022, 5, 8),
 datetime.date(2022, 5, 26),
 datetime.date(2022, 6, 5),
 datetime.date(2022, 6, 6),
 datetime.date(2022, 7, 14),
 datetime.date(2022, 8, 15),
 datetime.date(2022, 11, 1),
 datetime.date(2022, 11, 11),
 datetime.date(2022, 12, 25)]

Un print de l'objet:

>>> print(jf)
JOUR_DE_L_AN        : 2022-01-01
PAQUES              : 2022-04-17
LUNDI_DE_PAQUES     : 2022-04-18
FETE_DU_TRAVAIL     : 2022-05-01
VICTOIRE_1945       : 2022-05-08
ASCENSION           : 2022-05-26
PENTECOTE           : 2022-06-05
LUNDI_DE_PENTECOTE  : 2022-06-06
FETE_NATIONALE      : 2022-07-14
ASSOMPTION          : 2022-08-15
TOUSSAINT           : 2022-11-01
ARMISTICE_1918      : 2022-11-11
NOEL                : 2022-12-25

La méthode dumps retourne une représentation json (dict)

>>> jf.dumps()
{'JOUR_DE_L_AN': '2022-01-01',
 'PAQUES': '2022-04-17',
 'LUNDI_DE_PAQUES': '2022-04-18',
 'FETE_DU_TRAVAIL': '2022-05-01',
 'VICTOIRE_1945': '2022-05-08',
 'ASCENSION': '2022-05-26',
 'PENTECOTE': '2022-06-05',
 'LUNDI_DE_PENTECOTE': '2022-06-06',
 'FETE_NATIONALE': '2022-07-14',
 'ASSOMPTION': '2022-08-15',
 'TOUSSAINT': '2022-11-01',
 'ARMISTICE_1918': '2022-11-11',
 'NOEL': '2022-12-25'}

La méthode to_namedtuple retourne un objet collections.namedtuple

>>> jf.to_namedtuple()
JourFeries(JOUR_DE_L_AN='2022-01-01', PAQUES='2022-04-17', LUNDI_DE_PAQUES='2022-04-18', FETE_DU_TRAVAIL='2022-05-01', VICTOIRE_1945='2022-05-08', ASCENSION='2022-05-26', PENTECOTE='2022-06-05', LUNDI_DE_PENTECOTE='2022-06-06', FETE_NATIONALE='2022-07-14', ASSOMPTION='2022-08-15', TOUSSAINT='2022-11-01', ARMISTICE_1918='2022-11-11', NOEL='2022-12-25')

Pour afficher le jour férié correspondant à Pâques

>>> jf.PAQUES
datetime.date(2022, 4, 17)
>>> jf.PAQUES.year
2022
>>> jf.PAQUES.day
17
>>> jf.PAQUES.month
4
>>> jf.PAQUES.isocalendar()
(2022, 15, 7) # le 7ème jour de la semaine 15 de l'année 2022
>>> jf.PAQUES.isoformat()
2022-04-17
>>> jf.PAQUES.timetuple()
time.struct_time(tm_year=2022, tm_mon=4, tm_mday=17, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=107, tm_isdst=-1)

Tous les jours fériés disposent des mêmes fonctions.

Comme les jours fériés sont au format datetime.date, toutes les méthodes de l'objet datetime.date sont également accessibles.

La méthode proprietes affiche la liste des noms de tous les jours fériés

>>> jf.proprietes
['JOUR_DE_L_AN',
 'PAQUES',
 'LUNDI_DE_PAQUES',
 'FETE_DU_TRAVAIL',
 'VICTOIRE_1945',
 'ASCENSION',
 'PENTECOTE',
 'LUNDI_DE_PENTECOTE',
 'FETE_NATIONALE',
 'ASSOMPTION',
 'TOUSSAINT',
 'ARMISTICE_1918',
 'NOEL']

Ce qui permet de faire ceci

>>> for nom in jf.proprietes:
...    print(f"{nom:<20} {getattr(JoursFeries(dtstart.year), nom).strftime('%a %d %B %Y')}")
JOUR_DE_L_AN         Sat 01 Jan 2022
PAQUES               Sun 17 Apr 2022
LUNDI_DE_PAQUES      Mon 18 Apr 2022
FETE_DU_TRAVAIL      Sun 01 May 2022
VICTOIRE_1945        Sun 08 May 2022
ASCENSION            Thu 26 May 2022
PENTECOTE            Sun 05 Jun 2022
LUNDI_DE_PENTECOTE   Mon 06 Jun 2022
FETE_NATIONALE       Thu 14 Jul 2022
ASSOMPTION           Mon 15 Aug 2022
TOUSSAINT            Tue 01 Nov 2022
ARMISTICE_1918       Fri 11 Nov 2022
NOEL                 Sun 25 Dec 2022

Vraiment super pratique.

Python: La class avec Serpy

Serpy est un package Python qui permet sérialiser en json n'importe quel objet.

Serpy permet d'implémenter facilement et rapidement les méthodes __str__ et __repr__ d'une classe.

Il permet également d'implémenter une méthode dumps.

Exemple avec la classe suivante:

import serpy
import json
from decimal import Decimal

class Personne(object):

    def __init__(self, firstname: str, lastname: str, sexe: str, age: int, salaire: float, position: dict):
        self.firstname = firstname
        self.lastname = lastname
        self.sexe = sexe
        self.age = age
        self.salaire = salaire
        self.position = position

Cette classe permet de créer un objet Personne.
L'argument position est un dictionnaire dans lequel on renseigne une latitude et une longitude. Il devra contenir les 2 clés 'latitude' et 'longitude'.

Je vais maintenant créer deux nouvelles classes, qui vont permettre de sérialiser mon objet Personne et mon sous-objet Position.

class PositionSerializer(serpy.DictSerializer):
    latitude = serpy.FloatField()
    longitude = serpy.FloatField()


class PersonneSerializer(serpy.Serializer):
    firstname = serpy.StrField()
    lastname = serpy.StrField()
    sexe = serpy.MethodField()
    age = serpy.IntField()
    salaire = serpy.FloatField()
    position = PositionSerializer()

    def get_sexe(self, obj):
        if obj.sexe == 'M':
            return 'Homme'
        elif obj.sexe == 'F':
            return 'Femme'
        else:
            return 'N/A'

La classe PersonneSerializer étend la classe serpy.Serializer.
Elle reprend les différents arguments de la classe Personne en indiquant pour chaque argument son type Serpy (StrField, IntField, FloatField).
Le type particulier MethodField permet d'associer une méthode à l'argument afin d'effectuer des calculs spécifiques. La méthode doit être nommée comme le nom de l'argument précédé du terme 'get_'
Il est également possible d'associer à l'argument une classe qui permet de sérialiser le contenu de l'objet. Dans mon exemple, l'argument position est une dictionnaire qui contient 2 données décimales. La classe PositionSerializer étend la classe serpy.DictSerializer et reprend les 2 clés de mon objet dict position.

Je vais maintenant modifier ma classe Personne afin d'y ajouter les 3 méthodes __str__, __repr__ et dumps.

class Personne(object):

    def __init__(self, firstname: str, lastname: str, sexe: str, age: int, salaire: float, position: dict):
        self.firstname = firstname
        self.lastname = lastname
        self.sexe = sexe
        self.age = age
        self.salaire = salaire
        self.position = position

    def __str__(self):
        return json.dumps(PersonneSerializer(self).data, indent=4)

    def __repr__(self):
        return json.dumps(PersonneSerializer(self).data)

    def dumps(self):
        return PersonneSerializer(self).data

La méthode __str__ retourne mon objet sérialisé au format json via la classe PersonneSerializer.
La méthode __repr__ retourne également mon objet sérialisé au format json via la classe PersonneSerializer mais sans mise en forme.
La méthode dumps retourne mon objet sérialisé via la classe PersonneSerializer.

Et voici le résultat:

    personne1 = Personne(firstname='Matthieu', lastname='Lopes', sexe='M', age=43, salaire=1509.56,
                         position={'latitude': Decimal('-55.5232795'), 'longitude': Decimal('157.460948')})
    print(f'{" Personne 1 ":-^30}')
    print(repr(personne1))
    print(personne1)
    dumps = personne1.dumps()
    print(type(dumps))
    print(dumps)
    personne2 = Personne(firstname='Constance', lastname='Lopez', sexe='F', age=87, salaire=2056.35,
                         position={'latitude': Decimal('-56.710608'), 'longitude': Decimal('-154.540259')})
    print('')
    print(f'{" Personne 2 ":-^30}')
    print(repr(personne2))
    print(personne2)
    dumps = personne2.dumps()
    print(type(dumps))
    print(dumps)

--------- Personne 1 ---------
{"firstname": "Matthieu", "lastname": "Lopes", "sexe": "Homme", "age": 43, "salaire": 1509.56, "position": {"latitude": -55.5232795, "longitude": 157.460948}}
{
    "firstname": "Matthieu",
    "lastname": "Lopes",
    "sexe": "Homme",
    "age": 43,
    "salaire": 1509.56,
    "position": {
        "latitude": -55.5232795,
        "longitude": 157.460948
    }
}
<class 'dict'>
{'firstname': 'Matthieu', 'lastname': 'Lopes', 'sexe': 'Homme', 'age': 43, 'salaire': 1509.56, 'position': {'latitude': -55.5232795, 'longitude': 157.460948}}

--------- Personne 2 ---------
{"firstname": "Constance", "lastname": "Lopez", "sexe": "Femme", "age": 87, "salaire": 2056.35, "position": {"latitude": -56.710608, "longitude": -154.540259}}
{
    "firstname": "Constance",
    "lastname": "Lopez",
    "sexe": "Femme",
    "age": 87,
    "salaire": 2056.35,
    "position": {
        "latitude": -56.710608,
        "longitude": -154.540259
    }
}
<class 'dict'>
{'firstname': 'Constance', 'lastname': 'Lopez', 'sexe': 'Femme', 'age': 87, 'salaire': 2056.35, 'position': {'latitude': -56.710608, 'longitude': -154.540259}}

Le résultat est parfait.

Python: La compréhension de listes et de dictionnaires

La compréhension de listes et de dictionnaires en Python est une méthode très puissante pour transposer un texte structuré en liste, dictionnaire etc etc.

Python: La compréhension de dictionnaires

Nous allons utiliser la même chaine de texte que pour la compréhension de liste.

>>> s = """AIN    1    01    00
AISNE    1    02    00
ALLIER    1    03    00
ALPES(BASSES-)    1    04    00
ALPES(HAUTES-)    1    05    00
ALPES-MARITIMES    1    06    00
ARDECHE    1    07    00
ARDENNES    1    08    00
ARIEGE    1    09    00
AUBE    1    10    00
AUDE    1    11    00
AVEYRON    1    12    00
BOUCHES-DU-RHONE    1    13    00
CALVADOS    1    14    00
CANTAL    1    15    00
CHARENTE    1    16    00
CHARENTE-INFERIEURE    1    17    00
CHER    1    18    00
CORREZE    1    19    00
CORSE    1    20    00
COTE-D'OR    1    21    00
COTES-DU-NORD    1    22    00
CREUSE    1    23    00
"""

Nous allons initialiser un dictionnaire, ayant en clé le nom du département (par exemple), et les données numériques dans une liste correspondante à la valeur de la clé.

>>> d = {x.split('\t')[0]: x.split('\t')[1:] for x in s.split('\n')}
>>> pprint(d)
{'': [],
 'AIN': ['1', '01', '00'],
 'AISNE': ['1', '02', '00'],
 'ALLIER': ['1', '03', '00'],
 'ALPES(BASSES-)': ['1', '04', '00'],
 'ALPES(HAUTES-)': ['1', '05', '00'],
 'ALPES-MARITIMES': ['1', '06', '00'],
 'ARDECHE': ['1', '07', '00'],
 'ARDENNES': ['1', '08', '00'],
 'ARIEGE': ['1', '09', '00'],
 'AUBE': ['1', '10', '00'],
 'AUDE': ['1', '11', '00'],
 'AVEYRON': ['1', '12', '00'],
 'BOUCHES-DU-RHONE': ['1', '13', '00'],
 'CALVADOS': ['1', '14', '00'],
 'CANTAL': ['1', '15', '00'],
 'CHARENTE': ['1', '16', '00'],
 'CHARENTE-INFERIEURE': ['1', '17', '00'],
 'CHER': ['1', '18', '00'],
 'CORREZE': ['1', '19', '00'],
 'CORSE': ['1', '20', '00'],
 "COTE-D'OR": ['1', '21', '00'],
 'COTES-DU-NORD': ['1', '22', '00'],
 'CREUSE': ['1', '23', '00']}

Par contre, notre dictionnaire contient une ligne vide (la première), nous allons donc en tenir compte lors de la création de notre dictionnaire.

>>> d = {x.split('\t')[0]: x.split('\t')[1:] for x in s.split('\n') if x.split('\t')[0] != ''}
>>> pprint(d)
{'AIN': ['1', '01', '00'],
 'AISNE': ['1', '02', '00'],
 'ALLIER': ['1', '03', '00'],
 'ALPES(BASSES-)': ['1', '04', '00'],
 'ALPES(HAUTES-)': ['1', '05', '00'],
 'ALPES-MARITIMES': ['1', '06', '00'],
 'ARDECHE': ['1', '07', '00'],
 'ARDENNES': ['1', '08', '00'],
 'ARIEGE': ['1', '09', '00'],
 'AUBE': ['1', '10', '00'],
 'AUDE': ['1', '11', '00'],
 'AVEYRON': ['1', '12', '00'],
 'BOUCHES-DU-RHONE': ['1', '13', '00'],
 'CALVADOS': ['1', '14', '00'],
 'CANTAL': ['1', '15', '00'],
 'CHARENTE': ['1', '16', '00'],
 'CHARENTE-INFERIEURE': ['1', '17', '00'],
 'CHER': ['1', '18', '00'],
 'CORREZE': ['1', '19', '00'],
 'CORSE': ['1', '20', '00'],
 "COTE-D'OR": ['1', '21', '00'],
 'COTES-DU-NORD': ['1', '22', '00'],
 'CREUSE': ['1', '23', '00']}

Et voilà, tout est nickel. Et tout ça à partir d'une simple chaine de texte.

Pour obtenir les données relatives au département de la Charente:

>>> d['CHARENTE']
['1', '16', '00']
Etiquettes: 

Python: La compréhension de listes

Par exemple, avec la chaine de texte suivante qui contient des noms de départements et quelques données numériques:

>>> s = """AIN    1    01    00
AISNE    1    02    00
ALLIER    1    03    00
ALPES(BASSES-)    1    04    00
ALPES(HAUTES-)    1    05    00
ALPES-MARITIMES    1    06    00
ARDECHE    1    07    00
ARDENNES    1    08    00
ARIEGE    1    09    00
AUBE    1    10    00
AUDE    1    11    00
AVEYRON    1    12    00
BOUCHES-DU-RHONE    1    13    00
CALVADOS    1    14    00
CANTAL    1    15    00
CHARENTE    1    16    00
CHARENTE-INFERIEURE    1    17    00
CHER    1    18    00
CORREZE    1    19    00
CORSE    1    20    00
COTE-D'OR    1    21    00
COTES-DU-NORD    1    22    00
CREUSE    1    23    00
"""

Quand on l'affiche dans une console Python:

>>> s.__str__()
"AIN\t1\t01\t00\nAISNE\t1\t02\t00\nALLIER\t1\t03\t00\nALPES(BASSES-)\t1\t04\t00\nALPES(HAUTES-)\t1\t05\t00\nALPES-MARITIMES\t1\t06\t00\nARDECHE\t1\t07\t00\nARDENNES\t1\t08\t00\nARIEGE\t1\t09\t00\nAUBE\t1\t10\t00\nAUDE\t1\t11\t00\nAVEYRON\t1\t12\t00\nBOUCHES-DU-RHONE\t1\t13\t00\nCALVADOS\t1\t14\t00\nCANTAL\t1\t15\t00\nCHARENTE\t1\t16\t00\nCHARENTE-INFERIEURE\t1\t17\t00\nCHER\t1\t18\t00\nCORREZE\t1\t19\t00\nCORSE\t1\t20\t00\nCOTE-D'OR\t1\t21\t00\nCOTES-DU-NORD\t1\t22\t00\nCREUSE\t1\t23\t00\n"

On s'aperçoit que la chaine est composée de lignes séparées par '\n' et des colonnes séparées par '\t'.

Pour convertir cette chaine de texte en une liste simple il suffit de faire:

>>> l = s.split('\n')
>>> print(l)
['AIN\t1\t01\t00', 'AISNE\t1\t02\t00', 'ALLIER\t1\t03\t00', 'ALPES(BASSES-)\t1\t04\t00', 'ALPES(HAUTES-)\t1\t05\t00', 'ALPES-MARITIMES\t1\t06\t00', 'ARDECHE\t1\t07\t00', 'ARDENNES\t1\t08\t00', 'ARIEGE\t1\t09\t00', 'AUBE\t1\t10\t00', 'AUDE\t1\t11\t00', 'AVEYRON\t1\t12\t00', 'BOUCHES-DU-RHONE\t1\t13\t00', 'CALVADOS\t1\t14\t00', 'CANTAL\t1\t15\t00', 'CHARENTE\t1\t16\t00', 'CHARENTE-INFERIEURE\t1\t17\t00', 'CHER\t1\t18\t00', 'CORREZE\t1\t19\t00', 'CORSE\t1\t20\t00', "COTE-D'OR\t1\t21\t00", 'COTES-DU-NORD\t1\t22\t00', 'CREUSE\t1\t23\t00', '']

C'est mieux car on peut maintenant parcourir la liste ligne par ligne mais ce qui suit est encore mieux:

>>> l = [x.split('\t') for x in s.split('\n')]
>>> print(l)
[['AIN', '1', '01', '00'], ['AISNE', '1', '02', '00'], ['ALLIER', '1', '03', '00'], ['ALPES(BASSES-)', '1', '04', '00'], ['ALPES(HAUTES-)', '1', '05', '00'], ['ALPES-MARITIMES', '1', '06', '00'], ['ARDECHE', '1', '07', '00'], ['ARDENNES', '1', '08', '00'], ['ARIEGE', '1', '09', '00'], ['AUBE', '1', '10', '00'], ['AUDE', '1', '11', '00'], ['AVEYRON', '1', '12', '00'], ['BOUCHES-DU-RHONE', '1', '13', '00'], ['CALVADOS', '1', '14', '00'], ['CANTAL', '1', '15', '00'], ['CHARENTE', '1', '16', '00'], ['CHARENTE-INFERIEURE', '1', '17', '00'], ['CHER', '1', '18', '00'], ['CORREZE', '1', '19', '00'], ['CORSE', '1', '20', '00'], ["COTE-D'OR", '1', '21', '00'], ['COTES-DU-NORD', '1', '22', '00'], ['CREUSE', '1', '23', '00'], ['']]

Chaque colonne est séparée dans une liste. On appelle ça une compréhension de liste.

Il est donc très facile d'accéder à une donnée en particulier:

>>> print(l[0][0])
AIN
Etiquettes: 

Python: La compréhension de listes, effectuer des opérations complexes

Voici un exemple, qui ne sert pas à grand chose, mais qui permet de montrer les différents calculs complexes qu'il est possible de faire avec la compréhension de liste.

Dans cet exemple, j'ai une classe qui permet de générer, aléatoirement, des codes EAN13.

>>> from random import randint
>>> class EAN13():
    GENERATES = []
    def __init__(self):
        pass
    def new(self, count=1):
        ct = count
        while ct > 0:
            ean13 = '{:03}{}'.format(randint(40, 49), ''.join([((x+4)*'0'+str(randint(1, int((x+4)*'9'))))[-(x+4):] for x in range(2)]))
            ean13 += str(10 - (sum([int(y) * 3 if x % 2 == 0 else int(y) for x, y in enumerate(list(ean13), start=1)]) % 10))[-1]
            if ean13 not in EAN13.GENERATES:
                EAN13.GENERATES.append(ean13)
                ct -= 1
        return EAN13.GENERATES[-count:]

>>> EAN13().new(count=20)
['0467764013590', '0442457715586', '0478050754264', '0498544873950', '0419792606732', '0436177825380', '0487308428246', '0449011102394', '0487403914866', '0462030759684', '0438507088359', '0405068027851', '0489779856153', '0437057321695', '0465499916742', '0467772469648', '0493671951373', '0483943989975', '0499830229161', '0462787735498']

Comme indiqué sur wikipédia:

Un code EAN13 est composé de 13 chiffres

  • les deux ou trois premiers correspondent au pays de provenance du produit, ou à une classe normalisée de produits ;
  • les 4 ou 5 suivants sont le numéro de membre de l’entreprise participant au système EAN ;
  • les 4 ou 5 suivants sont le numéro d’article du produit ainsi marqué et
  • le treizième est une clé de contrôle calculée en fonction des douze précédents.

Je vais "exploser" mon code pour expliquer les différentes étapes.

Voici la ligne qui permet de générer aléatoirement les 12 premiers chiffres:

>>> ean13 = '{:03}{}'.format(randint(40, 49), ''.join([((x+4)*'0'+str(randint(1, int((x+4)*'9'))))[-(x+4):] for x in range(2)]))

Les 3 premiers chiffres de mon code, ceux correspondant au pays de provenance du produit, ou à une classe normalisée de produits, est un nombre aléatoire allant de 040 à 049 (à l'aide la fonction randint et format)

>>> '{:03}{}'.format(randint(40, 49), '')
'041'

Voici la fameuse compréhension de liste qui va permettre de générer deux nombres.
Le premier composé de 4 chiffres et le second composé de 5 chiffres.

>>> [((x+4)*'0'+str(randint(1, int((x+4)*'9'))))[-(x+4):] for x in range(2)]
['2983', '23696']

Si nous faisions la même chose mais sans utiliser la compréhension de liste, ça donnerait ceci:

>>> L = []
>>> for x in range(2):
    L.append(((x+4)*'0'+str(randint(1, int((x+4)*'9'))))[-(x+4):])
   
>>> L
['5237', '92948']

J'utilise donc ma boucle for pour gérérer la première fois (x=0) un nombre de 4 chiffres et la fois suivante (x=1) un nombre de 5 chiffres.

J'utilise également le slicing ([-(x+4):]) pour conserver uniquement les x derniers chiffres de mes deux nombres aléatoires auquels j'ai ajoutés des '0' à gauche pour être certain d'avoir le bon nombre de chiffres.

J'aurais également pû utiliser la fonction format comme ceci:

>>> L.append('{0:0{1}}'.format(randint(1, int((x+4)*'9')), x+4))

Il ne reste plus qu'à calculer la clé qui sera donc le treizième et dernier chiffre de notre code.

Voici donc la ligne de code qui permet de le faire:

>>> ean13 += str(10 - (sum([int(y) * 3 if x % 2 == 0 else int(y) for x, y in enumerate(list(ean13), start=1)]) % 10))[-1]

Cette ligne de code utilise également la compréhension de liste.

J'utilise donc une boucle for et la fonction enumerate qui permet d'indexer chaque chiffres de mon code.

Je vais donc pouvoir faire la somme de tous mes chiffres et en ayant multiplié par 3 les rangs pairs (comme indiqué dans la formule de calcul de la clé).

>>> list(ean13)
['0', '4', '6', '5', '0', '3', '9', '9', '9', '0', '9', '9']
>>> [int(y) * 3 if x % 2 == 0 else int(y) for x, y in enumerate(list(ean13), start=1)]
[0, 12, 6, 15, 0, 9, 9, 27, 9, 0, 9, 27]
>>> sum([int(y) * 3 if x % 2 == 0 else int(y) for x, y in enumerate(list(ean13), start=1)])
123

Ci-dessous le reste du code détaillé ligne par ligne pour obtenir la clé finale (le caractère '_' représentant le résultat de la dernière exécution)

>>> _ % 10
3
>>> 10 - _
7
>>> str(_)[-1]
'7'

Pour info:
La première ligne permet d'obtenir le reste de la division par 10 (123%10=3)
La seconde ligne permet de soustraire à 10 le chiffre précédement obtenu.
La troisième ligne permet uniquement de garder le bon chiffre, dans le cas où le reste de la division est égal à 0.

La compréhension de list en Python est vraiment très puissante.
Elle permet de faire beaucoup de choses d'une manière plus concentrée et parfois plus facile à comprendre.

J'espère avoir été assez clair dans mes explications...

Etiquettes: 

Python: Le module CSV

Testé avec Python 3.5

Le module csv de python permet de lire et d'écrire des fichiers csv très facilement.

Créer un fichier csv:

Exemple avec la liste suivante (cette liste contient 13 sous-listes de 7 valeurs)

>>> pprint(l)
[['root', 'x', '0', '0', 'root', '/root', '/bin/bash'],
 ['daemon', 'x', '1', '1', 'daemon', '/usr/sbin', '/usr/sbin/nologin'],
 ['bin', 'x', '2', '2', 'bin', '/bin', '/usr/sbin/nologin'],
 ['sys', 'x', '3', '3', 'sys', '/dev', '/usr/sbin/nologin'],
 ['sync', 'x', '4', '65534', 'sync', '/bin', '/bin/sync'],
 ['games', 'x', '5', '60', 'games', '/usr/games', '/usr/sbin/nologin'],
 ['man', 'x', '6', '12', 'man', '/var/cache/man', '/usr/sbin/nologin'],
 ['lp', 'x', '7', '7', 'lp', '/var/spool/lpd', '/usr/sbin/nologin'],
 ['mail', 'x', '8', '8', 'mail', '/var/mail', '/usr/sbin/nologin'],
 ['news', 'x', '9', '9', 'news', '/var/spool/news', '/usr/sbin/nologin'],
 ['uucp', 'x', '10', '10', 'uucp', '/var/spool/uucp', '/usr/sbin/nologin'],
 ['proxy', 'x', '13', '13', 'proxy', '/bin', '/usr/sbin/nologin'],
 ['www-data', 'x', '33', '33', 'www-data', '/var/www', '/usr/sbin/nologin']]

>>> import csv
>>> with open('passwd.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(l)

Vachement simple, non !

infoLa fonction "writerows" avec un "s" à la fin, permet d'écrire en une seule fois tout le contenu d'une liste contenant elle-même des sous-listes (comme dans l'exemple ci-dessus).
Par contre, pour écrire uniquement le contenu d'une liste, sans sous-liste, il faut utiliser la fonction "writerow" (sans le "s" à la fin).

>>> import csv
>>> with open('passwd.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        for x in l:
            writer.writerow(x)

warning Lors de l'ouverture du fichier à l'aide de la commande open, il faut obligatoirement indiqué le paramètre newline='' sinon des sauts de lignes supplémentaires seront ajoutés à chaque écriture.

Lire un fichier csv:

>>> import csv
>>> with open('passwd.csv', newline='') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

        
['root', 'x', '0', '0', 'root', '/root', '/bin/bash']
['daemon', 'x', '1', '1', 'daemon', '/usr/sbin', '/usr/sbin/nologin']
['bin', 'x', '2', '2', 'bin', '/bin', '/usr/sbin/nologin']
['sys', 'x', '3', '3', 'sys', '/dev', '/usr/sbin/nologin']
['sync', 'x', '4', '65534', 'sync', '/bin', '/bin/sync']
['games', 'x', '5', '60', 'games', '/usr/games', '/usr/sbin/nologin']
['man', 'x', '6', '12', 'man', '/var/cache/man', '/usr/sbin/nologin']
['lp', 'x', '7', '7', 'lp', '/var/spool/lpd', '/usr/sbin/nologin']
['mail', 'x', '8', '8', 'mail', '/var/mail', '/usr/sbin/nologin']
['news', 'x', '9', '9', 'news', '/var/spool/news', '/usr/sbin/nologin']
['uucp', 'x', '10', '10', 'uucp', '/var/spool/uucp', '/usr/sbin/nologin']
['proxy', 'x', '13', '13', 'proxy', '/bin', '/usr/sbin/nologin']
['www-data', 'x', '33', '33', 'www-data', '/var/www', '/usr/sbin/nologin']

Toujours aussi simple !

warning Ne pas oublier le paramètre newline='' avec la commande open

En prime, lecture d'un fichier csv à l'aide d'un tuple nommé contenant le nom des champs du fichiers csv.
Très utile pour la manipulation d'un fichier csv complexe.

Pour cela, nous allons utiliser la fonction namedtuple du module collections

>>> from collections import namedtuple
>>> # Nous initialisons la liste des noms de champs
>>> Headers = namedtuple('Headers', 'LoginName, EncryptedPassword, UserId, GroupId, UserName, HomeDirectory, Interpreter')
>>> with open('passwd.csv', newline='') as f:
    reader = csv.reader(f)
    for header in map(Headers._make, reader):
        # Nous pouvons afficher les valeurs à l'aide des attributs nommés
        print(header.LoginName, header.HomeDirectory)

        
root /root
daemon /usr/sbin
bin /bin
sys /dev
sync /bin
games /usr/games
man /var/cache/man
lp /var/spool/lpd
mail /var/mail
news /var/spool/news
uucp /var/spool/uucp
proxy /bin
www-data /var/www

Que dire de plus ...

Python: Le module enum

Le module enum est très pratique pour créer et utiliser des constantes dans des programmes python.

Dans l'exemple suivant, je crée une classe qui va me permettre de générer des constantes pour les codes HTTP à l'aide de la classe Enum du module enum.

Cette classe pourra ensuite être utilisée dans n'importe quel programme ayant besoin d'utiliser ces codes.

Pour l'exemple, j'ai utilisé que quelques codes.
La liste complète étant disponible ici.

from enum import Enum

class Status(Enum):
    HTTP_OK = 200
    HTTP_CREATED = 201
    HTTP_ACCEPTED = 202
    HTTP_NON_AUTHORITATIVE = 203
    HTTP_NO_CONTENT = 204
    HTTP_PARTIAL_CONTENT = 206
    HTTP_MULTIPLE_CHOICES = 300
    HTTP_MOVED_PERMANENTLY = 301
    HTTP_MOVED_TEMPORARILY = 302
    HTTP_SEE_OTHER = 303
    HTTP_NOT_MODIFIED = 304
    HTTP_BAD_REQUEST = 400
    HTTP_UNAUTHORIZED = 401
    HTTP_PAYMENT_REQUIRED = 402
    HTTP_FORBIDDEN = 403
    HTTP_NOT_FOUND = 404
    HTTP_METHOD_NOT_ALLOWED = 405
    HTTP_INTERNAL_SERVER_ERROR = 500
    HTTP_NOT_IMPLEMENTED = 501
    HTTP_BAD_GATEWAY = 502

J'ai donc créé une classe Status qui étend la classe Enum du module enum.

Pour créer les constantes, il suffit juste de créer des paires clés / valeurs.
Les valeurs peuvent être numériques ou des chaines de caractères.

Pour utiliser cette classe personnalisée, il suffit de procéder de cette manière.

Pour l'exemple, je pars du principe que la classe Status est enregistrée dans un fichier nommé Apache.py et que ce fichier est disponible dans un répertoire listé dans la variable path du module sys.

>>> from Apache import Status

>>> Status
<enum 'Status'>
>>> Status(200)
<Status.HTTP_OK: 200>
>>> Status['HTTP_UNAUTHORIZED']
<Status.HTTP_UNAUTHORIZED: 401>
>>> Status.HTTP_BAD_REQUEST
<Status.HTTP_BAD_REQUEST: 400>
>>> Status(200).value
200
>>> Status(200).name
'HTTP_OK'
>>> Status['HTTP_OK'].value
200

On voit bien que la classe Status étend le module enum.
Pour afficher la description (le nom de la constante correspondante) d'un code, il suffit d'appeler la classe avec le code entre parenthèses.
Si le code n'existe pas, une exception "ValueError" est levée.
On peut également afficher l'information à l'aide du nom de la constante entre crochets (comme un dictionnaire).
Les constantes sont accessibles directement via la classe Status comme n'importe quelles constantes de classes ordinaires.
Il est également très facile d'obtenir la valeur d'une constante et/ou son nom que ce soit à partir de sa valeur ou de son nom.

Il est également possible d'ajouter des informations supplémentaires comme un label par exemple.

from enum import Enum


class Status(int, Enum):

    HTTP_OK = (200, 'Ok')
    HTTP_CREATED = (201, 'Created')
    HTTP_ACCEPTED = (202, 'Accepted')
    HTTP_NON_AUTHORITATIVE = (203, 'Non Authoritative')
    HTTP_NO_CONTENT = (204, 'No Content')
    HTTP_PARTIAL_CONTENT = (206, 'Partial Content')
    HTTP_MULTIPLE_CHOICES = (300, 'Multiple Choices')
    HTTP_MOVED_PERMANENTLY = (301, 'Moved Permanently')
    HTTP_MOVED_TEMPORARILY = (302, 'Moved Temporarily')
    HTTP_SEE_OTHER = (303, 'See Other')
    HTTP_NOT_MODIFIED = (304, 'Not Modified')
    HTTP_BAD_REQUEST = (400, 'Bad Request')
    HTTP_UNAUTHORIZED = (401, 'Unauthorized')
    HTTP_PAYMENT_REQUIRED = (402, 'Payment Required')
    HTTP_FORBIDDEN = (403, 'Forbidden')
    HTTP_NOT_FOUND = (404, 'Not Found')
    HTTP_METHOD_NOT_ALLOWED = (405, 'Method Not Allowed')
    HTTP_INTERNAL_SERVER_ERROR = (500, 'Internal Server Error')
    HTTP_NOT_IMPLEMENTED = (501, 'Not Implemented')
    HTTP_BAD_GATEWAY = (502, 'Bad Gateway')

    def __new__(cls, value, label):
        obj = int.__new__(cls, value)
        obj._value_ = value
        obj.label = label
        return obj

Pour ajouter des labels par exemple, il est nécessaire de modifier la classe Status.
Il faut ajouter une dépendance à l'objet int.
Il faut ensuite modifier les valeurs en les remplaçant par des tuples contenant la valeur et le label.
Pour finir, il faut ajouter une fonction __new__ afin d'indiquer quelle valeur du tuple sera affectée à l'attribut _value_
La nouvelle classe est sauvegardée dans un fichier nommé HTTP_Status.py

Résultat:

from HTTP_Status import Status
Status
Out[3]: <enum 'Status'>
Status.HTTP_OK
Out[4]: <Status.HTTP_OK: 200>
Status.HTTP_OK.name
Out[5]: 'HTTP_OK'
Status.HTTP_OK.value
Out[6]: 200
Status.HTTP_OK.label
Out[7]: 'Ok'
Status.HTTP_NOT_FOUND
Out[8]: <Status.HTTP_NOT_FOUND: 404>
Status.HTTP_NOT_FOUND.name
Out[9]: 'HTTP_NOT_FOUND'
Status.HTTP_NOT_FOUND.value
Out[10]: 404
Status.HTTP_NOT_FOUND.label
Out[11]: 'Not Found'
Status(404)
Out[12]: <Status.HTTP_NOT_FOUND: 404>
Status(404).label
Out[13]: 'Not Found'
Status['HTTP_NOT_FOUND']
Out[14]: <Status.HTTP_NOT_FOUND: 404>
Status['HTTP_NOT_FOUND'].label
Out[15]: 'Not Found'
Status['HTTP_NOT_FOUND'].value
Out[16]: 404

 

Etiquettes: 

Python: Le module json

Sous Python, le module json permet de créer et de lire des données au format json.

Repartons de cet exemple: Python: Parser et indenter un flux XML

J'ai donc un dictionnaire de données contenant les valeurs suivantes:

>>> from pprint import pprint
>>> pprint(datas)
{'2782113': {'countryCode': 'AT',
             'countryName': 'Austria',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.33333',
             'lng': '13.33333',
             'name': 'Austria',
             'toponymName': 'Republic of Austria'},
 '2921044': {'countryCode': 'DE',
             'countryName': 'Germany',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '51.5',
             'lng': '10.5',
             'name': 'Germany',
             'toponymName': 'Federal Republic of Germany'},
 '3017382': {'countryCode': 'FR',
             'countryName': 'France',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '46',
             'lng': '2',
             'name': 'France',
             'toponymName': 'Republic of France'},
 '3042058': {'countryCode': 'LI',
             'countryName': 'Liechtenstein',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.16667',
             'lng': '9.53333',
             'name': 'Liechtenstein',
             'toponymName': 'Principality of Liechtenstein'},
 '3175395': {'countryCode': 'IT',
             'countryName': 'Italy',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '42.83333',
             'lng': '12.83333',
             'name': 'Italy',
             'toponymName': 'Repubblica Italiana'}}
>>>

Pour convertir ce dictionnaire au format json, il suffit d'utiliser le module json de Python:

>>> import json
>>> print(json.dumps(datas, indent=4))
{
    "3017382": {
        "toponymName": "Republic of France",
        "lat": "46",
        "countryName": "France",
        "fcl": "A",
        "fcode": "PCLI",
        "name": "France",
        "countryCode": "FR",
        "lng": "2"
    },
    "3175395": {
        "toponymName": "Repubblica Italiana",
        "lat": "42.83333",
        "countryName": "Italy",
        "fcl": "A",
        "fcode": "PCLI",
        "name": "Italy",
        "countryCode": "IT",
        "lng": "12.83333"
    },
    "2921044": {
        "toponymName": "Federal Republic of Germany",
        "lat": "51.5",
        "countryName": "Germany",
        "fcl": "A",
        "fcode": "PCLI",
        "name": "Germany",
        "countryCode": "DE",
        "lng": "10.5"
    },
    "3042058": {
        "toponymName": "Principality of Liechtenstein",
        "lat": "47.16667",
        "countryName": "Liechtenstein",
        "fcl": "A",
        "fcode": "PCLI",
        "name": "Liechtenstein",
        "countryCode": "LI",
        "lng": "9.53333"
    },
    "2782113": {
        "toponymName": "Republic of Austria",
        "lat": "47.33333",
        "countryName": "Austria",
        "fcl": "A",
        "fcode": "PCLI",
        "name": "Austria",
        "countryCode": "AT",
        "lng": "13.33333"
    }
}
>>>

Dans la commande précédente, j'ai utiliser la fonction dumps du module json qui permet de formater n'importe quel objet Python (dictionnaire, liste, une chaine de caractères, un nombre) au format json avec en plus une indentation avec 4 espaces (indent=4).

Il est donc tout à fait possible d'enregistrer ce résultat dans un fichier:

>>> with open('datas.json', 'w') as f:
    f.write(json.dumps(datas, indent=4))

    
1293
>>>

Ou alors avec la fonction dump (sans le `s` à la fin).
Cette commande permet d'enregistrer les données directement dans le flux précédemment ouvert.

>>> with open('datas.json', 'w') as f:
    json.dump(datas, f, indent=4)

    
>>>

Et voilà, je me retrouve avec un beau fichier json tout beau tout neuf.

Et pour lire un contenu json:

>>> with open('datas.json', 'r') as f:
    datas = json.load(f)

    
>>> type(datas)
<class 'dict'>
>>> pprint(datas)
{'2782113': {'countryCode': 'AT',
             'countryName': 'Austria',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.33333',
             'lng': '13.33333',
             'name': 'Austria',
             'toponymName': 'Republic of Austria'},
 '2921044': {'countryCode': 'DE',
             'countryName': 'Germany',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '51.5',
             'lng': '10.5',
             'name': 'Germany',
             'toponymName': 'Federal Republic of Germany'},
 '3017382': {'countryCode': 'FR',
             'countryName': 'France',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '46',
             'lng': '2',
             'name': 'France',
             'toponymName': 'Republic of France'},
 '3042058': {'countryCode': 'LI',
             'countryName': 'Liechtenstein',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.16667',
             'lng': '9.53333',
             'name': 'Liechtenstein',
             'toponymName': 'Principality of Liechtenstein'},
 '3175395': {'countryCode': 'IT',
             'countryName': 'Italy',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '42.83333',
             'lng': '12.83333',
             'name': 'Italy',
             'toponymName': 'Repubblica Italiana'}}
>>>

Grâce à la fonction load du module json, je peux charger tout le contenu d'un fichier json directement dans une variable.

Cette variable est de type dict (dictionnaire)

La fonction loads (avec un `s` à la fin) permet de charger une chaine de caractères au format json.

Etiquettes: 

Python: Le module pickle

Comme son nom l'indique, enfin non, pas vraiment, le module pickle permet de sauvegarder dans un fichier, au format binaire,  n'importe quel objet Python.

En clair, si pour une raison quelconque, dans un script Python, vous avez besoin de sauvegarder, temporairement ou même de façon plus pérenne, le contenu d'un objet Python comme une liste, un dictionnaire, un tuple etc etc ... au lieu d'utiliser une base de données ou un simple fichier texte, le module pickle est fait pour ça.

Il permet de stocker et de restaurer un objet Python tel quel sans aucune manipulation supplémentaire.

C'est vraiment super pratique.

Il fonctionne comme le module json mais n'est pas limité à un seul format d'objet.

Exemples:

>>> import pickle
>>> import string
>>> L = list(string.ascii_letters)
>>> print(L)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(L, f1)
>>> with open('mypicklefile', 'r') as f1:
    f1.read()

    
'€\x03]q\x00(X\x01\x00\x00\x00aq\x01X\x01\x00\x00\x00bq\x02X\x01\x00\x00\x00cq\x03X\x01\x00\x00\x00dq\x04X\x01\x00\x00\x00eq\x05X\x01\x00\x00\x00fq\x06X\x01\x00\x00\x00gq\x07X\x01\x00\x00\x00hq\x08X\x01\x00\x00\x00iq\tX\x01\x00\x00\x00jq\nX\x01\x00\x00\x00kq\x0bX\x01\x00\x00\x00lq\x0cX\x01\x00\x00\x00mq\nX\x01\x00\x00\x00nq\x0eX\x01\x00\x00\x00oq\x0fX\x01\x00\x00\x00pq\x10X\x01\x00\x00\x00qq\x11X\x01\x00\x00\x00rq\x12X\x01\x00\x00\x00sq\x13X\x01\x00\x00\x00tq\x14X\x01\x00\x00\x00uq\x15X\x01\x00\x00\x00vq\x16X\x01\x00\x00\x00wq\x17X\x01\x00\x00\x00xq\x18X\x01\x00\x00\x00yq\x19X\x01\x00\x00\x00zq\x1aX\x01\x00\x00\x00Aq\x1bX\x01\x00\x00\x00Bq\x1cX\x01\x00\x00\x00Cq\x1dX\x01\x00\x00\x00Dq\x1eX\x01\x00\x00\x00Eq\x1fX\x01\x00\x00\x00Fq X\x01\x00\x00\x00Gq!X\x01\x00\x00\x00Hq"X\x01\x00\x00\x00Iq#X\x01\x00\x00\x00Jq$X\x01\x00\x00\x00Kq%X\x01\x00\x00\x00Lq&X\x01\x00\x00\x00Mq\'X\x01\x00\x00\x00Nq(X\x01\x00\x00\x00Oq)X\x01\x00\x00\x00Pq*X\x01\x00\x00\x00Qq+X\x01\x00\x00\x00Rq,X\x01\x00\x00\x00Sq-X\x01\x00\x00\x00Tq.X\x01\x00\x00\x00Uq/X\x01\x00\x00\x00Vq0X\x01\x00\x00\x00Wq1X\x01\x00\x00\x00Xq2X\x01\x00\x00\x00Yq3X\x01\x00\x00\x00Zq4e.'
>>> OL = None
>>> print(OL)
None
>>> with open('mypicklefile', 'rb') as f1:
    OL = pickle.load(f1)
>>> type(OL)
<class 'list'>
>>> print(OL)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> L == OL
True
>>>

Dans l'exemple ci-dessus, j'ai créé une liste "L" contenant toutes les lettres de l'alphabet.
J'ai sauvegardé mon objet "L" (liste Python) dans un fichier "mypicklefile" grâce à la méthode dump du module pickle.
Précision importante, le module pickle écrit les données uniquement dans un fichier ouvert en mode binaire.
J'ai ouvert le fichier et afficher son contenu pour bien montrer que pickle écrit les données au format binaire.
J'ai créé un nouvel objet "OL" ayant None comme valeur (cette étape n'est pas obligatoire - uniquement pour montrer que l'objet "OL" n'existait pas auparavant).
J'ai ensuite chargé le contenu du fichier "mypicklefile" dans mon objet "OL" grâce à la méthode load du module pickle.
J'affiche le contenu de la nouvelle liste "OL" et le test d'égalité de l'objet "L" et "OL" pour bien montrer que les deux objets sont bien identiques.

La liste "OL" peut être modifiée (ajout, modification, suppression des valeurs) et à nouveau sauvegardée dans le fichier grâce à la méthode dump du module pickle pour une prochaine utilisation.

Petite précision, pour la méthode dump, le fichier doit être ouvert en mode 'wb' afin d'écraser le contenu précédent.
Si le fichier est ouvert en mode 'ab', les données sont écrites à la fin du fichier mais la méthode load récupère les données au début du fichier.
De toute manière, le mode append n'a aucun intérêt pour ce genre de stockage de données. On dump et on load l'intégralité du contenu d'un objet.

Et ça fonctionne pour tous types d'objets

Avec un tuple

>>> import pickle
>>> T = tuple(string.ascii_letters)
>>> T
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(T, f1)
>>> with open('mypicklefile', 'rb') as f1:
    OT = pickle.load(f1)
>>> OT
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
>>>

Avec un dictionnaire

>>> import pickle
>>> I = list(range(52))
>>> L = list(string.ascii_letters)
>>> D = {i: l for i, l in zip(I, L)}
>>> D
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(D, f1)
>>> with open('mypicklefile', 'rb') as f1:
    OD = pickle.load(f1)
>>> OD
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>>

Avec un deque (du module collections)

>>> import pickle
>>> from collections import deque
>>> L = list(string.ascii_letters)
>>> L
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> DE = deque(L)
>>> DE
deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'])
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(DE, f1)
>>> with open('mypicklefile', 'rb') as f1:
    ODE = pickle.load(f1)
>>> ODE
deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'])
>>>

Précision importante, la méthode load importe automatiquement l'objet nécessaire au chargement des données.
C'est à dire que dans l'exemple ci-dessus, l'objet deque du module collections n'a pas besoin d'être importé pour être correctement chargé via la méthode load. Il doit bien évidement être disponible dans la liste des modules de Python.

Et enfin, avec un objet perso

>>> class MyObject():
    def __init__(self):
        self.un = 1
        self.deux = 2
        self.trois = 3
    def __repr__(self):
        return "{}, {}, {}".format(self.un, self.deux, self.trois)
    def __str__(self):
        return "un: {}\ndeux: {}\ntrois: {}".format(self.un, self.deux, self.trois)

    
>>> A = MyObject()
>>> A
1, 2, 3
>>> print(A)
un: 1
deux: 2
trois: 3
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(A, f1)
>>> with open('mypicklefile', 'rb') as f1:
    B = pickle.load(f1)
>>> B
1, 2, 3
>>> print(B)
un: 1
deux: 2
trois: 3
>>>

Dans ce cas, la class MyObject() doit être disponible pour pouvoir être utilisée via la méthode load sinon, une erreur "AttributeError" est levée.

Associé au module tempfile, nous avons un système complet de sauvegarde temporaire d'objets utilisable dans n'importe quel script Python.

Etiquettes: 

Python: Le module requests

Aujourd'hui, le web a prit une place importante dans notre vie de tous les jours.

Que ce soit pour lire ses mails, lire les actualités, consulter la météo, se documenter, jouer, bref la liste est longue et non exhaustive.

Python permet de faire différents types de requêtes sur le web.
Pour ce faire, Python propose le module urllib3.
Ce module est très complet et par conséquent un peu complexe à utiliser.

Le module requests, qui utilise toute la puissance du module urllib3 est beaucoup plus simple d'utilisation et énormément utilisé pour interagir avec le web.

Comme un petit exemple vaut mieux qu'un long discours...

>>> import requests
>>> html = requests.get('https://www.python.org/downloads/')
>>> html.status_code
200
>>> html.url
'https://www.python.org/downloads/'
>>> html.raw
<urllib3.response.HTTPResponse object at 0x0B80D6D0>
>>> html.encoding
'utf-8'
>>> html.headers
{'Server': 'nginx', 'Content-Type': 'text/html; charset=utf-8', 'X-Frame-Options': 'DENY', 'Cache-Control': 'max-age=604800, public', 'Via': '1.1 vegur, 1.1 varnish, 1.1 varnish', 'Content-Length': '113054', 'Accept-Ranges': 'bytes', 'Date': 'Thu, 06 Jun 2019 13:08:10 GMT', 'Age': '132076', 'Connection': 'keep-alive', 'X-Served-By': 'cache-iad2130-IAD, cache-cdg20735-CDG', 'X-Cache': 'HIT, HIT', 'X-Cache-Hits': '1, 5', 'X-Timer': 'S1559826490.039199,VS0,VE1', 'Strict-Transport-Security': 'max-age=63072000; includeSubDomains'}
>>> html.is_redirect
False

Voici donc un exemple d'une requête "GET" et des différentes informations que nous obtenons (dans l'ordre):
- Le statut de la requête (ici 200, indiquant que la requête s'est bien déroulée)
- L'url requêtée
- Le format brut de notre requête (on voit qu'il s'agit d'un objet HTTPResponse du module urllib3)
- De l'encodage utilisé (ici, UTF-8)
- Tout le contenu de l'en-tête
- Si la requête effectuée est une redirection

Mais une des choses les plus importantes est quand même le contenu de la requête....

>>> len(html.content)
113054
>>> html.content[:500]
b'<!doctype html>\n<!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->\n<!--[if IE 7]>      <html class="no-js ie7 lt-ie8 lt-ie9">          <![endif]-->\n<!--[if IE 8]>      <html class="no-js ie8 lt-ie9">                 <![endif]-->\n<!--[if gt IE 8]><!--><html class="no-js" lang="en" dir="ltr">  <!--<![endif]-->\n\n<head>\n    <meta charset="utf-8">\n    <meta http-equiv="X-UA-Compatible" content="IE=edge">\n\n    <link rel="prefetch" href="//ajax.googleapis.com/ajax/libs/jqu'
>>> html.content[-500:]
b'ncludes.js"></script>\n\n    <script type="text/javascript" src="/static/js/main-min.fbfe252506ae.js" charset="utf-8"></script>\n    \n\n    <!--[if lte IE 7]>\n    <script type="text/javascript" src="/static/js/plugins/IE8-min.16868e6a5d2f.js" charset="utf-8"></script>\n    \n    \n    <![endif]-->\n\n    <!--[if lte IE 8]>\n    <script type="text/javascript" src="/static/js/plugins/getComputedStyle-min.c3860be1d290.js" charset="utf-8"></script>\n    \n    \n    <![endif]-->\n\n    \n\n    \n    \n\n</body>\n</html>\n'

J'affiche les 500 premiers/derniers caractères du contenu de la requête qui en contient au total 113054.

Nous voilà donc avec tout le contenu de la page https://www.python.org/downloads/.

Si les données de la requête sont au format JSON, très courant dans le monde des APIs et des WebServices.

L'objet retourné par request contient une méthode json qui permet de formater le contenu en JSON si celui-ci est reconnu comme tel.

Par exemple, avec la liste des codes postaux...

>>> req = requests.get('https://unpkg.com/codes-postaux@3.2.0/codes-postaux.json')
>>> req.status_code
200
>>> json = req.json()
>>> type(json)
<class 'list'>
>>> len(json)
35728
>>> pprint(json[:5])
[{'codeCommune': '01001',
  'codePostal': '01400',
  'libelleAcheminement': "L'ABERGEMENT-CLEMENCIAT",
  'nomCommune': "L'Abergement-Clémenciat"},
 {'codeCommune': '01002',
  'codePostal': '01640',
  'libelleAcheminement': 'ABERGEMENT-DE-VAREY (L )',
  'nomCommune': "L'Abergement-de-Varey"},
 {'codeCommune': '01004',
  'codePostal': '01500',
  'libelleAcheminement': 'AMBERIEU-EN-BUGEY',
  'nomCommune': 'Ambérieu-en-Bugey'},
 {'codeCommune': '01005',
  'codePostal': '01330',
  'libelleAcheminement': 'AMBERIEUX-EN-DOMBES',
  'nomCommune': 'Ambérieux-en-Dombes'},
 {'codeCommune': '01006',
  'codePostal': '01300',
  'libelleAcheminement': 'AMBLEON',
  'nomCommune': 'Ambléon'}]

Quoi de plus simple.

Si une authentification HTTP basique est nécessaire, il est possible de passer le nom d'utilisateur et le mot de passe lors de l'exécution de la requête.

>>> req = requests.get('https://unpkg.com/codes-postaux@3.2.0/codes-postaux.json', auth=('username','password'))
>>> req.status_code
200
>>> req.request.headers
{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Basic dXNlcm5hbWU6cGFzc3dvcmQ='}
>>> import base64
>>> base64.b64decode(req.request.headers.get('Authorization').split(' ')[1])
b'username:password'

En examinant l'en-tête de la requête, on s'aperçoit qu'une clé 'Authorization' a été ajouté, que le type est 'Basic' et le contenu, nom d'utilisateur et mot de passe, sont encodés en base64.

Si on décode le contenu à l'aide du module base64, on récupère les informations d'authentification.

D'autres méthodes d'authentification sont diponibles, tout est expliqué ici.

Pour finir, il est également possible d'utiliser une session pour effectuer plusieurs requêtes sur un site, ce qui permet de conserver les informations d'identification et les cookies pendant l'exécution de toutes les requêtes.

>>> sess = requests.Session()
>>> req = sess.get('https://unpkg.com/codes-postaux@3.2.0/codes-postaux.json')
>>> req.status_code
200

A vous de jouer.

Etiquettes: 

Python: Le module tempfile

Besoin d'un fichier temporaire pour y stocker provisoirement des données.

Nul besoin de se prendre la tête en créant un fichier de toute pièce où il faudra penser au répertoire de stockage, au nom du fichier, à sa suppression etc etc ...

Le module tempfile, disponible sous Python 2 et 3 permet de gérer des fichiers temporaires de leurs créations à leurs suppressions.

Fait partie des modules à utiliser sans modération.

Exemple:

>>> import tempfile
# Je créé un nouveau fichier temporaire
>>> mytmpfile = tempfile.NamedTemporaryFile()
# je peux afficher son nom
>>> mytmpfile.name
'/tmp/tmp00plrd7l'
# je peux y écrire du contenu très facilement
>>> mytmpfile.file.write(b'the first test')
>>> mytmpfile.file.write(b'the second test')
# tant que la méthode flush ou close (de la sous-classe file) n'a pas été exécutée, le contenu est stocké en mémoire
>>> with open(mytmpfile.name, 'rb') as f1:
...     f1.read()
...
...
b''
# J'enregistre le contenu dans le fichier
>>> mytmpfile.file.flush()
# je vérifie que le contenu a bien été enregistré (pour info)
>>> with open(mytmpfile.name, 'rb') as f1:
...     f1.read()
...
...
b'the first testthe second test'
# ATTENTION, la méthode flush (de la sous-classe file) permet d'enregistrer dans le fichier tout le contenu écrit avec la méthode write (entre deux flush)
# Il est donc possible de rajouter du contenu si nécessaire.
# La méthode close (de la sous-classe file) permet d'enregistrer dans le fichier tout le contenu écrit avec la méthode write et ferme le fichier.
# Il n'est donc plus possible de rajouter du contenu.
# Tant que le script est exécuté et que la méthode close de la classe tempfile n'est pas exécutée, le fichier temporaire est existant sur le disque.
# La commande suivante supprime définitivement le fichier sur le disque.
>>> mytmpfile.close()

Un tas d'options est disponible mais celles par défaut sont amplement suffisantes.

Python: Mettre à jour tous les paquets très simplement

Voici une astuce qui permet de maintenir à jour ses paquets Python sans trop se prendre la tête.

Premièrement, générer le fichier des paquets installés

# python -m pip freeze > requirement
# head requirement
aiofiles==0.6.0
certifi==2020.12.5
chardet==3.0.4
click==7.1.2
gunicorn==20.0.4
h11==0.11.0
httptools==0.1.1
idna==2.10
Jinja2==2.11.2
MarkupSafe==1.1.1

Deuxièmement, créer un fichier de mise à jour à partir du fichier requirement

# sed 's/==/>=/g' requirement > upgrade_requirement
# head upgrade_requirement
aiofiles>=0.6.0
certifi>=2020.12.5
chardet>=3.0.4
click>=7.1.2
gunicorn>=20.0.4
h11>=0.11.0
httptools>=0.1.1
idna>=2.10
Jinja2>=2.11.2
MarkupSafe>=1.1.1

Et enfin, lancer la mise à jour des paquets

# python -m pip install -U -r upgrade_requirement

Et voilà, c'est fini

 

Etiquettes: 

Python: Modifier la valeur d'une balise d'un XML en fonction de la valeur de son attribut

Voici un petit exemple qui permet de modifier la valeur d'une balise en fonction de son attribut et de sa valeur.

XML d'origine:

<xml>
    <ma_balise mon_attribut="ok">valeur1</ma_balise>
    <ma_balise mon_attribut="ko">valeur2</ma_balise>
    <ma_balise>valeur3</ma_balise>
    <ma_balise mon_attribut="ok">valeur4</ma_balise>
    <ma_balise>valeur5</ma_balise>
</xml>

Script Python:

>>> from lxml import etree
>>> xml = etree.parse('monfichier.xml')
>>> for ma_balise in xml.getchildren():
    if 'mon_attribut' in ma_balise.attrib and ma_balise.attrib['mon_attribut'] == 'ok':
        ma_balise.text = 'nouvelle valeur'
        # Si besoin d'une section CDATA
        # ma_balise.text = etree.CDATA('nouvelle valeur')
                
>>> with open('monfichier.xml', 'w') as f1:
    f1.write(etree.tounicode(xml))

>>>

XML après traitement:

<xml>
    <ma_balise mon_attribut="ok">nouvelle valeur</ma_balise>
    <ma_balise mon_attribut="ko">valeur2</ma_balise>
    <ma_balise>valeur3</ma_balise>
    <ma_balise mon_attribut="ok">nouvelle valeur</ma_balise>
    <ma_balise>valeur5</ma_balise>
</xml>

Seules les balises ayant l'attribut 'mon_attribut' et sa valeur égale à 'ok' ont été modifiées.

Python: Modifier le timezone d'un objet datetime

Un évènement en Martinique le 26 novembre 2020 à 12:42 (heure locale of course), ça fait quelle heure en Métropole ?

>>> from datetime import datetime
>>> from dateutil import tz

>>> MTQ = tz.gettz('America/Martinique')
>>> dt1 = datetime(2020, 11, 26, 12, 42, 0, tzinfo=MTQ)
>>> print(dt1)
2020-11-26 12:42:00-04:00
>>> repr(dt1)
"datetime.datetime(2020, 11, 26, 12, 42, tzinfo=tzfile('America/Martinique'))"
>>> FRA = tz.gettz('Europe/Paris')
>>> dt2 = dt1.astimezone(tz=FRA)
>>> print(dt2)
2020-11-26 17:42:00+01:00
>>> repr(dt2)
"datetime.datetime(2020, 11, 26, 17, 42, tzinfo=tzfile('Europe/Paris'))"

ça fait 17:42 heure de Paris (le même jour)

Un autre exemple:

>>> dt1 = datetime(2020, 11, 26, 20, 45, 0, tzinfo=MTQ)
>>> dt2 = dt1.astimezone(tz=FRA)
>>> print(dt1)
2020-11-26 20:45:00-04:00
>>> print(dt2)
2020-11-27 01:45:00+01:00

Le 26 novembre 2020 20:45 heure de Martinique, correspond au 27 novembre 2020 01:45 heure de Paris.

 

Python: MySQL

Des requêtes MySQL dans des scripts Python

 

Pré requis:

$ apt-get install python-mysqldb

Requête simple (select)

#!/usr/bin/env python
# -*- coding: utf-8 -*

# on importe le module MySQLdb
import MySQLdb

# On créé un dictionnaire contenant les paramètres de connexion MySQL
paramMysql = {
    'host'   : 'localhost',
    'user'   : 'username',
    'passwd' : 'password',
    'db'     : 'mabase'
}
# Bien respecter les noms des paramètres (host, user, passwd, db)

sql = """\
SELECT * FROM matable
WHERE monchamp1 = 'valeur1'
"""

try:
    # On  créé une conexion MySQL
    conn = MySQLdb.connect(**paramMysql)
    # On créé un curseur MySQL
    cur = conn.cursor(MySQLdb.cursors.DictCursor)
    # On exécute la requête SQL
    cur.execute(sql)
    # On récupère toutes les lignes du résultat de la requête
    rows = cur.fetchall()
    # On parcourt toutes les lignes
    for row in rows:
        # Pour récupérer les différentes valeurs des différents champs
        valeur1 = row['monchamp1']
        valeur2 = row['monchamp2']
        valeur3 = row['monchamp3']
        # etc etc ...

except MySQLdb.Error, e:
    # En cas d'anomalie
    print "Error %d: %s" % (e.args[0],e.args[1])
    sys.exit(1)

finally:
    # On ferme la connexion
    if conn:
        conn.close()

Requête insert, update, delete

#!/usr/bin/env python
# -*- coding: utf-8 -*

# on importe le module MySQLdb
import MySQLdb

# On créé un dictionnaire contenant les paramètres de connexion MySQL
paramMysql = {
    'host'   : 'localhost',
    'user'   : 'username',
    'passwd' : 'password',
    'db'     : 'mabase'
}
# Bien respecter les noms des paramètres (host, user, passwd, db)

sql = """\
INSERT INTO matable
(champ1, champ2, champ3)
VALUES ('valeur1', 'valeur2', 'valeur3')
"""

try:
    # On  créé une conexion MySQL
    conn = MySQLdb.connect(**paramMysql)
    # On créé un curseur MySQL
    cur = conn.cursor()
    try:
        # On exécute la requête SQL
        cur.execute(sql)
        # On commit
        conn.commit()
    except MySQLdb.Error, e:
        # En cas d'erreur on annule les modifications
        conn.rollback()

except MySQLdb.Error, e:
    # En cas d'anomalie
    print "Error %d: %s" % (e.args[0],e.args[1])
    sys.exit(1)

finally:
    # On ferme la connexion
    if conn:
        conn.close()

Python: PIP

Différentes manières d'installer pip pour Python 2 et 3:

Pré-requis:

$ apt-get install build-essential python-dev python3-dev

Pour python 2 (3 méthodes):

$ apt-get install python-pip

$ apt-get install python-setuptools
$ easy_install pip

$ curl https://bootstrap.pypa.io/get-pip.py | python

Pour python 3 (3 méthodes):

$ apt-get install python3-pip

$ apt-get install python3-setuptools
$ easy_install3 pip

$ curl https://bootstrap.pypa.io/get-pip.py | python3

Petite astuce pour ne plus être embêté avec pip quand on a plusieurs version de python installée.

Cette astuce fonctionne avec toutes les versions de python.

Remplacer le x par la version désirée

Lister tous les paquets obsolètes:

$ pythonx -m pip list -o

Installer un paquet:

$ pythonx -m pip install --upgrade mysqlclient

Installer une version spécifique d'un paquet:

$ pythonx -m pip install --upgrade mysqlclient==1.3.14

Python: Pandas DataFrame groupby resample

Comment analyser des données en les regroupant par les valeurs d'une colonne et en les ré-échantillonant, via une colonne "date", à chaque fin de mois.

J'ai un DataFrame Pandas "df1" avec les données suivantes:

>>> df1
      DATE        USER     DUREE
0     2020-09-03  USER#1   0.50
1     2020-09-02  USER#1   0.00
2     2020-09-02  USER#1   0.25
3     2020-09-01  USER#1   0.00
4     2020-09-01  USER#1   0.25
          ...     ...    ...
10715 2017-08-01  USER#2   0.75
10716 2017-07-19  USER#2   0.00
10717 2017-07-19  USER#2   0.00
10718 2017-07-17  USER#2   0.25
10719 2017-07-17  USER#2   0.00
[10720 rows x 3 columns]

J'ai donc 3 colonnes, une colonne "DATE", une colonne "USER" et une colonne "DUREE" avec les formats suivants:

>>> df1.dtypes
DATE     datetime64[ns]
USER             object
DUREE           float64
dtype: object

J'aimerais donc connaitre le cumul du temps passé par chaque "USER" à chaque fin de mois et obtenir le résultat suivant:

USER        USER#1  USER#2
DATE                     
2011-04-30   76.25    0.00
2011-05-31  109.75    0.00
2011-06-30   74.00    0.00
2011-07-31   31.25    0.00
2011-08-31   83.75    0.00
            ...     ...
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50
[114 rows x 2 columns]

Ce résultat peut-être obtenu en utilisant tout simplement 5 méthodes disponibles pour les DataFrame.

Premièrement, je ré-index mon DataFrame en utilisant la colonne "DATE" - Indispensable pour le ré-échantillonage

>>> df1.set_index('DATE')
              USER  DUREE
DATE                    
2020-09-03  USER#1   0.50
2020-09-02  USER#1   0.00
2020-09-02  USER#1   0.25
2020-09-01  USER#1   0.00
2020-09-01  USER#1   0.25
            ...    ...
2017-08-01  USER#2   0.75
2017-07-19  USER#2   0.00
2017-07-19  USER#2   0.00
2017-07-17  USER#2   0.25
2017-07-17  USER#2   0.00
[10720 rows x 2 columns]

Ensuite, j'effectue mon regroupement sur la colonne "USER"

>>> df1.set_index('DATE').groupby('USER')
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000151789EDEB0>

J'obtiens donc un objet "DataFrameGroupBy"

Pour le ré-échantillonage, j'utilise la méthode "resample" qui va agir sur les données contenues dans mon index (par défaut).
Le paramètre "M" va ré-échantilloner mes dates à chaque fin de mois.

>>> df1.set_index('DATE').groupby('USER').resample('M')
<pandas.core.resample.DatetimeIndexResamplerGroupby object at 0x000001517888F3A0>

J'obtiens donc un objet "DatetimeIndexResamplerGroupby"

Je fais maintenant la somme des valeurs de ma colonne "DUREE"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum()
USER    DATE     
USER#1  2011-04-30     76.25
        2011-05-31    109.75
        2011-06-30     74.00
        2011-07-31     31.25
        2011-08-31     83.75
                       ... 
USER#2  2020-05-31     17.50
        2020-06-30     18.25
        2020-07-31     38.00
        2020-08-31      5.25
        2020-09-30      4.50
Name: DUREE, Length: 153, dtype: float64

Pour finir, je souhaite transposer le premier niveau "USER" de mon index en colonne.
Pour cela j'utilise la méthode "unstack" avec le paramètre "level=0" pour lui indiquer le premier niveau de mon index et je lui demande de remplacer les valeurs nulles par "0" grâce à "fill_value=0"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0)
USER        USER#1  USER#2
DATE                     
2011-04-30   76.25    0.00
2011-05-31  109.75    0.00
2011-06-30   74.00    0.00
2011-07-31   31.25    0.00
2011-08-31   83.75    0.00
            ...     ...
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50
[114 rows x 2 columns]

Et voilà, mes données ont été agrégées par "USER" et ré-échantillonées à chaque fin de mois.

Il est possible de filtrer les données par année

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).loc['2020']
USER        USER#1  USER#2
DATE                     
2020-01-31   25.50   50.50
2020-02-29   35.25   15.25
2020-03-31   33.25   26.75
2020-04-30    6.50    1.25
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50

Ou de calculer le total par "USER"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).sum()
USER
USER#1    8219.5
USER#2    2432.5
dtype: float64

Ou de calculer le total par "USER" pour une année précise

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).loc['2020'].sum()
USER
USER#1    233.00
USER#2    177.25
dtype: float64

Simple comme bonjour.

Merci Pandas

Python: Parser et indenter un flux XML

Parser un flux XML afin de le valider, extraire des données et l'afficher à l'écran avec une bonne indentation, tout ceci est possible grâce au module xml.dom.minidom.

Exemple avec Python3 et le flux XML suivant:

>>> flux = '<?xml version="1.0" encoding="UTF-8" standalone="no"?><geonames style="MEDIUM"><totalResultsCount>5</totalResultsCount><geoname><toponymName>Republic of Austria</toponymName><name>Austria</name><lat>47.33333</lat><lng>13.33333</lng><geonameId>2782113</geonameId><countryCode>AT</countryCode><countryName>Austria</countryName><fcl>A</fcl><fcode>PCLI</fcode></geoname><geoname><toponymName>Republic of France</toponymName><name>France</name><lat>46</lat><lng>2</lng><geonameId>3017382</geonameId><countryCode>FR</countryCode><countryName>France</countryName><fcl>A</fcl><fcode>PCLI</fcode></geoname><geoname><toponymName>Federal Republic of Germany</toponymName><name>Germany</name><lat>51.5</lat><lng>10.5</lng><geonameId>2921044</geonameId><countryCode>DE</countryCode><countryName>Germany</countryName><fcl>A</fcl><fcode>PCLI</fcode></geoname><geoname><toponymName>Repubblica Italiana</toponymName><name>Italy</name><lat>42.83333</lat><lng>12.83333</lng><geonameId>3175395</geonameId><countryCode>IT</countryCode><countryName>Italy</countryName><fcl>A</fcl><fcode>PCLI</fcode></geoname><geoname><toponymName>Principality of Liechtenstein</toponymName><name>Liechtenstein</name><lat>47.16667</lat><lng>9.53333</lng><geonameId>3042058</geonameId><countryCode>LI</countryCode><countryName>Liechtenstein</countryName><fcl>A</fcl><fcode>PCLI</fcode></geoname></geonames>'
>>> from xml.dom.minidom import parseString
>>> from pprint import pprint
>>> # La commande suivante permet de créer le parseur et de valider le flux XML par la même occasion
>>> parser = parseString(flux)
>>> # La commande suivante permet d'afficher le flux XML correctement indenté
>>> print(parser.toprettyxml())
<?xml version="1.0" ?>
<geonames style="MEDIUM">
    <totalResultsCount>5</totalResultsCount>
    <geoname>
        <toponymName>Republic of Austria</toponymName>
        <name>Austria</name>
        <lat>47.33333</lat>
        <lng>13.33333</lng>
        <geonameId>2782113</geonameId>
        <countryCode>AT</countryCode>
        <countryName>Austria</countryName>
        <fcl>A</fcl>
        <fcode>PCLI</fcode>
    </geoname>
    <geoname>
        <toponymName>Republic of France</toponymName>
        <name>France</name>
        <lat>46</lat>
        <lng>2</lng>
        <geonameId>3017382</geonameId>
        <countryCode>FR</countryCode>
        <countryName>France</countryName>
        <fcl>A</fcl>
        <fcode>PCLI</fcode>
    </geoname>
    <geoname>
        <toponymName>Federal Republic of Germany</toponymName>
        <name>Germany</name>
        <lat>51.5</lat>
        <lng>10.5</lng>
        <geonameId>2921044</geonameId>
        <countryCode>DE</countryCode>
        <countryName>Germany</countryName>
        <fcl>A</fcl>
        <fcode>PCLI</fcode>
    </geoname>
    <geoname>
        <toponymName>Repubblica Italiana</toponymName>
        <name>Italy</name>
        <lat>42.83333</lat>
        <lng>12.83333</lng>
        <geonameId>3175395</geonameId>
        <countryCode>IT</countryCode>
        <countryName>Italy</countryName>
        <fcl>A</fcl>
        <fcode>PCLI</fcode>
    </geoname>
    <geoname>
        <toponymName>Principality of Liechtenstein</toponymName>
        <name>Liechtenstein</name>
        <lat>47.16667</lat>
        <lng>9.53333</lng>
        <geonameId>3042058</geonameId>
        <countryCode>LI</countryCode>
        <countryName>Liechtenstein</countryName>
        <fcl>A</fcl>
        <fcode>PCLI</fcode>
    </geoname>
</geonames>

>>> # On peut également en profiter pour écrire le contenu dans un fichier et correctement indenté
>>> with open('monFlux.xml', 'w') as f:
    f.write(parser.toprettyxml())

    
1494
>>> # Afficher le nombre total d'éléments (correspond à la valeur de la balise totalResultsCount)
>>> print(parser.getElementsByTagName('totalResultsCount')[0].firstChild.data)
5
>>> # La commande suivante permet d'extraire les données d'un tag précis
>>> # et de les sauvegarder dans un dictionnaire
>>> datas = dict()
>>> geonames = parser.getElementsByTagName('geoname')
>>> for geoname in geonames:
    toponymName = geoname.getElementsByTagName('toponymName')[0].firstChild.data
    name = geoname.getElementsByTagName('name')[0].firstChild.data
    lat = geoname.getElementsByTagName('lat')[0].firstChild.data
    lng = geoname.getElementsByTagName('lng')[0].firstChild.data
    geonameid = geoname.getElementsByTagName('geonameId')[0].firstChild.data
    countrycode = geoname.getElementsByTagName('countryCode')[0].firstChild.data
    countryname = geoname.getElementsByTagName('countryName')[0].firstChild.data
    fcl = geoname.getElementsByTagName('fcl')[0].firstChild.data
    fcode = geoname.getElementsByTagName('fcode')[0].firstChild.data
    datas[geonameid] = {'toponymName': toponymName, 'name': name, 'lat': lat, 'lng': lng, 'countryCode': countrycode, 'countryName': countryname, 'fcl': fcl, 'fcode': fcode}

    
>>> pprint(datas)
{'2782113': {'countryCode': 'AT',
             'countryName': 'Austria',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.33333',
             'lng': '13.33333',
             'name': 'Austria',
             'toponymName': 'Republic of Austria'},
 '2921044': {'countryCode': 'DE',
             'countryName': 'Germany',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '51.5',
             'lng': '10.5',
             'name': 'Germany',
             'toponymName': 'Federal Republic of Germany'},
 '3017382': {'countryCode': 'FR',
             'countryName': 'France',
             
'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '46',
             'lng': '2',
             'name': 'France',
             'toponymName': 'Republic of France'},
 '3042058': {'countryCode': 'LI',
             'countryName': 'Liechtenstein',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '47.16667',
             'lng': '9.53333',
             'name': 'Liechtenstein',
             'toponymName': 'Principality of Liechtenstein'},
 '3175395': {'countryCode': 'IT',
             'countryName': 'Italy',
             'fcl': 'A',
             'fcode': 'PCLI',
             'lat': '42.83333',
             'lng': '12.83333',
             'name': 'Italy',
             'toponymName': 'Repubblica Italiana'}}
>>>

C'est assez simple dans l'ensemble.

Python: Rechercher tous les anagrammes dans un texte

Selon wikipédia, une anagramme est un mot ou une expression obtenu en permutant les lettres d'un mot ou d'une expression de départ.

Pour l'exemple, je vais rechercher tous les anagrammes présents dans l'oeuvre de Jules Verne, "Le Tour Du Monde En 80 Jours".

Première étape, récupérer l'oeuvre au format txt.
Le site gutenberg permet de faire ce genre de choses.

Le module requests permet d'exécuter des requêtes HTTP

$ python3 -m pip install --upgrade requests
>>> import requests
>>> r1 = requests.get('https://www.gutenberg.org/files/800/800-8.txt')

Récupération du texte dans la réponse de la requête et nettoyage de tous les caractères non indispensables.
Le texte commence au 3 941 ème caractères et se termine au 431 252 ème caractères.
Tous les accents sont supprimés.
Tout le texte est converti en minuscule.
La regex recherche tous les mots de 3 caractères et plus.
Tous les mots en double sont supprimés.

Pour la suppression des caractères accentués, j'utilise le module removeaccents

$ python3 -m pip install --upgrade removeaccents
>>> from removeaccents.removeaccents import remove_accents
>>> text = r1.text[3941:431252]
>>> text = remove_accents(text)
>>> text = text.lower()
>>> words = re.findall(r'\b[A-Za-z]{3,}\b', text)
>>> words = list(set(words))

Nous nous retrouvons avec une liste de 8426 mots de 3 caractères et plus.

>>> len(words)
8426

Nous allons maintenant indexer tous les anagrammes dans un dictionnaire.

>>> anagrammes = dict()
>>> for word in words:
    letters = ''.join(sorted(word))
    if letters in anagrammes:
        anagrammes.get(letters).append(word)
    else:
        anagrammes[letters] = [word]
>>> anagrammes = {k: v for k, v in anagrammes.items() if len(v) > 1}

Nous obtenons une liste de 312 anagrammes

>>> len(anagrammes)
312

Voici la liste des 30 premiers anagrammes triée par ordre décroissant du nombre de mots.

>>> print(sorted(anagrammes.items(), key=lambda x: len(x[1]), reverse=True)[:30])
[('aerstu', ['sauter', 'autres', 'surate']), ('eeprst', ['pertes', 'pester', 'pretes']), ('eeelrv', ['releve', 'revele', 'elever']), ('aeenprst', ['esperant', 'presenta', 'separent']), ('aeiprt', ['partie', 'pirate', 'patrie']), ('efmnort', ['froment', 'forment', 'fremont']), ('aisv', ['vais', 'avis', 'visa']), ('aceinrt', ['crainte', 'ecriant', 'certain']), ('aceerss', ['sacrees', 'caresse', 'cessera']), ('aerrs', ['rares', 'raser', 'serra']), ('aeirstt', ['artiste, 'restait', 'attires']), ('eimprs', ['permis', 'mepris', 'primes']), ('eegnr', ['green', 'genre', 'gener']), ('adegr', ['garde', 'egard', 'grade']), ('eirsv', ['servi', 'viser', 'rives']), ('aeegilns', ['inegales', 'alignees', 'signalee']), ('aegnt', ['agent', 'geant', 'etang']), ('acert', ['ecart', 'trace', 'carte']), ('eeinrtt', ['interet', 'retenti', 'retient']), ('adegnrs', ['grandes', 'dangers', 'gardens']), ('eerrsv', ['verres', 'revers', 'verser']), ('aeirrtt', ['traiter', 'attirer', 'traitre']), ('acers', ['sacre', 'races', 'arecs']), ('ersu', ['user', 'rues', 'ruse']), ('cdeirt', ['decrit', 'credit', 'direct']), ('einru', ['nuire', 'ruine', 'reuni']), ('bceimno', ['combien', 'combine']), ('eirtv', ['vitre', 'revit']), ('aceimnoprt', ['importance', 'comprenait']), ('aceinrst', ['certains', 'craintes'])]

 

Python: Rediriger la sortie standard vers un fichier

En Python, comme en Bash, il est possible de rediriger tous les flux des commandes d'affichages (print, pprint, etc...) vers un fichier.

Voici un exemple tout simple:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from pprint import pprint
# We open the log file in writting mode
with open('myLogFile', 'w') as f:
    # We redirect the 'sys.stdout' command towards the descriptor file
    sys.stdout = f
    # Now, all the print commands write directly in the file
    print('This is a test.')
    D = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
    pprint(D)
    A = 'Hello, how are you ?\n'
    sys.stdout.write(A)

Quand on exécute le script:

# python3 testStdoutToFile.py
# cat myLogFile
This is a test.
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
Hello, how are you ?

Soo simple !

Python: Requests: appeler un webservice SOAP

Pour l'exemple je vais appeler un webservice SOAP qui me retourne le résultat d'une addition de deux entiers.

J'ai utilisé SoapUI afin de récupérer le format du fichier XML à utiliser.
Le WSDL du webservice SOAP est disponible à cette adresse http://www.dneonline.com/calculator.asmx?wsdl

Le WSDL contient toutes les informations nécessaires pour l'utilisation du webservice.

>>> import requests
>>> url = 'http://www.dneonline.com/calculator.asmx'
>>> xml = '''\
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/">
   <soap:Header/>
   <soap:Body>
      <tem:Add>
         <tem:intA>50</tem:intA>
         <tem:intB>20</tem:intB>
      </tem:Add>
   </soap:Body>
</soap:Envelope>'''
>>> headers = {'content-type': 'application/soap+xml; charset=utf-8'}
>>> r1 = requests.post(url, data=xml, headers=headers)
>>> print(r1.text)
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <AddResponse xmlns="http://tempuri.org/">
            <AddResult>70</AddResult>
        </AddResponse>
    </soap:Body>
</soap:Envelope>

Pour un appel de webservice SOAP, il est très important de modifier le content-type du header de la requête.

Parfois, il faut indiquer 'application/soap+xml; charset=utf-8' et d'autres fois 'text/xml; charset=utf-8'.

Ca dépend du webservice SOAP.
Si le content-type n'est pas correct, l'erreur sera renseignée dans la réponse.

Dans mon exemple, le résultat de l'addition est retourné dans la balise <AddResult>

Python: Rotation des logs Apache

Voici un petit script en Python qui permet de faire la rotation des fichiers de log d'un serveur Apache.

# -*- coding: UTF-8 -*-

"""
    logrotate.py
    ============
    
    Permet la rotation des logs Apache
    
    :Example:
    
    >>> import logrotate
    >>> logrotate.main()
"""

import os, shutil, zipfile
import datetime as dt

F = r'C:\wamp\logs'                                 # Répertoire des logs Apache
LOG = 'logrotate.log'                               # Fichier de log pour les rotations
TXT = 'ROTATION DU FICHIER'                         # Texte affiché dans le log des rotations
TXT2 = 'SUPPRESSION DU FICHIER'                     # Texte affiché dans le log des rotations
TXT3 = 'COMPRESSION DU FICHIER'                     # Texte affiché dans le log des rotations
NBARCHIVE = 20                                      # Nombre de fichier de log historisé .0 .1 .2 etc etc ...

def now():
    """
        Retourne la date et l'heure courante au format ISO 2018-06-28T10:30:23.816122
        
        :return: La date et heure courante
        :rtype:  datetime.datetime
    """
    return dt.datetime.now().isoformat()
    
def cleanList(LIST = [], LISTERR = []):
    """
        Supprime de la liste ``LIST`` tous les fichiers 
        correspondant à ceux en erreur présent dans la liste ``LISTERR``
        
        :return: Une nouvelle liste de fichiers nettoyées
        :rtype:  list
    """
    for ERR in LISTERR:
        LIST = list(filter(lambda f: not f.startswith(ERR), LIST))
    return LIST

def log(FLOG, TXT, FILE, MSG):
    """
        Ecrit dans le fichier ``FLOG`` les infos
        ``TXT``, ``FILE`` et ``MSG`` avec la date courante
    """
    print('{DATETIME} {T:<25s} {FILE:<40s} --> {MSG}'.format(DATETIME=now(), FILE=FILE, T=TXT, MSG=MSG), file=FLOG)

def delFile(FILE, FLOG):
    """
        Supprime le fichier ``FILE``
    """
    try: os.remove(FILE)
    except: log(FLOG, TXT2, FILE, 'KO')
    else: log(FLOG, TXT2, FILE, 'OK')

def listLog(LIST, EXT):
    """
        Extrait tous les fichiers de log avec l'extension ``EXT``
        de la liste ``LIST``

        :return: Une liste contenant uniquement les fichiers ayant l'extension ``EXT``
        :rtype:  list
    """
    return list(filter(lambda f: f.endswith(EXT), LIST))

def removeUnusedFiles(FLOG):
    """
        Supprime tous les fichiers qui ne sont plus concernés par des fichiers LOG
        Retourne une nouvelle liste avec les fichiers supprimés en moins

        :return: Une nouvelle liste avec les fichiers supprimés en moins
        :rtype:  list
    """
    LIST = os.listdir(F)
    LISTLOG = listLog(LIST, '.log')
    LISTDEL = list(filter(lambda x: '.'.join(x.split('.')[:2]) not in LISTLOG, LIST))
    for FILE in LISTDEL:
        delFile(os.path.join(F, FILE), FLOG)
    return list(set(LIST) - set(LISTDEL))

def incrementZipFile(LIST, FLOG, LISTERR = []):
    """
        Incrémente les archives ZIP jusqu'à ``NBARCHIVE``
        
        :return: Une liste de fichiers en erreur
        :rtype:  list
    """
    LISTLOG = listLog(LIST, '.log')
    LISTZIP = listLog(LIST, '.zip')
    for f in LISTLOG:
        WLIST = sorted(list(filter(lambda x: x.startswith(f), LISTZIP)), key=lambda y: int(y.split('.')[2]), reverse=True)
        ERR = False
        for FILE in WLIST:
            TF = FILE.split('.')
            IDX = int(TF[2])
            if IDX < NBARCHIVE:
                OLDFILE = os.path.join(F, FILE)
                TF[2] = str(IDX + 1)
                NEWFILE = os.path.join(F, '.'.join(TF))
                try:
                    with zipfile.ZipFile(OLDFILE, mode='r', compression=zipfile.ZIP_DEFLATED) as f1:
                        try:
                            CT = f1.read(os.path.splitext(FILE)[0])
                        except: ERR = True
                        else:
                            BASE = os.path.splitext(os.path.basename(NEWFILE))[0]
                            try:
                                with zipfile.ZipFile(NEWFILE, mode='w', compression=zipfile.ZIP_DEFLATED) as f2:
                                    f2.writestr(BASE, CT)
                            except: ERR = True
                            else: log(FLOG, TXT, OLDFILE, '{NEWFILE}'.format(NEWFILE=NEWFILE))
                except: ERR = True
                finally:
                    if ERR:
                        LISTERR.append(f)
                        log(FLOG, TXT, OLDFILE, '{NEWFILE} ***KO***'.format(NEWFILE=NEWFILE))
                        break
    return LISTERR

def log_0ToZip(LIST, FLOG, LISTERR = []):
    """
        On écrit tout le contenu des fichiers LOG *.log.0
        dans de nouveaux fichiers ZIP *.log.1.zip
        On supprime ensuite tous les fichiers *.log.0
        
        :return: Une liste de fichiers en erreur
        :rtype:  list
    """
    LISTLOG0 = listLog(LIST, '.log.0')
    for FILE in LISTLOG0:
        ERR = False
        with open(os.path.join(F, FILE), mode='r', encoding='UTF-8') as f1:
            BASE = FILE[:-1] + '1'
            ZIP = os.path.join(F, BASE + '.zip')
            with zipfile.ZipFile(ZIP, 'w', compression=zipfile.ZIP_DEFLATED) as f2:
                try: f2.writestr(BASE, f1.read().encode('UTF-8'))
                except: 
                    ERR = True
                    LISTERR.append(FILE[:-2])
                    log(FLOG, TXT3, os.path.join(F, FILE), '{NEWFILE} ***KO***'.format(NEWFILE=ZIP))
                else: log(FLOG, TXT3, os.path.join(F, FILE), '{NEWFILE}'.format(NEWFILE=ZIP))
        if not ERR: delFile(os.path.join(F, FILE), FLOG)
    return LISTERR

def logTo0(LIST, FLOG):
    """
        On écrit tout le contenu des fichiers LOG *.log 
        dans de nouveaux fichiers numérotés *.log.0
        On écrase ensuite le contenu des fichiers LOG
    """
    LISTLOG = listLog(LIST, '.log')
    for FILE in LISTLOG:
        OLDFILE = os.path.join(F, FILE)
        NEWFILE = os.path.join(F, FILE + '.0')
        try:
            with open(OLDFILE, mode='r', encoding='UTF-8') as f1:
                with open(NEWFILE, mode='w', encoding='UTF-8') as f2:
                    f2.write(f1.read())
        except: log(FLOG, TXT, OLDFILE, '{NEWFILE} ***KO***'.format(NEWFILE=NEWFILE))
        else: 
            log(FLOG, TXT, OLDFILE, '{NEWFILE}'.format(NEWFILE=NEWFILE))
            try:
                with open(OLDFILE, mode='w', encoding='UTF-8') as f1:
                    f1.write('')
            except: log(FLOG, TXT2, OLDFILE, 'KO')
            else: log(FLOG, TXT2, OLDFILE, 'OK')

def main():
    """
        Fonction principale
        
        Pour chaque fichier de log (*.log) trouvé dans le dossier (variable ``F``)
        Chaque niveau d'archive est incrémenté de 1 dans la limite du nombre d'archive
        indiqué dans la variable ``NBARCHIVE``
    """
    with open(os.path.join(F, LOG), mode='a', encoding='UTF-8') as FLOG:
        LIST = removeUnusedFiles(FLOG)
        LIST.remove(LOG) # On supprime de la liste le fichier de LOG du script
        LISTERR = incrementZipFile(LIST, FLOG)
        LIST = cleanList(LIST, LISTERR)
        LISTERR = log_0ToZip(LIST, FLOG, LISTERR)
        LIST = cleanList(LIST, LISTERR)
        logTo0(LIST, FLOG)

if __name__ == '__main__':
    main()

Et pour l'exécuter (après avoir adapté les variables F, LOG, NBARCHIVE):

$ python3 logrotate.py

Fonctionne sous Linux et Windows

Une tâche cron sous Linux ou une tâche planifiée sous Windows permet d'automatiser l'exécution du script toutes les semaines par exemple.

Le script peut-être téléchargé via le lien ci-dessous

https://git.quennec.fr/ronan/scripts_pub/raw/master/logrotate.py

Python: SQLite: select avec le nom des colonnes

Par défaut, lors d'une requête select sur une table SQLite, les valeurs des colonnes sont indexées par numéro, la position de la colonne dans le résultat.

>>> import sqlite3
>>> from faker import Faker
>>> fake = Faker('fr_FR')
>>> con = sqlite3.connect(':memory:')
>>> cur = con.cursor()
>>> cur.execute('create table contact (id integer primary key, firstname text, lastname text, email text, mobile_phone text)')
>>> for x in range(100):
    cur.execute('insert into contact (firstname, lastname, email, mobile_phone) values (?, ?, ?, ?)', (fake.first_name(), fake.last_name(), fake.email(), fake.phone_number()))
>>> for row in cur.execute('select * from contact where firstname = ?', ('Pierre', )):
    print(row)

(38, 'Pierre', 'Collin', 'marc71@bigot.com', '+33 2 53 97 25 45')

>>> cur.execute('select * from contact where firstname = ?', ('Pierre', ))
>>> cur.fetchall()
[(38, 'Pierre', 'Collin', 'marc71@bigot.com', '+33 2 53 97 25 45')]

L'objet row retourné correspond à un tuple, par conséquent pour obtenir, par exemple, la valeur de l'adresse mail, il faut utiliser l'index correspondant, c'est à dire 3.

>>> for row in cur.execute('select * from contact where firstname = ?', ('Pierre', )):
    print(row[3])

marc71@bigot.com

Pour pouvoir utiliser le nom des colonnes, il faut obligatoirement paramétrer la connexion de cette manière:

>>> con.row_factory = sqlite3.Row
>>> cur = con.cursor()
>>> for row in cur.execute('select * from contact where firstname = ?', ('Pierre', )):
    drow = dict(row) # on convertit l'objet row en objet dict
    print(drow)

{'id': 38, 'firstname': 'Pierre', 'lastname': 'Collin', 'email': 'marc71@bigot.com', 'mobile_phone': '+33 2 53 97 25 45'}

>>> for row in cur.execute('select * from contact where firstname = ?', ('Pierre', )):
    # on peut accéder au noms de colonnes directement
    print(row['email'])

marc71@bigot.com

>>> cur.execute('select * from contact where firstname = ?', ('Pierre', ))
>>> list(map(dict, cur.fetchall()))
[{'id': 38, 'firstname': 'Pierre', 'lastname': 'Collin', 'email': 'marc71@bigot.com', 'mobile_phone': '+33 2 53 97 25 45'}]

 

Python: SQlite3 - Utilisée une base de donnée en mémoire

Avec Python, il est possible d'utiliser une base de donnée SQlite montée en mémoire vive.

Très pratique dans le cas d'une utilisation occasionnelle et non persistente (analyse d'un fichier csv par exemple).

Exemple avec Python3

Pour l'exemple, j'utilise le fichier des codes postaux français.

#!/usr/bin/env python3

import csv
import sqlite3
import time

# Execution start time
start = time.time()

# Fields list
fieldsList = [
    'countryCode', 'postalCode', 'placeName', 'adminName1', 'adminCode1', 'adminName2',
    'adminCode2', 'adminName3', 'adminCode3', 'latitude', 'longitude', 'accuracy'
]

# Create database in memory
con = sqlite3.connect(':memory:')
cur = con.cursor()

# Create table in database
cur.execute('create table postalCodes \
    (id integer primary key, {0} text, {1} text, {2} text, \
    {3} text, {4} text, {5} text, {6} text, {7} text, \
    {8} text, {9} real, {10} real, {11} integer)'.format(*fieldsList)
)

# Insert request
sqlInsert = 'insert into postalCodes ({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}) \
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'.format(*fieldsList)

# Read file and insert values into table 'postalCodes'
with open('FR.txt', 'r', newline='', encoding='utf-8') as f:
    reader = csv.reader(f, delimiter='\t')
    for row in reader:
        cur.execute(sqlInsert, row)
        
# Commit
con.commit()

# Execution end time
end = time.time()

# Start of analysis
for row in cur.execute('select count(*) from postalCodes'):
    totalLines = row[0]
    
print("{:n} lines inserted in {:f} sec.".format(totalLines, end - start), end='\n\n')

# Result
51141 lines inserted in 0.493353 sec.

print("All lines with the postal code: '44150'.")
for row in cur.execute('select * from postalCodes where postalCode = ?', ('44150', )):
    print(row)

# Result
All lines with the postal code: '44150'.
(27254, 'FR', '44150', 'Saint-Herblon', 'Pays de la Loire', '52', 'Loire-Atlantique', '44', 'Arrondissement d’Ancenis', '444', 47.4079, -1.0974, 5)
(27255, 'FR', '44150', 'Anetz', 'Pays de la Loire', '52', 'Loire-Atlantique', '44', 'Arrondissement d’Ancenis', '444', 47.3806, -1.1058, 5)
(27256, 'FR', '44150', 'Saint-Géréon', 'Pays de la Loire', '52', 'Loire-Atlantique', '44', 'Arrondissement d’Ancenis', '444', 47.3677, -1.2026, 5)
(27257, 'FR', '44150', 'Ancenis', 'Pays de la Loire', '52', 'Loire-Atlantique', '44', 'Arrondissement d’Ancenis', '444', 47.3667, -1.1667, 5)
    
print()
print("Minimum and maximum postal code for the adminCode2: 'Côtes-d'Armor'.")
for row in cur.execute('select min(postalCode), max(postalCode) from postalCodes where adminName2 = ?', ('Côtes-d\'Armor', )):
    print(row)

# Result
Minimum and maximum postal code for the adminCode2: 'Côtes-d'Armor'.
('22000', '22980')

print()
print("The number of placeName by adminName1 and adminName2.")    
for row in cur.execute('select adminName1, adminName2, count(placeName) from postalCodes group by adminName1, adminName2'):
    print(row)

# Result
The number of placeName by adminName1 and adminName2.
('', '', 1)
('Alsace-Champagne-Ardenne-Lorraine', 'Ardennes', 496)
('Alsace-Champagne-Ardenne-Lorraine', 'Aube', 497)
('Alsace-Champagne-Ardenne-Lorraine', 'Bas-Rhin', 802)
...
('Île-de-France', 'Yvelines', 601)

Super pratique, non !!!

Python: Supprimer tous les dossiers vides sur un serveur FTP

Voici un script Python qui permet, comme le titre l'indique, de parcourir toute l'arborescence d'un serveur FTP à la recherche des dossiers vides et de les supprimer.

Ce script a été testé avec Python 2.7, 3.4 et 3.5.

Détail du script:

La fonction principale qui permet de parcourir, récursivement, l'arborescence du serveur FTP.

def checkFtpEmptyFolders(ftp, dir):
    ctn = 0
    l = []
    ftp.dir(dir, l.append)
    l = [x.split() for x in l]
    """
        If the list is empty, the folder is empty.
        So, deletion of the folder.
    """
    if len(l) == 0:
        try:
            print("Delete folder %s" % dir)
            ftp.rmd(dir)
            ctn = 1
        except:
            pass
    
    else:
        """
            Browse the folder.
            If the item is a folder,
            then, recall the function
            with the subfolder in parameter.
        """
        for x in l:
            try:
                _dir = (dir + "/" + x[-1]).replace('//', '/')
                ftp.cwd(_dir)
                ftp.cwd(dir)
                ctn = ctn | checkFtpEmptyFolders(ftp, _dir)
            except:
                continue
    return ctn

Et enfin, la méthode pour appeler cette fonction.

from ftplib import FTP

host   = 'monFtp.monDomaine.fr'
user   = 'monUser'
passwd = 'monPass'
# Indiquer "/" pour débuter à la racine
dir    = '/monDossier'
# Cette variable permet de sortir de la boucle while
# s'il n'y a plus de dossier vide à supprimer
ctn    = 1

try:
    ftp = FTP(host)
    ftp.login(user, passwd)
    ftp.set_pasv(1)
    ftp.cwd(dir)
    while ctn:
        ctn = checkFtpEmptyFolders(ftp, dir)
    ftp.quit()
finally:
    try:
        ftp.close()
    except:
        pass

Et voilà, tous les dossiers vides, enfin supprimés.

Plusieurs adaptations peuvent être faites à partir de ce script.

Etiquettes: 

Python: Trier le contenu d'un dictionnaire (dict)

En python, l'objet dict ne conserve pas l'ordre dans lequel les éléments sont ajoutés et ne possède pas de fonction sort permettant de trier les données suivants les clés ou les valeurs.

Pour trier un objet dict, il suffit d'utiliser la fonction sorted.
Cette fonction retourne une liste contenant les valeurs triées.
Dans le cas d'un objet dict, les données (clés + valeurs) sont converties en tuple.

Exemple:

>>> d1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'a1': 5, 'b1': 10, 'c1': 8, 'd1': 6}
>>> print(d1)
{'d1': 6, 'c': 3, 'd': 4, 'b': 2, 'a1': 5, 'c1': 8, 'b1': 10, 'a': 1}

On voit que l'ordre d'insertion n'a pas été conservé.

Tri de l'objet dict en fonction des clés

>>> sorted(d1.items(), key=lambda t: t[0])
[('a', 1), ('a1', 5), ('b', 2), ('b1', 10), ('c', 3), ('c1', 8), ('d', 4), ('d1', 6)]

On utilise donc la fonction sorted avec en paramètre la liste des données (clé + valeurs) de l'objet dict (la fonction items() de l'objet dict) et en clé de tri, une fonction lambda indiquant l'indice à utiliser pour le tri.

L'indice '0' (zéro) indique la clé de l'objet dict.
Pour un tri suivant les valeurs, il suffit d'indiquer l'indice '1' (un)

>>> sorted(d1.items(), key=lambda t: t[1])
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a1', 5), ('d1', 6), ('c1', 8), ('b1', 10)]

Si les valeurs sont des tuples ou des lists, il suffit d'indiquer, un second indice (x), correspondant à la position sur laquelle on souhaite faire le tri.

>>> sorted(d1.items(), key=lambda t: t[1][x])

Pour conserver l'ordre de tri dans un objet dict, il est obligatoire d'utiliser l'objet OrderedDict du module collections.

>>> from collections import OrderedDict
>>> d2 = OrderedDict(sorted(d1.items(), key=lambda t: t[0]))
>>> print(d2)
OrderedDict([('a', 1), ('a1', 5), ('b', 2), ('b1', 10), ('c', 3), ('c1', 8), ('d', 4), ('d1', 6)])

L'itération d'un objet OrderedDict s'utilise de la même manière que pour un objet dict.

>>> for k, v in d2.items():
    print("{:>3s} : {:<d}".format(k, v))

  a : 1
 a1 : 5
  b : 2
 b1 : 10
  c : 3
 c1 : 8
  d : 4
 d1 : 6

infoTesté avec Python2 et Python3

Python: Trier les balises d'un fichier XML avec BeautifulSoup

BeautifulSoup est un module Python qui permet de manipuler très facilement n'importe quel fichier XML.

Pour l'installer, rien de plus simple que:

$ python3 -m pip install --upgrade bs4

Exemple avec le fichier XML suivant

<?xml version="1.0" encoding="utf-8"?>
<towns>
 <town name="Paris"/>
 <town name="Lyon"/>
 <town name="Marseille"/>
 <town name="Nantes"/>
 <town name="Ancenis"/>
 <town name="Bordeaux"/>
 <town name="Toulouse"/>
 <town name="Rouen"/>
 <town name="Brest"/>
</towns>

On importe le XML à l'aide du module BeautifulSoup

>>> from bs4 import BeautifulSoup as bs
>>> xml = bs("""<?xml version="1.0" encoding="utf-8"?>
<towns>
 <town name="Paris"/>
 <town name="Lyon"/>
 <town name="Marseille"/>
 <town name="Nantes"/>
 <town name="Ancenis"/>
 <town name="Bordeaux"/>
 <town name="Toulouse"/>
 <town name="Rouen"/>
 <town name="Brest"/>
</towns>""", 'xml')
>>> xml
<?xml version="1.0" encoding="utf-8"?>
<towns>
<town name="Paris"/>
<town name="Lyon"/>
<town name="Marseille"/>
<town name="Nantes"/>
<town name="Ancenis"/>
<town name="Bordeaux"/>
<town name="Toulouse"/>
<town name="Rouen"/>
<town name="Brest"/>
</towns>
>>>

Je souhaite trier par ordre croissant toutes les balises town en fonction de la valeur de l'attribut name.

Pour cela, je vais dans un premier temps récupérer toutes les balises town.

>>> towns = xml.findAll('town')
>>> towns
[<town name="Paris"/>, <town name="Lyon"/>, <town name="Marseille"/>, <town name="Nantes"/>, <town name="Ancenis"/>, <town name="Bordeaux"/>, <town name="Toulouse"/>, <town name="Rouen"/>, <town name="Brest"/>]

Me voici donc avec une liste towns contenant toutes mes balises town.

Ensuite, je trie par ordre croissant le contenu de ma liste towns.

>>> towns.sort(key=lambda x: x.get('name'))
>>> towns
[<town name="Ancenis"/>, <town name="Bordeaux"/>, <town name="Brest"/>, <town name="Lyon"/>, <town name="Marseille"/>, <town name="Nantes"/>, <town name="Paris"/>, <town name="Rouen"/>, <town name="Toulouse"/>]

Pour trier correctement le contenu de ma liste, j'utilise le paramètre key de la fonction sort en y indiquant une fonction lambda dans laquelle je lui indique d'utiliser l'attribut name pour effectuer le tri.

Avec BeautifulSoup, pour obtenir la valeur d'un attribut d'une balise, il suffit d'utiliser la méthode get sur n'importe quel élément avec le nom de l'attribut, en l'occurence name.

Dans ma fonction lambda, x étant remplacé par chaque élément town de la liste.

Me voici donc avec ma liste parfaitement trié.

Il suffit donc maintenant de remplacer tout le contenu de la balise towns de mon XML avec le contenu de ma liste.

>>> xml.towns.clear()
>>> xml.towns
<towns/>
>>> xml.towns.extend(towns)
>>> print(xml.prettify())
<?xml version="1.0" encoding="utf-8"?>
<towns>
 <town name="Ancenis"/>
 <town name="Bordeaux"/>
 <town name="Brest"/>
 <town name="Lyon"/>
 <town name="Marseille"/>
 <town name="Nantes"/>
 <town name="Paris"/>
 <town name="Rouen"/>
 <town name="Toulouse"/>
</towns>

J'ai donc utilisé la méthode clear de l'élément towns pour vider tous les éléments town.

Enfin, comme pour une liste Python, la méthode extend de l'élément towns permet d'ajouter tout le contenu de la liste towns correctement triée.

Simple comme bonjour, isn't it....

Etiquettes: 

Python: Utiliser un fichier de paramètres

Sous Python, il est possible d'utiliser dans les scripts des fichiers de paramètres à la manière des fichier INI sous Windows.

Un fichier de paramètres doit contenir une ou plusieurs sections et pour chaque section, une paire paramètre/valeur.

Exemple:

$ cat monFichierDeParametres
[MYSQL]
host: localhost
user: toto
pass: pass4toto
db: maBase

[MAIL]
server: monserveursmtp.fr
from: toto@domaine.com
to: tutu@domaine.com
sujet: bla bla bla
  • Une section doit être écrite entre crochets [...]
  • Le caractère ":" doit être utilisé pour séparer le paramètre de la valeur


Utilisation dans un script Python:

#!/usr/bin/env python
# -*- coding: utf-8 -*
import ConfigParser # Permet de parser le fichier de paramètres
config = ConfigParser.RawConfigParser() # On créé un nouvel objet "config"
config.read('monFichierDeParametres') # On lit le fichier de paramètres
# On récupère les valeurs des différents paramètres
# ATTENTION, cette syntaxe est spécifique pour les paramètres MySQL
# On créé un dictionnaire contenant les paires clés/valeurs
# Pour chaque paramètre, on utilise la fonction "get" de notre objet "config" en lui indiquant la section et le nom du paramètre
paramMysql = {
    'host'   : config.get('MYSQL','host'),
    'user'   : config.get('MYSQL','user'),
    'passwd' : config.get('MYSQL','pass'),
    'db'     : config.get('MYSQL','db')
}
# Récupération basique dans des variables
serveurMail = config.get('MAIL','server')
from = config.get('MAIL','from')
to = config.get('MAIL','to')
sujet = config.get('MAIL','sujet')

C'est quand même vachement pratique.

Etiquettes: 

Python: Vérifier la version TLS d'une serveur

Pour vérifier la version TLS d'un serveur Python propose 2 modules qui permet de le faire.

Voici un exemple qui permet de controler la version TLS de mon serveur.

import socket
import ssl

hostname = 'quennec.fr'
context = ssl.create_default_context()

with socket.create_connection((hostname, 443)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())
        
TLSv1.2

La version TLS de mon serveur est la 1.2

Etiquettes: 

Python: décortiquer une URL à l'aide d'une regex

Cette regex permet de décortiquer une URL et de nommer les différents éléments.

regexp_url = "^(?i)\
(?P<proto>(http(s)*|ftp|ssh))\
(://)\
((?P<user>\w+)(:(?P<password>\w+))?@)?\
(?P<hostname>[\w\.-]+)\
(:(?P<port>[0-9]+))?\
(?P<path>.*)?\
$"

Je l'ai mise sur plusieurs lignes pour mieux la comprendre (d'où les '\' à chaque fin de ligne).

Sur la première ligne, le groupement (?i) permet d'indiquer que la regex doit être insensible à la casse.

La seconde ligne permet de parser et mémoriser le protocole utilisé (http/https/ftp/ssh).

La quatrième ligne permet de parser et mémoriser, si besoin, le user et le password.

La cinquième ligne permet de parser et mémoriser le hostname.

La sixième ligne permet de parser et mémoriser, si besoin, le port.

Pour terminer, la septième ligne permet de parser et mémoriser, si besoin, le path.

Le couple user:password est facultatif ainsi que le port et le path.

Quelques exemples

>>> import re
>>> from pprint import pprint
>>> regexp_url = "^(?i)\
(?P<proto>(http(s)*|ftp|ssh))\
(://)\
((?P<user>\w+)(:(?P<password>\w+))?@)?\
(?P<hostname>[\w\.-]+)\
(:(?P<port>[0-9]+))?\
/\
(?P<path>.*)?\
$"
>>> pprint(re.match(regexp_url, 'https://www.google.fr/').groupdict())
{'hostname': 'www.google.fr',
 'password': None,
 'path': '/',
 'port': None,
 'proto': 'https',
 'user': None}
>>> pprint(re.match(regexp_url, 'https://mail.google.com/mail/ca/u/0/#inbox').groupdict())
{'hostname': 'mail.google.com',
 'password': None,
 'path': '/mail/ca/u/0/#inbox',
 'port': None,
 'proto': 'https',
 'user': None}
>>> pprint(re.match(regexp_url, 'http://un-site:8080/greenhis/issues/14431').groupdict())
{'hostname': 'un-site',
 'password': None,
 'path': '/greenhis/issues/14431',
 'port': '8080',
 'proto': 'http',
 'user': None}
>>> pprint(re.match(regexp_url, 'https://docs.python.org/2/library/re.html#regular-expression-syntax').groupdict())
{'hostname': 'docs.python.org',
 'password': None,
 'path': '/2/library/re.html#regular-expression-syntax',
 'port': None,
 'proto': 'https',
 'user': None}
>>> pprint(re.match(regexp_url, 'ftp://toto:1234@ftpperso.free.fr/').groupdict())
{'hostname': 'ftpperso.free.fr',
 'password': '1234',
 'path': '/',
 'port': None,
 'proto': 'ftp',
 'user': 'toto'}
>>> pprint(re.match(regexp_url, 'ftp://toto:1234@ftpperso.free.fr:21/').groupdict())
{'hostname': 'ftpperso.free.fr',
 'password': '1234',
 'path': '/',
 'port': '21',
 'proto': 'ftp',
 'user': 'toto'}
>>> pprint(re.match(regexp_url, 'ftp://toto@ftpperso.free.fr:21/').groupdict())
{'hostname': 'ftpperso.free.fr',
 'password': None,
 'path': '/',
 'port': '21',
 'proto': 'ftp',
 'user': 'toto'}
>>>

Penser à partager vos améliorations si besoin.

Les fonctions urlparse et urlsplit de la classe parse du module urllib permettent aproximativement de faire la même chose

>>> import urllib
>>> urllib.parse.urlparse('http://un-site:8080/greenhis/issues/14431')
ParseResult(scheme='http', netloc='un-site:8080', path='/greenhis/issues/14431', params='', query='', fragment='')
>>> urllib.parse.urlparse('ftp://toto:1234@ftpperso.free.fr:21/')
ParseResult(scheme='ftp', netloc='toto:1234@ftpperso.free.fr:21', path='/', params='', query='', fragment='')
>>> urllib.parse.urlsplit('ftp://toto:1234@ftpperso.free.fr:21/')
SplitResult(scheme='ftp', netloc='toto:1234@ftpperso.free.fr:21', path='/', query='', fragment='')

 

Python: extraire les données d'une chaine de texte structurée en fonction d'une liste d'index

Compatible Python2 & Python3

Exemple avec le contenu de la variable "d" organisée sous la forme d'un tableau

>>> print(d)
2015  0 391371 374179 765550
2014  1 403204 385442 788646
2013  2 405502 386831 792333
2005 10 426532 406488 833020
2004 11 423605 404592 828197
2003 12 421432 402484 823916
1929 86  97489 180249 277738
1928 87  86122 165344 251466
1927 88  72603 149288 221891
1919 96   6011  21172  27183
1918 97   3649  13644  17293
1917 98   2013   9475  11488
1916 99   1324   6313   7637
>>>

Chaque ligne contient exactement le même nombre de données.
Chaque donnée (de longueur fixe) se trouve exactement à la même place sur chaque ligne.
Dans l'exemple, chaque ligne contient donc 5 colonnes.

>>> print(columns)
['Année de naissance', 'Age révolu', "Nombre d'hommes", 'Nombre de femmes', 'Ensemble']
>>>

Les données de la première colonne commencent à l'index 0
Les données de la seconde colonne commencent à l'index 5
Les données de la troisième colonne commencent à l'index 8
Les données de la quatrième colonne commencent à l'index 15
Et enfin, les données de la cinquième colonne commencent à l'index 22

Et c'est pareil pour chaque ligne.

>>> print(i)
[0, 5, 8, 15, 22]
>>>

Une petite boucle va permettre d'extraire toutes les données en fonction de la liste d'index ci-dessus

>>> for line in d.split('\n'):
    x = 0
    for pos in i:
        print("{COLUMN:>20s} : {DATA:<6s}".format(COLUMN=columns[x], DATA=line[pos:i[i.index(pos)+1] if i.index(pos)+1 < len(i) else None].strip()))
        x += 1
    print('*' * 31)

    
  Année de naissance : 2015  
          Age révolu : 0     
     Nombre d'hommes : 391371
    Nombre de femmes : 374179
            Ensemble : 765550
*******************************
  Année de naissance : 2014  
          Age révolu : 1     
     Nombre d'hommes : 403204
    Nombre de femmes : 385442
            Ensemble : 788646
*******************************
  Année de naissance : 2013  
          Age révolu : 2     
     Nombre d'hommes : 405502
    Nombre de femmes : 386831
            Ensemble : 792333
*******************************
  Année de naissance : 2005  
          Age révolu : 10    
     Nombre d'hommes : 426532
    Nombre de femmes : 406488
            Ensemble : 833020
*******************************
  Année de naissance : 2004  
          Age révolu : 11    
     Nombre d'hommes : 423605
    Nombre de femmes : 404592
            Ensemble : 828197
*******************************
  Année de naissance : 2003  
          Age révolu : 12    
     Nombre d'hommes : 421432
    Nombre de femmes : 402484
            Ensemble : 823916
*******************************
  Année de naissance : 1929  
          Age révolu : 86    
     Nombre d'hommes : 97489
    Nombre de femmes : 180249
            Ensemble : 277738
*******************************
  Année de naissance : 1928  
          Age révolu : 87    
     Nombre d'hommes : 86122
    Nombre de femmes : 165344
            Ensemble : 251466
*******************************
  Année de naissance : 1927  
          Age révolu : 88    
     Nombre d'hommes : 72603
    Nombre de femmes : 149288
            Ensemble : 221891
*******************************
  Année de naissance : 1919  
          Age révolu : 96    
     Nombre d'hommes : 6011  
    Nombre de femmes : 21172
            Ensemble : 27183
*******************************
  Année de naissance : 1918  
          Age révolu : 97    
     Nombre d'hommes : 3649  
    Nombre de femmes : 13644
            Ensemble : 17293
*******************************
  Année de naissance : 1917  
          Age révolu : 98    
     Nombre d'hommes : 2013  
    Nombre de femmes : 9475  
            Ensemble : 11488
*******************************
  Année de naissance : 1916  
          Age révolu : 99    
     Nombre d'hommes : 1324  
    Nombre de femmes : 6313  
            Ensemble : 7637  
*******************************
>>>

 

Python: glances (htop amélioré)

glances est un module python qui permet d'afficher un état détaillé du système, rafraichit automatiquement, à la manière de la commande htop.

Pré requis:

$ apt-get install python
$ apt-get install python-dev
$ apt-get install python-pip
$ pip install psutil

Pour l'installer:

$ pip install glances

Pour les mises à jour:

$ pip install psutil --upgrade
$ pip install glances --upgrade

Pour l'exécuter:

$ glances

Résultat:

Vraiment très pratique.

Python: obtenir le fuseau horaire à partir de coordonnées géographiques

Comment connaitre le fuseau horaire à partir d'une latitude et d'une longitude ?

Grâce au module timezonefinder.

# python3 -m pip install --upgrade timezonefinder

Et comment fonctionne-t-il ?

Tout simplement de cette manière:

Par exemple, avec les coordonnées de Montréal (Canada)

>>> from timezonefinder import TimezoneFinder
>>> timezoneid = TimezoneFinder().timezone_at(lat=45.5704, lng=-73.7674)
>>> timezoneid
'America/Toronto'

Avec les coordonnées de Tokyo (Japon)

>>> timezoneid = TimezoneFinder().timezone_at(lat=35.876, lng=136.912)
>>> timezoneid
'Asia/Tokyo'

Avec les coordonnées de Nantes (France)

>>> timezoneid = TimezoneFinder().timezone_at(lat=47.2383, lng=-1.5603)
>>> timezoneid
'Europe/Paris'

 

Python: pydf (pour controler l'espace disque)

pydf est un module python qui permet d'afficher à l'écran des statistiques sur l'espace disque (version évoluée de la commande df).

Pré requis:

$ apt-get install python
$ apt-get install python-dev
$ apt-get install python-pip

Pour l'installer:

$ pip install pydf

Pour les mises à jour:

$ pip install pydf --upgrade

Pour l'exécuter:

$ pydf

Résultat:

Plutôt sympa et vraiment très utile.

Python: pytail (tail -f à la mode Python)

Juste comme ça, pour s'amuser.

Voici un script Python 'pytail.py' qui fait exactement la même chose que la commande tail -f /path/to/log/file sous Linux, sauf que ce script peut-être utilisé sur tous les systèmes où la commande tail -f fait défaut (comme sous Windows par exemple).

Testé avec Python 3

#!/usr/bin/env python3

import re, os, time, sys
from time import time, sleep
from os.path import join, getsize, getmtime

if len(sys.argv) == 1:
    print("Argument Missing !")
    print("Usage:\n    {0:s} /path/to/log/file".format(sys.argv[0]))
    sys.exit(1)

startTime = time()
mTimeStart = 0
fileName = sys.argv[1]

if not os.path.exists(fileName) or not os.path.isfile(fileName):
    print("This file '{0:s}' does not exist !".format(fileName))
    sys.exit(2)

try:
    with open(fileName, 'r') as f:
        f.read(1)
except Exception as e:
    print("This file '{0:s}' cannot be opened for reading !".format(fileName))
    sys.exit(3)

folderLogs = os.path.dirname(fileName)

copyLines = []

try:

    while True:
        with open(fileName, 'r') as f:
            lines = f.readlines()

        show = False

        if len(copyLines) == 0:
            showLines = lines[-10:]
            show = True

        elif len(copyLines) != len(lines):
            showLines = lines[len(copyLines) - len(lines):]
            show = True

        if show:
            copyLines = lines.copy()
            for line in showLines:
                print(line, end='')

        sleep(1)

except KeyboardInterrupt as e:
    print("Program stopped by user !")
    sys.exit(0)

except Exception as e:
    print("Unknown error during execution !")
    print(e)
    sys.exit(4)

Pour l'exécuter:

# python3 pytail.py /var/log/mail.log

Merci qui, merci Python.

Etiquettes: 

Matériel

Etiquettes: 

Tester le débit descendant d'une connexion internet

Le site http://test-debit.free.fr/ propose 9 tailles différentes de fichiers afin de tester le débit descendant d'une connexion internet.

Pour effectuer le test, rien de plus simple :

$ wget http://test-debit.free.fr/512.rnd
--2012-06-18 08:46:53--  http://test-debit.free.fr/512.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 524288 (512K) [text/plain]
Saving to: `512.rnd'
 
100%[======================================================================================================================>] 524,288      385K/s   in 1.3s
 
2012-06-18 08:46:54 (385 KB/s) - `512.rnd' saved [524288/524288]
 
$ wget http://test-debit.free.fr/1024.rnd
--2012-06-18 08:47:06--  http://test-debit.free.fr/1024.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1048576 (1.0M) [text/plain]
Saving to: `1024.rnd'
 
100%[======================================================================================================================>] 1,048,576    407K/s   in 2.5s
 
2012-06-18 08:47:09 (407 KB/s) - `1024.rnd' saved [1048576/1048576]
 
$ wget http://test-debit.free.fr/2048.rnd
--2012-06-18 08:47:17--  http://test-debit.free.fr/2048.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2097152 (2.0M) [text/plain]
Saving to: `2048.rnd'
 
100%[======================================================================================================================>] 2,097,152    405K/s   in 5.1s
 
2012-06-18 08:47:23 (398 KB/s) - `2048.rnd' saved [2097152/2097152]
 
$ wget http://test-debit.free.fr/4096.rnd
--2012-06-18 08:47:42--  http://test-debit.free.fr/4096.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4194304 (4.0M) [text/plain]
Saving to: `4096.rnd'
 
100%[======================================================================================================================>] 4,194,304    395K/s   in 10s
 
2012-06-18 08:47:52 (400 KB/s) - `4096.rnd' saved [4194304/4194304]
 
$ wget http://test-debit.free.fr/8192.rnd
--2012-06-18 08:48:04--  http://test-debit.free.fr/8192.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8388608 (8.0M) [text/plain]
Saving to: `8192.rnd'
 
100%[======================================================================================================================>] 8,388,608    556K/s   in 15s
 
2012-06-18 08:48:19 (542 KB/s) - `8192.rnd' saved [8388608/8388608]
 
$ wget http://test-debit.free.fr/16384.rnd
--2012-06-18 08:48:37--  http://test-debit.free.fr/16384.rnd
Resolving test-debit.free.fr... 212.27.60.49
Connecting to test-debit.free.fr|212.27.60.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16777216 (16M) [text/plain]
Saving to: `16384.rnd'
 
100%[======================================================================================================================>] 16,777,216   428K/s   in 33s
 
2012-06-18 08:49:10 (497 KB/s) - `16384.rnd' saved [16777216/16777216]
 
$

L'automatisation via un script est donc tout à fait possible.

Tester une alimentation

Comment savoir si une alimentation fonctionne.
Tout simplement en la testant avec un voltmètre.

voltmètre

Une alimentation ATX est composée d'une broche à 20 pins

connecteur

Prendre soin de débrancher l'alimentation avant de procéder à cette opération.
Pour la tester, il faut utiliser un trombonne ou un cable électrique et relié le pin 14 au pin 16.

C'est à dire, relié le cable vert (PS_ON#) à un cable noir (COM), comme ceci

A l'aide d'un voltmètre, tester les différentes alimentations en reliant le cable noir du voltmètre à 1 pin COM

Il se peut que les voltages soient légèrement différents.

WakeOnLan / WakeOnWan

WakeOnLan est un standard des réseaux ethernet qui permet de démarrer à distance un ordinateur éteint.

Allumer des ordinateurs à distance pour maintenance ou suite à une coupure de courant est donc possible grâce à cette technologie.

Le réveil de l'ordinateur est déclenché quand la carte réseau reçoit un paquet magique sur le port UDP 7 ou 9 (ports standards).

Pré-requis :

  1. Disposer d'une carte mère compatible WakeOnLan
  2. Disposer d'une carte réseau compatible WakeOnLan
  3. Activer l'option WakeOnLan dans le BIOS de l'ordinateur

Utilisation via le réseau ethernet :

  • Sous Linux (Ubuntu / Debian)

Grâce au paquet etherwake ou wakeonlan :

$ sudo apt-get install etherwake

$ sudo apt-get install wakeonlan

Installer le paquet ethtool :

$ sudo apt-get install ethtool

Vérifier si la carte est configurée pour le WOL :

$ sudo ethtool eth0
Settings for eth0:
        .....
        Wake-on: g
        .....

La paramètre Wake-on doit être égal à g.
Si le paramètre est égal à d, activer le WOL avec ette commande :

$ sudo ethtool -s eth0 wol g

Pour réveiller un ordinateur ayant comme adresse mac 00-27-10-0D-9C-98 :

$ wakeonlan 00-27-10-0D-9C-98

  • Sous Windows

Vérifier également la configuration de la carte réseau.
Dans le gestionnaire de prériphériques, double-cliquer sur la carte réseau concernée puis dans l'onglet avancé, vérifier la valeur du paramètre Wake on. Sélectionner la valeur Wake On Magic Packet puis valider.

Grâce au logiciel WakeOnLan disponible sur le site http://www.dipisoft.com/articles.php?lng=fr&pg=90

Disponible également à cette adresse http://www.dipisoft.com/file/wakeonlan.exe

Très simple d'utilisation, il suffit d'y ajouter la liste des ordinateurs à gérer, soit manuellement, soit via une liste générée à l'aide du logiciel Dipiscan (anciennement IPScan32) disponible également sur le site https://www.dipisoft.com/articles.php?lng=fr&pg=2116&mnuid=2211&tconfig=0

Disponible également à cette adresse https://www.dipisoft.com/download_plus.php?action=1&lng=fr&pg=2117

Utilisation via le réseau internet :

Pour un réveil via internet, il faut un routeur compatible WOW/WOL et le configurer afin de rediriger le port 7, 9 ou n'importe quel autre port disponible vers l'adresse IP de l'ordinateur concerné (protocole UDP).
Pour gérer plusieurs ordinateurs, il suffit de rediriger autant de ports que d'ordinateurs.

Par exemple :

UDP 7002 --> 192.168.1.2
UDP 7003 --> 192.168.1.3
....
UDP 7010 --> 192.168.1.10

Enfin, différents sites proposent du WOL via internet :

http://www.depicus.com/wake-on-lan/woli.aspx

http://www.wakeonlan.net/

Windows: Réseau: Ajouter une route à travers une connexion VPN

Besoin d'accéder à un site sur internet dont l'accès est filtré sur l'adresse IP du navigateur client ou dans le cas d'un intranet d'entreprise dont le seul moyen d'y accéder se fait par le réseau interne.

Il faut quand même avoir un accès VPN.
Mais les navigateurs n'utilisent pas les accès VPN pour les noms de domaines publics.
Il se peut également que la configuration des routes internes ne soient pas configurées dans la connexion VPN.

Pour ajouter une route manuellement, c'est tout simple.
Ouvrir une fenêtre PowerShell en mode administrateur.

PS C:\WINDOWS\system32> route add -p [adresse ip destination] MASK 255.255.255.255 0.0.0.0 METRIC 35 IF [numéro de l'interface réseau à utiliser]

La commande est toute simple.
- L'option -p permet d'ajouter la route définitivement
- [adresse ip destination] correspond donc au site internet auquel on souhaite accéder (ça peut être également une machine pour un accès en RDP - pour connaitre l'adresse IP d'un FQDN, il suffit d'utiliser la commande nslookup foo.bar.fr)
- [numéro de l'interface réseau à utiliser] correspond au numéro de l'interface réseau de la connexion vpn

Pour trouver le numéro de l'interface réseau de la connexion vpn, dans la fenêtre powershell, il suffit d'exécuter cette commande

PS C:\WINDOWS\system32> route PRINT
===========================================================================
Liste d'Interfaces
..................................
104...........................SonicWall NetExtender
..................................
  1...........................Software Loopback Interface 1
===========================================================================

IPv4 Table de routage
===========================================================================
Itinéraires actifs :
Destination réseau    Masque réseau  Adr. passerelle   Adr. interface Métrique
          0.0.0.0          0.0.0.0      192.168.1.1     192.168.1.26     50
.................

Dans mon cas, ma connexion vpn correspond à l'interface SonicWall NetExtender et son numéro est le 104

Systèmes

CentOS 7

centos logo

Etiquettes: 

Divers

CentOS: Les différents paquets à installer

Voici une liste des paquets à installer en priorité:

# yum install \
epel-release.noarch \
bash-completion.noarch \
bash-completion-extras.noarch \
net-tools.x86_64 \
dpkg-devel.x86_64 dpkg-dev.noarch dpkg.x86_64 \
perl-Apache-Htpasswd.noarch \
python34.x86_64 \
gcc.x86_64 \
python34-devel.x86_64 \
python-devel.x86_64 \
python-pip.noarch \
python34-setuptools.noarch \
glances.noarch \
python34-pip.noarch \
man-db.x86_64 \
bind-utils

Liste non exhaustive.

Etiquettes: 

CentOS: Quelques commandes utiles

Lister les paquets installés

# yum list installed

Connaitre le statut d'un service (exemple avec le service ntpd)

# systemctl status ntpd

ou

# service ntpd status

Redémarrer le réseau

# systemctl restart network

Activer un service (exemple avec le service nginx)

# systemctl enable nginx

Démarrer un service (exemple avec le service nginx)

# systemctl start nginx

 

CentOS: localtime

Pour configurer le bon fuseau horaire sur CentOS, il faut indiquer les bons paramètres dans le fichier /etc/localtime

On trouve dans le dossier /usr/share/zoneinfo tous les fichiers pour toutes les zones de la planète.

Il suffit donc de faire un lien entre le bon fichier fichier correspondant à la zone souhaitée et le fichier /etc/localtime.

# unlink /etc/localtime
# ln -s /usr/share/zoneinfo/Europe/Paris /etc/localtime

 

Etiquettes: 

Réseau

CentOS: Configuration IP statique

Sous CentOS, la configuration de l'interface réseau avec une adresse IP statique diffère de celle sous Debian ou Ubuntu.

Pour identifier les cartes réseaux présentes sur la machine, exécuter la commande suivante:

# nmcli d

Pour une configuration avec un assistant, exécuter la commande suivante et suivre les indications à l'écran:

# nmtui

Une fois la configuration réseau terminée, assistée ou manuelle, redémarrer le service réseau:

# systemctl restart network

Pour une configuration manuelle, voir ci-dessous.

Sous CentOS, il faut créer un fichier ifcfg-eth0, correspondant bien sûr au nom de l'interface réseau à configurer. Dans ce cas, il s'agit de l'interface eth0.

Ce fichier est à créer dans le dossier /etc/sysconfig/network-scripts

Il doit contenir les informations suivantes:

# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE="eth0"
HWADDR="A6:B5:6C:4F:E5:C0"
NM_CONTROLLED="no"
ONBOOT="yes"
BOOTPROTO=static
# BOOTPROTO=dhcp
IPADDR=172.16.1.115
NETMASK=255.255.255.0
#
#   the GATEWAY is sometimes in: /etc/sysconfig/network
GATEWAY=172.16.1.1

Les paramètres à renseigner sont assez explicites.

Il faut également modifier le fichier /etc/sysconfig/network

# cat /etc/sysconfig/network
NETWORKING="yes"
DNS1=8.8.8.8
DNS2=8.8.4.4
SEARCH=mondomaine.fr

Là également, les paramètres à renseigner sont assez explicites.

Reste à exécuter la commande suivante afin d'initialiser les différents scripts.

# rpm -qd initscripts

Et voilà, c'est terminé. Redémarrer le serveur afin de prendre en compte les modifications.

La commande ifconfig permet de vérifier que tout est correct.

Si la commande ifconfig n'est pas installée:

# yum install net-tools.x86_64

Puis:

# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.1.115  netmask 255.255.255.0  broadcast 172.16.1.255
        inet6 fe80::a4b5:6cff:fe4f:e5c0  prefixlen 64  scopeid 0x20<link>
        ether a6:b5:6c:4f:e5:c0  txqueuelen 0  (Ethernet)
        RX packets 43646  bytes 64538893 (61.5 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 20396  bytes 1157263 (1.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

GNU/Linux

Logo Linux
Tous mes trucs et astuces pour utiliser GNU/Linux

Programmation shell sous GNU/Linux

Bash Shell

Qu'est ce que le shell sous GNU/Linux ?

Le shell est à Linux ce que MS-DOS est à Windows.

En effet, sur un système GNU/Linux, il est très important de connaitre les bases du shell.
Même si les interfaces graphique sont là pour simplifier les opérations, il est parfois nécessaire, voir indispensable, de savoir utiliser des commandes GNU/Linux.

Parfois, il n'y a pas toujours de programmes avec de belles interfaces graphiques pour faire ce que nous souhaitons.
Parfois, il est plus simple et plus rapide de faire telle ou telle action via les lignes de commandes.

Rechercher des fichiers, créer des répertoires, rechercher du texte dans plusieurs fichiers, renommer en masse tout un ensemble de fichier selon certains critères, se déplacer au sein de l'arborescence GNU/Linux etc etc ...

Et enfin, le plus important, écrire ses propres scripts afin d'automatiser certaines actions répétitives.

Extraits du livre "Programmation Shell sous Unix / Linux" aux éditions ENI que je conseil très fortement de se procurer.

Programmation shell sous Linux

Les mécanismes du shell

Commandes internes et externes

Les commandes externes

Une commande externe est un fichier présent dans l'arborescence.

Quand un utilisateur exécute la commande ls, le shell demande au noyau Linux d'exécuter le fichier /bin/ls

Sont considérés comme commandes externes, tous les fichiers au format binaire exécutable ainsi que tous les fichiers au format texte représentant un script de commandes.

La commande file indique le type de données contenues dans un fichier.

$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped

$ file monscript
monscript: POSIX shell script text executable

L'argument de la commande file est un nom de fichier indiqué en relatif ou en absolu

Etiquettes: 

Les commandes internes

Une commande interne est intégrée au processus shell.

Elle n'a aucune correspondance avec un fichier sur le disque.

La commande type indique si une commande est interne ou externe.

$ type cd
cd est une primitive du shell

$ type ls
ls est un alias vers « ls --color=auto »

La commande type prend en argument le nom d'une commande. Si cette dernière n'est pas une commande interne, elle est recherchée dans les répertoires indiqués dans la variable PATH

Etiquettes: 

Les commandes internes et externes

Certaines commandes ont une implémentation interne et externe.

  • La commande interne est exécutée en priorité.
  • L'exécution d'une commande interne est plus rapide.
  • La commande type, indique que la commande est interne mais ne précise pas qu'elle peut également être une commande externe.

La commande pwd est une commande interne :

$ type pwd
pwd est une primitive du shell

La commande pwd possède également une implémentation externe :

$ ls -l /bin/pwd
-rwxr-xr-x 1 root root 34376 2010-09-21 20:33 /bin/pwd

Pour forcer l'utilisation de la commande externe, il faut indiquer l'emplacement de la commande :

$ /bin/pwd
/home
$ cd /bin
$ ./pwd
/bin

Affichage à l'écran

La commande interne echo permet d'afficher des données à l'écran.

$ echo Ceci est un exemple
Ceci est un exemple

Il faut échapper l'apostrophe avec l'antislash

$ echo Un exemple avec l\'apostrophe
Un exemple avec l'apostrophe

Il faut échapper les guillemets avec l'antislash

$ echo Un exemple avec des \"guillemets\"
Un exemple avec des "guillemets"

En bash, il faut obligatoirement utilisé l'option "-e" pour utiliser les caractères spéciaux suivants.

  • Le caractère "\n" sert à provoquer un saut de ligne :

$ echo -e "Un exemple\navec un saut de ligne"
Un exemple
avec un saut de ligne

  • Le caractère "\c" supprime le saut de ligne naturel de la commande echo :

Sans le caractère "\c"

$ echo -e "Une ligne" ; echo -e "Une autre ligne"
Une ligne
Une autre ligne

Avec le caractère "\c"

$ echo -e "Une ligne\c" ; echo -e "Une autre ligne"
Une ligneUne autre ligne

L'option -n remplace le caractère "\c"

$ echo -n "Une ligne" ; echo "Une autre ligne"
Une ligneUne autre ligne

  • Le caractère "\t" sert à afficher une tabulation :

$ echo -e "Voici 1 tabulation\tet la suite"
Voici 1 tabulation      et la suite

  • Le caractère "\\" permet d'afficher un antislash :

$ echo -e "Afficher un antislash\\"
Afficher un antislash\

Liste des caractères spéciaux :

\0NNN Valeur d'un caractère exprimé en octal
\\ Antislash
\a Bip
\b Effacement du caractère précédent
\c Suppression du saut de ligne en fin de ligne
\f Saut de page
\n Saut de ligne
\r Retour chariot
\t Tabulation horizontale
\v Tabulation verticale

 

Le caractère ~ (tilde)

Le caractère "~" représente le répertoire HOME de l'utilisateur.

Par exemple, pour l'utilisateur "toto"

$ cd ~
$ pwd
/home/toto

Copier le fichier "fichier1" du répertoire /tmp dans le répertoire /home/toto

$ cd /tmp
$ cp fichier1 ~

Copier le fichier "fichier2" du répertoire /tmp dans le répertoire /home/toto/mesfichiers

$ cd /tmp
$ cp fichier2 ~/mesfichiers

Si le caractère "~" est immédiatement suivi d'un mot, ce dernier est considéré comme un nom d'utilisateur.

Copier le fichier "fichier3" du répertoire /home/toto vers le répertoire /home/titi

$ cd ~
$ pwd
/home/toto
$ cp fichier3 ~titi

Copier le fichier "fichier4" du répertoire /home/toto vers le répertoire /home/titi/tmp

$ cd ~
$ pwd
/home/toto
$ cp fichier4 ~titi/tmp

 

La commande interne cd

La commande cd, sans arguments, permet à l'utilisateur de se placer dans son répertoire HOME.

$ pwd
/tmp
$ cd
$ pwd
/home/toto

Identique en utilisant le caractère "~"

$ cd ~

Pour se rendre dans le répertoire HOME de tata :

$ pwd
/home/toto
$ cd ~tata
$ pwd
/home/tata

Pour revenir au répertoire précédent avec la commande "cd -" :

$ cd -
$ pwd
/home/toto

Remplacement de noms de fichiers

Expressions simples

Attention, certaines de ces expressions ne fonctionnent pas toutes avec les mêmes shell.
Pour certaines, elles sont compatibles BASH, pour d'autres ZSH et certaines ne fonctionnent qu'avec KSH.

Le caractère "*" permet de remplacer n'importe quel caractère.

$ ls
fichier1 fichier2.a monfichier tOnfichier.b

  • Afficher uniquement les fichiers se terminant par ".a" :

$ ls *.a
fichier2.a

  • Afficher uniquement les fichiers commançant par "f" :

$ ls f*
fichier1 fichier2.a

Le caractère "?" représente un caractère quelconque.

  • Afficher uniquement les fichiers ayant une extension composée d'un seul caractère :

$ ls *.?
fichier2.a tOnfichier.b

  • Afficher uniquement les fichiers composés de 8 caractères :

$ ls ????????
fichier1

Les caractères "[ ]" permettent d'indiquer la liste des caractères que l'on recherche à une position bien précise dans le noms des fichiers.

  • Afficher les fichiers commençant par la lettre "f" ou "t" et se terminant
    par le "." suivi d'une lettre minuscule :

$ ls [ft]*.[a-z]
fichier2.a tOnfichier.b

  • Afficher tous les fichiers ayant en deuxième caractère une lettre majuscule, ou un chiffre,
    ou la lettre "e" :

% ls ?[A-Z0-9e]*
cOucou  f1chier  F2chier  Hello

  • Afficher tous les fichiers ne commançant pas par une lettre minuscule :

$ ls [!a-z]*
1coucou  Coucou  F2chier  Fichier  Hello

  • Supprimer tous les fichiers se terminant par .b ou .c :

% rm -i *.b *.c
rm : supprimer fichier vide «fichier1.b» ? y
rm : supprimer fichier vide «fichier1.c» ? y

 

Expressions complexes

Les expressions complexes sont compatible BASH à condition d'activer l'option extglob avec la commande shopt (shopt -s extglob).

$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c

  • ?(expression)

L'expression sera présente 0 ou 1 fois.

Afficher tous les fichiers dont le nom contient 0 ou 1 fois "159"

$ ls fichier?(159).log
fichier159.log  fichier.log

  • *(expression)

L'expression sera présente entre 0 et x fois.

Afficher tous les fichiers dont le nom contient 0 ou x fois "159"

$ ls fichier*(159).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier.log

  • +(expression)

L'expression sera présente entre 1 et x fois.

Afficher tous les fichiers dont le nom contient 1 ou x fois "159"

$ ls fichier+(159).log
fichier159159159.log  fichier159159.log  fichier159.log

  • @(expression)

L'expression sera présente exactement 1 fois.

Afficher tous les fichiers dont le nom contient 1 fois "159"

$ ls fichier@(159).log
fichier159.log

  • !(expression)

L'expression ne sera pas présente.

Afficher tous les fichiers dont le nom ne contient pas 1 fois "159"

$ ls fichier!(159).log
fichier159159159.log  fichier159160.log  fichier161.log
fichier159159.log     fichier160.log     fichier.log

Afficher tous les fichiers dont le nom ne contient pas "fichier"

$ ls !(fichier*)
1coucou  Coucou    coucou.s  coucou.uvw  F2chier  Hello.txt
cOucou   coucou.r  coucou.t  f1chier     Fichier

Une barre verticale dans une expression correspond à "ou bien"

Afficher tous les fichiers dont le nom contient 1 fois "159" ou "160"

$ ls fichier@(159|160).log
fichier159.log  fichier160.log

Afficher tous les fichiers dont le nom contient 1 ou x fois "159" ou "161"

$ ls fichier+(159|161).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Afficher tous les fichiers dont le nom contient 1 fois ( 1 ou x fois "159" ou "161")

$ ls fichier@(+(159)|+(161)).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Séparateur de commandes

Le caractère ";" permet d'écrire plusieurs commandes sur une même ligne. Toutes les commandes sont exécutées dans l'ordre.

$ pwd
/home/toto
$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c
$ mkdir tmp; chmod 755 tmp; chown toto:toto tmp; cd tmp; pwd
/home/toto/tmp

Redirections

Les redirections sont souvent utilisées dans les commandes Linux.
Elles permettent de récupérer le résultat d'une ou plusieurs commandes dans un ou plusieurs fichiers ou de faire lire le contenu d'un fichier à une commande.

Entrée et sorties standard des commandes

Les commandes Linux ont par défaut 3 descripteurs de fichier différent.

  • Entrée standard :

L'entrée standard d'une commande correspond au descripteur de fichier 0.
Les commandes qui attendent des informations de la part de l'utilisateur déclenche une requête de lecture sur le descripteur 0.
Par défaut, ce descripteur est associé au terminal et donc par une saisie au clavier.

  • Sortie standard :

La sortie standard d'une commande correspond au descripteur de fichier 1.
Le résultat d'une commande est, par défaut, affichée à l'écran via ce descripteur mais il peut être redirigé dans un fichier.

  • Sortie d'erreur standard :

La sortie d'erreur standard d'une commande correspond au descripteur de fichier 2.
Quand une commande rencontre une erreur, celle-ci est retournée à l'écran via le descripteur 2 mais elle peut également être retournée dans un fichier.

Redirection des sorties vers un fichier

Cette redirection permet d'écrire dans un fichier le résultat d'une commande au lieu de l'afficher à l'écran.

  • Sortie standard (descripteur de fichier 1)

Simple redirection

$ commande > fichier
ou
$ commande 1> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, il est tout simplement écrasé.

Récupérer le résultat de la commande ls dans un fichier "liste"

$ ls > liste
$ cat liste
1coucou
cOucou
Coucou

Double redirection

Elle permet de concaténer le résultat d'une commande au contenu d'un fichier déjà existant.

$ commande >> fichier
ou
$ commande 1>> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, le résultat de la commande est ajouté au fichier.

$ pwd >> liste
$ cat liste
1coucou
cOucou
Coucou
/home/toto

  • Sortie d'erreur standard (descripteur de fichier 2)

Simple redirection

$ commande 2> fichier

$ ls /root 2> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

Double redirection

$ commande 2>> fichier

$ mkdir /root/tmp 2>> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
mkdir: impossible de créer le répertoire «/root/tmp»: Permission non accordée

  • Sortie standard et sortie d'erreur standard

Il est possible de rediriger plusieurs sorties sur une même ligne de commande.

$ commande 1> fichier1 2> fichier2
ou
$ commande 2> fichier2 1> fichier1

$ find /etc -name smb.conf 1> resultat 2> erreur
$ cat resultat
/etc/samba/smb.conf
$ cat erreur
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée

Si l'on ne souhaite pas afficher et/ou enregistrer les erreurs retournées, il est possible de rediriger le descripteur 2 vers un fichier spécial existant sur tous les systèmes Linux "/dev/null"

$ find /etc -name smb.conf 1> resultat 2> /dev/null
$ cat resultat
/etc/samba/smb.conf
$ cat /dev/null
$
 

Redirection de l'entrée standard

La redirection de l'entrée standard concerne toutes les commandes attendant une saisie de la part de l'utilisateur sur le descripteur 0 (saisie écran).

$ mail toto
>Coucou
>Comment vas tu ?
>^d (équivalent de CTRL+d)
$

La commande mail lit sur l'entrée standard toutes les données saisies à l'écran. La saisie se termine par la fonction CTRL+d.
Ensuite, la commande mail envoie ces données dans un message à l'utilisateur indiqué (toto).

Il est tout à fait possible d'écrire le contenu du message dans un fichier et de "l'injecter" à la commande mail sur son descripteur 0.

$ commande 0< fichier
ou
$ commande < fichier

$ echo "Coucou, comment vas tu ?" > message
$ cat message
Coucou, comment vas tu ?
$ mail toto < message

 

Redirections avancées

Rediriger les descripteurs 1 et 2 vers le même fichier :

$ commande 1> fichier1 2>&1
ou
$ commande 2> fichier2 1>&2

Le principe consiste à écrire dans un fichier le résulat du descripteur 1 dans le fichier "fichier1" (1> fichier1) puis le résultat du descripteur 2 vers le descripteur 1 (2>&1) et par conséquent dans le fichier "fichier1".

$ find /etc -name smb.conf 1> resultat 2>&1
$ cat resultat
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée
/etc/samba/smb.conf

Attention :

$ commande 2>&1 1> fichier1

Cette syntaxe n'agit pas de la même manière que les précédentes.

En effet, nous indiquons dans un premier temps de rediriger le descripteur 2 (sortie d'erreur standard) vers le descripteur 1, c'est à dire vers la sortie standard qui est, par défaut, l'affichage à l'écran, puis dans un second temps nous redirigeons le descripteur 1 (sortie standard) vers le fichier "fichier1".
En conclusion, tous les messages d'erreurs seront affichés à l'écran et le résulat de la commande dans le fichier "fichier1".

Cela revient à saisir :

$ commande 1> fichier1

La double redirection en lecture :

Elle est principalement utilisée dans les scripts shell.
Elle permet de connecter l'entrée standard d'une commande sur une portion du script

Par exemple, avec la commande mail, cela permet de terminer la saisie du texte avec un caractère ou une chaine de caractère.

$ mail toto<<end
> bonjour
> ceci est un test
> end
$

Pour mettre fin à la commande mail, il suffit simplement de saisir l'expression inscrite juste après les doubles chevrons "<<". Dans l'exemple, la commande se termine après avoir saisi "end".
Identique à la fonction ^d (CTRL+d) en console.

Utilisation dans un script :

$ cat envoiMail.sh
#!/bin/sh
mail toto<<end
Bonjour,
Ceci est un test
Voici la liste des fichiers presents dans ton repertoire :
`ls -lhtr`
end
exit 0

Exécution du script

$ sh ./envoiMail.sh

Fermeture d'un descripteur :

Il est tout à fait possible de fermé un descripteur d'entrée ou de sortie en utilisant les symboles "<&-" ou ">&-" ou "2>&-".

Descripteur d'entrée standard "0" :

$ commande <&-

Descripteur de sortie standard "1" :

$ commande >&-

Descripteur de sortie d'erreur standard "2" :

$ commande 2>&-

Tubes de communications

Un tube de communication, ou pipe en anglais, permet de faire communiquer 2 commandes.
Ce "tube" est représenté par la barre verticale "|".
Le résultat de la commande de gauche est envoyé dans le tube et récupéré par la commande de droite.
C'est à dirte que la sortie standard "1" de la commande de gauche est connecté directement à l'entrée standard "0" de la commande de droite.
La sortie d'erreur standard "2" de la commande de gauche n'est pas envoyée dans le tube.

Pour que cette utilisation est un sens il faut impérativement que la commande de gauche utilise la sortie standard "1" et que la commande de droite utilise l'entrée standard "0".

Les commandes lisant leur entrée standard sont facilement identifiable car elles demandent une saisie au clavier.

Envoyer par mail la liste des users connectés :

$ who | mail toto

Envoyer par mail la liste des fichiers d'un répertoire :

$ ls -lht | mail toto

Envoyer un message à un utilisateur connecté au système :

$ echo "coucou, comment vas tu ?" | write tata

Afficher le nombre de fichiers d'un répertoire :

$ ls | wc -l
29

Envoyer par mail le nombre de fichiers d'un répertoire :

$ ls | wc -l | mail toto

Afficher avec pagination le contenu d'un fichier :

$ cat /etc/passwd | more

Lister le contenu d'un répertoire avec pagination :

$ ls -lht /etc | more

Afficher uniquement certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7
root:/bin/bash

Envoyer par mail certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7 | mail toto

Afficher à l'écran et enregistrer dans un fichier le contenu d'un répertoire :

$ ls -t | tee listeFichiers
listeFichiers
envoiMail.sh
message
script.sh
$ cat listeFichiers
listeFichiers
envoiMail.sh
message
script.sh

Il est donc tout à fait possible de cumuler autant de commandes que l'on souhaite à partir du moment où l'on respecte l'ordre des commandes et des sorties / entrées standard.

Rediriger la sortie d'erreur standard dans un tube

$ ls -l /root 2>&1 | tee listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
$ cat listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

 

Regrouper des commandes

Le regroupement de commandes est utilisé pour rediriger les sorties standards de plusieurs commandes vers un même fichier ou vers un tube ou pour exécuter des commandes dans un même environnement.

$ date ; ls > listeFichiers
lundi 19 septembre 2011, 08:47:11 (UTC+0200)
$ cat listeFichiers
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Dans l'exemple ci-dessus, la commande date et ls sont regroupées grâce au caractère ";".
Le résultat de la commande date est affiché à l'écran alors que le résultat de la commande ls est redirigé dans le fichier "listeFichiers".

Afin de rediriger la sortie standard des 2 commandes au même endroit, ils faut obligatoirement les regroupées avec des parenthèses "()" ou des accolades "{}".

$ ( date ; ls ) > listeFichiers
$ cat listeFichiers
lundi 19 septembre 2011, 08:51:58 (UTC+0200)
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Les commandes regroupées entre parenthèses sont exécutées par le shell enfant alors que les commandes regroupées entre accolades sont exécutées par le shell parent.

Regroupement avec parenthèses :

$ pwd
/home/toto
$ ( mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ) > listeFichiers
$ cat listeFichiers
/home/toto/temp
fichierTemp
$ pwd
/home/toto

Après exécution des commandes, le répertoire courant est le même qu'avant exécution.

Regroupement avec accolades :

$ pwd
/home/toto
$ { mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ; } > listeFichiers
$ pwd
/home/toto/temp
$ cat ../listeFichiers
/home/toto/temp
fichierTemp

Après exécution des commandes, le répertoire courant n'est plus le même qu'avant exécution.

  • L'exécution de commandes regroupées entre accolades est plus rapide que les commandes regroupées entre parenthèses.
  • Le regroupement entre accolades doit obligatoirement se terminer par un point-virgule.
    Les accolades doivent toujours être suivies et précédées par une espace.

     

Généralement, le regroupement entre parenthèses est plus utilisé que le regroupement entre accolades car la syntaxe est plus simple et cela ne modifie pas le shell courant mais pour un gain de performances le regroupement entre accolades est préférable.

Exécuter des commandes en arrière-plan

Pour exécuter des commandes en arrière-plan, il suffit de rajouter à la fin le caractère "&".
Cela permet de récupérer le shell aussitôt après sans être obligé d'attendre la fin de l'exécution de la commande précédente.
Par contre, il est préférable d'utiliser la redirection de la sortie standard et de la sortie d'erreur standard.

$ find /etc -name passwd 1>listePasswd 2>/dev/null &
$ echo "On peut saisir d'autre commandes en attendant de voir le message ci-dessous une fois le processus terminé"
[1]+  Exit 1                  find /etc -name passwd > listePasswd 2> /dev/null
$ cat listePasswd
/etc/passwd
/etc/webmin/passwd
/etc/pam.d/passwd

Paramétrer son environnement de travail

Les variables d'environnement

Le commande set permet d'obtenir la liste des variables paramétrées pour le shell courant.

$ set
BASH=/bin/bash
HOME=/home/toto
HOSTNAME=myhostname
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h$
PS2='> '
PS4='+ '
SHELL=/bin/bash
...

Le caractère $ permet d'obtenir la valeur d'une variable :

$ echo $BASH
/bin/bash

Pour créer ou modifier une variable :

$ VARIABLE='valeur'
$ echo $VARIABLE
valeur

Attention, car toutes variables créées ou modifiées de cette manière ne sont valables que pour la session en cours.

$ MAVARIABLE='<= Ceci est le contenu de ma variable =>'
$ echo $MAVARIABLE
<= Ceci est le contenu de ma variable =>

Les principales variables :

HOME : cette variable contient le chemin du répertoire d'accueil de l'utilisateur.

PATH : cette variable contient une liste de répertoire dans lesquels le shell recherche toutes les commandes qui sont exécutées.
Si une commande est exécutée et qu'elle ne se trouve dans aucun des répertoires indiqués dans la variable PATH, une erreur sera retournée en indiquant que la commande est introuvable.
Si l'on souhaite exécuter une commande qui se trouve dans un répertoire non indiqué dans la variable PATH, il suffit tout simplement de modifier le contenu de la variable.

$ PATH=$PATH:/leRepertoireDeMaCommande

Pour modifier la variable PATH définitivement, il suffit d'ajouter une ligne au fichier ~/.bashrc :

> export PATH=$PATH:/mon_autre_repertoire

Ou modifier directement le fichier /etc/environment :

> PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/mon_autre_repertoire"

Pour exécuter une commande présente dans le répertoire courant, il suffit d'ajouter à la variable PATH la valeur ":." ou ":".

PWD : cette variable contient le chemin du répertoire courant.

PS1 : cette variable contient la chaine de caractères correspondant au prompt ($ en règle générale).

Pour faire apparaitre la valeur du répertoire courant dans le prompt :

$ PS1='$PWD$ '
ou
$ PS1='\w$ '

BASH : Séquence d'échappement permettant de paramétrer la variable PS1

Séquence échappement Valeur
\u Nom de l'utilisateur
\h Nom de la machine
\w Répertoire courant
\W Partie terminale du répertoire courant

PS2 : cette variable contient la chaine de caractères correspondant au prompt secondaire (> en règle générale).

$ echo 'ceci est le début
> de ma chaine de texte
> et tant que je ne saisi pas
> le caractère apostrophe
> ça continue
> '
ceci est le début
de ma chaine de texte
et tant que je ne saisi pas
le caractère apostrophe
ça continue
$

$ mail toto<<fin
> un autre exemple
>
> fin
$

TERM : cette variable contient le type du terminal de l'utilisateur.

LOGNAME : cette variable contient le nom de l'utilisateur connecté.

Exporter des variables :

Il est parfois nécessaire d'exporter une variable car toutes ne le sont pas.
Pour savoir quelles sont les variables exportées d'office il suffit d'utiliser la commande env.

$ env
TERM=xterm
SHELL=/bin/bash
CDPATH=:.:/home/
USER=toto
...

Pour exporter une variable :

$ MAVARIABLE='maValeur'
$ export MAVARIABLE

ou

$ export MAVARIABLE='maValeur'

Par exemple, en BASH, pour utiliser le programme nano (éditeur de texte) comme éditeur par défaut du programme mutt (client mail en ligne de commande), il suffit d'inscrire dans son fichier ~/.bashrc :

export EDITOR=/usr/bin/nano

et pour une prise en compte immédiate, sans être obligé de se reconnecter, saisir dans la console :

$ export EDITOR=/usr/bin/nano
Et pour vérifier :
$ set | grep EDITOR
EDITOR=/usr/bin/nano
$ env | grep EDITOR
EDITOR=/usr/bin/nano

Les options du shell

Pour activer ou désactiver les options du shell, il suffit d'utiliser la commande set avec les options -o et +o.

Activation :

$ set -o option

Désactivation :

$ set +o option

Pour visualiser  la liste des options disponibles ainsi que leur état, saisir dans une console :

$ set -o
allexport       off
braceexpand     on
emacs           on
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      on
history         on
ignoreeof       off
interactive-comments    on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

Détails de certaines options :

ignoreeof

Pour quitter le shell, il existe 3 méthodes :

  • La commande exit.
  • La commande logout.
  • La combinaison des touches ^d (CTRL+d).

Si l'option ignoreeof est activée, il n'est plus possible de quitter le shell en appuyant sur ^d.

noclobber

Quand une redirection est faite vers un fichier déjà existant, celui-çi est automatiquement écrasé sans confirmation. Pour inverser se fonctionnement, il suffit d'activer l'option noclobber.

On vérifie l'état de l'option noclobber

$ set -o | grep noclobber
noclobber       off

On redirige le résultat de la commande ls vers le fichier liste

$ ls -l > liste

On redirige le résultat de la commande pwd vers le fichier liste déjà existant

$ pwd > liste

On active l'option noclobber

$ set -o noclobber

On vérifie que l'option noclobber est bien activée

$ set -o | grep noclobber
noclobber       on

On redirige le résultat de la commande pwd vers le fichier liste déjà existant

$ pwd > liste
-bash: liste : impossible d'écraser le fichier existant

On force la redirection de la commande pwd vers le fichier liste déjà existant

$ pwd >| liste

emacs & vi

Ces 2 options permettent de paramétrer le rappel des commandes.
En ksh, ces 2 options sont désactivées.
En bash, seule l'option emacs est activée.

xtrace

Cette option est utilisée pour déboguer les scripts shell.

Les alias

Les alias permettent de créer des raccourcis vers des commandes et de les personnaliser.

Pour visualiser la liste des alias déjà existant :

$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -lF'
alias ls='ls --color=auto'
alias tarc='tar -cvzf'
alias tarx='tar -xvzf'

Pour visualiser un alias en particulier :

$ alias la
alias la='ls -A'

Pour créer un alias :

$ alias c='clear'
$ alias rm='rm -i'
$ rm liste
rm : supprimer fichier «liste» ? y

En bash, pour créer un alias définitivement, il suffit de le renseigner dans le fichier ~/.bashrc

Pour supprimer un alias :

$ unalias c
$ c
c : commande introuvable

Historique des commandes

Le shell enregistre toutes les commandes saisies dans un fichier texte.
En bash, il s'agit du fichier ~/.bash_history
En ksh, il s'agit du fichier ~/.sh_history
Pour utiliser le rappel des commandes, le shell utilise soit emacs soit vi.
En bash, c'est l'option emacs qui est activée par défaut mais il est possible d'utiliser vi.
En ksh, les 2 options sont par défaut désactivées.
Le choix d'activer l'un, désactive l'autre.

$ set -o | grep "^emacs\|^vi"
emacs           on
vi              off

Pour les utilisateurs non habitués à l'éditeur vi, il est préférable d'utiliser emacs car le rappel des commandes se fait avec les flèches du clavier.

Les fichiers d'environnement

Les fichiers d'environnement sont utilisés pour stocker de manière permanente tout le paramétrage de l'environnement de l'utilisateur.

Ce sont des scripts shell qui contiennent un ensemble de commandes Linux.

Certains scripts sont exécutés uniquement par le shell de connexion et d'autres par un shell ordinaire.

Le shell de connexion est lancé immédiatement après l'identification de l'utilisateur.

Le shell de connexion exécute en premier le script /etc/profile. Ce script contient le paramétrage commun à tous les utilisateurs.
Il recherche ensuite dans le répertoire d'accueil de l'utilisateur un script dont le nom dépend du shell utilisé.

En sh / ksh :

Le script de connexion se nomme ~/.profile

En bash :

L'un des 3 scripts suivants dans l'ordre

  1. ~/.bash_profile
  2. ~/.bash_login
  3. ~/.profile

 

En bash, le shell de connexion exécute en premier le script /etc/profile, puis le script ~/.profile et enfin le script ~/.bashrc.

C'est dans ce dernier fichier, ~/.bashrc, que l'on peut définir de nouveaux alias, exporter automatiquement de nouvelles variables, paramétrer l'auto-complétion, définir les variables PS1 et PS2, paramétrer le nombre de commandes à historiser, etc etc.

Les bases de la programmation shell

Les variables utilisateur

Le shell permet de définir ou redéfinir des variables pour l'environnement de l'utilisateur.

Il est également possible de définir d'autres variables dites variables utilisateur qui vont permettre de stocker des informations utiles durant l'exécution de scripts.

Le nomage des variables

  • Le premier caractère doit être obligatoirement une lettre minuscule, majuscule ou un tiret bas [a-zA-Z_]
  • Les caractères suivants peuvent être des lettres en minuscule, majuscule, des chiffres de 0 à 9 ou un tiret bas [a-zA-Z0-9_]

Définition d'une variable

$ variable1=valeur1
$ echo $variable1
valeur1
$

Il ne faut surtout pas mettre d'espace avant et après le signe égal (=) ainsi que dans la valeur de la variable.

Si un espace est placé avant le signe '=', le shell va interpréter la chaine 'variable1' comme étant une commande et la chaine '=valeur1' comme étant un paramètre de la commande variable1.

Pour pouvoir affecter une valeur contenant un espace, il faut obligatoirement le protéger avec des simples quotes.

$ variable2='valeur2 valeur21'
$ echo $variable2
valeur2 valeur21
$

Pour supprimer le contenu d'une variable, il faut utiliser la commande unset :

$ variable3=valeur3
$ echo $variable3
valeur3
$ set | grep variable3
variable3=valeur3
$ unset variable3
$ echo $variable3
$ set | grep variable3
$

Concaténer des variables avec une chaine de caractères :

Un exemple qui produit une erreur

$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=$fichier_$dateJour
$ echo $nouveauFichier
20111006
$

Le but de cet exemple est de concaténer la variable $fichier, un tiret bas '_', puis la variable $dateJour.
Le résultat final est erroné car au moment d'affecter la nouvelle variable nouveauFichier (ligne 3), le shell interprète $fichier_ comme étant une variable à part entière puisque le tiret bas '_' est autorisé dans le nommage des variables.
La variable $fichier_ n'existant pas, celle ci est égale à "blanc".
Pour indiquer au shell qu'il faut interpréter le tiret bas '_' comme étant une chaine de caractères, il faut obligatoirement entourer la variable qui le précède avec des {}.

$ fichier=monFichier
$ dateJour=20111006
$ nouveauFichier=${fichier}_$dateJour
$ echo $nouveauFichier
monFichier_20111006
$

Substituer des variables :

Le shell offre la possibilité d'attribuer une valeur par défaut aux variables non initialisées ou au contraire initialisées.

Expression ${variable:-valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression est égale au contenu de valeur

$ jour=mardi
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : mardi
$ unset jour
$ echo "Nous sommes : ${jour:-dimanche}"
Nous sommes : dimanche
$ echo $jour
$

Expression ${variable:=valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression et la variable sont égale au contenu de valeur

$ jour=mardi
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : mardi
$ echo $jour
mardi
$ unset jour
$ echo "Nous sommes : ${jour:=dimanche}"
Nous sommes : dimanche
$ echo $jour
dimanche
$

Expression ${variable:+valeur}

  • Si la variable n'est pas vide, l'expression est égale au contenu de valeur
  • Si la variable est vide, l'expression est égale à variable, donc vide

$ bool=1
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche : true
$ unset bool
$ echo "Si bool=1 alors j'affiche : ${bool:+true}"
Si bool=1 alors j'affiche :
$

Expression ${variable:?message}

  • Si la variable n'est pas vide, l'expression est égale au contenu de la variable
  • Si la variable est vide, l'expression est égale au nom de la variable suivi du contenu de message

Dans un script, si la variable est vide, le message est affiché et le script se termine aussitôt

$ nombre=56
$ echo ${nombre:?"nombre indefini"}
56
$ unset nombre
$ echo ${nombre:?"nombre indefini"}
-bash: nombre: nombre indefini
Affichage du message par défaut
$ echo ${nombre:?}
-bash: nombre : parametre vide ou non defini
$

Substitution de commandes

Les caractères de substitution permettent de remplacer une commande par le résultat de son exécution.

Par exemple, comment faire pour afficher un message affichant la date du jour.

$ echo "Nous sommes le date"
Nous sommes le date
$

Il suffit d'entourer la commande avec les caractères de substitution :

  • Les quotes inversés `commande`
  • $(commande)

$ echo "Nous sommes le `date`"
Nous sommes le vendredi 7 octobre 2011, 08:39:49 (UTC+0200)
$ echo "Nous sommes le $(date)"
Nous sommes le vendredi 7 octobre 2011, 08:40:05 (UTC+0200)
$

Idem pour initialiser une variable :

$ date=`date`
$ echo $date
vendredi 7 octobre 2011, 08:42:34 (UTC+0200)
$

Les caractères de protection

Les caractères de protection servent à utiliser les caractères spéciaux du shell comme n'importe quels caractères.

Les simples quotes

Les simples quotes désactivent tous les caractères spéciaux du shell. Ils doivent être utilisés en nombre pair sur une ligne de commande.

$ echo $PWD
/home/toto

Avec les simples quotes, la fonction du caractère '$' est désactivée :

$ echo '$PWD'
$PWD
$

Afficher tous les fichiers d'un dossier à l'aide de la fonction 'echo' et du caractère spécial '*'

$ echo *
1coucou cOucou Coucou coucou.r ...

Avec les simples quotes, la fonction du caractère '*' est désactivée :

$ echo '*'
*
$

$ echo Bonjour $(logname)
Bonjour toto
$ echo 'Bonjour $(logname)'
Bonjour $(logname)
$

La quote ne se protège pas elle-même.
Tant que les quotes ne sont pas par pair, le shell affiche le prompt secondaire

$ echo 'J'affiche un quote supplémentaire'
>

L'antislash '\'

L'antislash désactive la fonctionnalité du caractère qui le suit.

$ echo La liste des fichiers * du dossier $PWD
La liste des fichiers 1coucou cOucou Coucou coucou.r du dossier /volume1/home/ronan/atuer/toto
$ echo La liste des fichiers \* du dossier \$PWD
La liste des fichiers * du dossier $PWD
$

L'antislash se protège lui-même

$ echo \\
\
$

$ echo L\'antislash protège la quote
L'antislash protège la quote
$

Le premier antislash protège le second, le troisième protège le '$'

$ echo \\\$PWD
\$PWD
$

Les guillemets

Les guillemets protège tous les caractères spéciaux du shell à l'exception du dollar $, des quotes inversés ``, du dollar et des parenthèses $( ), de l'antislash \ et des guillemets ".

$ echo "
> La variable \$HOME est substituée par $HOME
> La commande \$(logname) est exécutée : $(logname)
> Je peux afficher des > et des |
> Et même des guillemets \"."
La variable $HOME est substituée par /home/toto
La commande $(logname) est exécutée : toto
Je peux afficher des > et des |
Et même des guillemets ".
$

En règle générale, il est préférable d'utiliser la commande echo avec des guillemets

Récapitulatif des caractères spéciaux

Caractères Signification
espace - tabulation - saut de ligne Séparateurs de mots sur la ligne de commande
& Arrière-plan
|   <<   <   >   >> Tubes et redirections
() et {} Regroupement de commandes
; Séparateur de commandes
*   ?   [ ]   ?( )   +( )   *( )   !( )   @( ) Caractères de génération de noms de fichier
$ et ${ } Valeur d'une variable
``   $( ) Substitution de commandes
' '   " "   \ Caractères de protection

 

Interprétation d'une ligne de commande

Les caractères spéciaux du shell sont interprétés dans un ordre bien précis.

  1. Isolement des mots séparés par les caractères espace, tabulation, saut de ligne.
  2. Traitement des caractères de protections ( ' ', " ", \).
  3. Substitution des variables ($)
  4. Substitution des commandes (``, $( ) )
  5. Substitution des caractères de génération des noms de fichiers (*, ?, [ ], ?( ), +( ), *( ), !( ), @( ))
  6. Traitement des tubes et redirections (|, <<, <, >, >>)
  7. Lancement de la commande

Exemple :

$ echo "\$HOME --> $HOME
> Mon login --> $(logname)
> Nous sommes le `date`
> La liste des fichiers :" * | nl > test ; cat test
     1  $HOME --> /home/toto
     2  Mon login --> toto
     3  Nous sommes le jeudi 20 octobre 2011, 08:27:05 (UTC+0200)
     4  La liste des fichiers : fichier1 fichier2 fichier3 fichier4
$

Interprétation :

  1. \$HOME (traitement des caractères de protection)
  2. $HOME (substitution des variables)
  3. $(logname) et `date` (substitution des commandes)
  4. * (substitution des caractères de génération des noms de fichiers)
  5. | nl > test (traitement des tubes et redirections)
  6. echo "...." ; cat test (exécution des commandes)

Ecriture et exécution d'un script shell

Un script shell est un fichier texte contenant des commandes Linux internes ou externes.

Le nom du script peut avoir une extension mais cela n'est pas obligatoire.

En règle générale, tous les scripts portent l'extension .sh

Exemple :

$ nl monPremierScript.sh
     1  pwd
     2  echo "Liste des fichiers commençant par la lettre C"
     3  ls -lh c*
$

Exécution :

$ bash monPremierScript.sh
/home/toto
Liste des fichiers commençant par la lettre C
-rw-r--r-- 1 toto toto 0 2011-09-14 13:55 cOucou
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.r
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.s
-rw-r--r-- 1 toto toto 0 2011-09-15 07:20 coucou.t
-rw-r--r-- 1 toto toto 0 2011-09-15 07:21 coucou.uvw
$

Exécuter un script par le shell enfant

Il existe 3 méthodes pour exécuter un script.

La première méthode consiste à appeler la commande bash, sh, ksh etc.. (l'interpréteur de commande désiré) suivi du nom du script.

$ bash monPremierScript.sh

Dans ce cas, le droit de lecture sur le fichier est suffisant.

$ ls -l monPremierScript.sh
-rw-r--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$

La seconde méthode consiste à utiliser l'entrée standard de la commande bash, sh, ksh etc.. afin de lui faire lire le contenu du script.

$ bash < monPremierScript.sh

Dans ce cas, le droit de lecture sur le fichier est également suffisant.
Cette méthode est très peu utilisée.

La troisième méthode consiste à rendre le script exécutable.

$ chmod u+x monPremierScript.sh
$ ls -l monPremierScript.sh
-rwxr--r-- 1 toto toto 68 2011-10-20 08:52 monPremierScript.sh
$ ./monPremierScript.sh

Choisir un interpréteur de commande :

Pour indiquer au script quel interpréteur de commande utiliser, il faut indiquer sur la première ligne du script les caractères #! suivi du chemin absolu de l'interpréteur.

Par exemple, pour utiliser l'interpéteur de commande BASH, on recherche son chemin absolu :

$ whereis bash
bash: /bin/bash /etc/bash.bashrc /usr/share/man/man1/bash.1.gz
$

La résultat de la commande nous indique le répertoire /bin/bash
Inscrire sur la première ligne du script #! /bin/bash

$ nl monPremierScript.sh
     1  #! /bin/bash
     2  pwd
     3  echo "Liste des fichiers commençant par la lettre C"
     4  ls -lh c*
$

De cette manière, le script sera toujours exécuté avec l'interpréteur de commande BASH.

Exécuter un script par le shell courant

L'exécution d'un script par le shell courant permet de modifier l'environnement du shell courant.

Les droits d'exécution sur le script ne sont pas nécessaires.
L'utilisation de la commande interne "." est nécessaire pour l'exécution.

Par exemple, pour modifier l'éditeur par défaut, il est nécessaire de modifier la variable environnement EDITOR.

$ env | grep EDITOR
EDITOR=/usr/bin/vi
echo $EDITOR
/usr/bin/vi
nano .profile
export EDITOR='/usr/bin/nano'
^o (enregistrer)
^x (quitter)
$ . .profile
$ echo $EDITOR
/usr/bin/nano
env | grep EDITOR
EDITOR=/usr/bin/nano
$

Attention, l'appel de la commande interne "." est obligatoirement suivi d'un espace et d'un script shell.

Les commentaires

Il est possible d'écrire dans un script des commentaires afin de faciliter la compréhension du script.

Les commentaires sont désignés à l'aide du caractère #.
Ne pas confondre avec #! inscrit sur la première ligne du script qui sert à renseigner l'interpréteur de commande.

$ nl monPremierScript.sh
     1    #! /bin/bash
       
     2    # Ceci est un commentaire
     3    # La commande PWD indique le répertoire courant
     4    pwd
     5    # La commande ECHO permet d'afficher du texte et des informations
     6    echo "Liste des fichiers commençant par la lettre C"
     7    # La commande LS permet de lister tous les fichiers commençant par la lettre "c"
     8    ls -lh c*
$

Les variables réservées du shell

Dans un script, il est possible de récupérer en lecture un certain nombre de variables réservées.
Ces variables sont initialisées par le shell et contiennent des informations diverses et variées.

Les paramètres positionnels

Les scripts shell sont capables de récupérer des arguments placés juste après l'appel du script.

  • $# indique le nombre d'arguments passés au script
  • $0 indique le nom du script
  • $1, $2, $3 ... $9 contient la valeur de l'argument 1, 2, 3 ... 9

En KSH et en BASH, il est possible d'aller au delà de 9 arguments en utilisant les variables spéciales ${10}, ${11} etc...
Les accolades sont obligatoires à partir du moment où le nom de la variable contient plus d'un chiffre.

  • $* et $@ contient la liste de tous les arguments

Exemple :

$ nano monSecondScript.sh
#! /bin/bash
echo "Il y a $# arguments passés au script"
echo "Le script se nomme $0"
echo "La valeur du premier argument : $1"
echo "La valeur du second argument : $2"
echo "La valeur du troisième argument : $3"
echo "Les valeurs de tous les arguments : $*"
^o (enregistrer)
^x (quitter)
$ chmod u+x monSecondScript.sh
$ ./monSecondScript.sh arg1 arg2 arg3 arg4 arg5
Il y a 5 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : arg1
La valeur du second argument : arg2
La valeur du troisième argument : arg3
Les valeurs de tous les arguments : arg1 arg2 arg3 arg4 arg5
$

Exemple avec 3 arguments :

$ ./monSecondScript.sh 25 + 15
Il y a 3 arguments passés au script
Le script se nomme ./monSecondScript.sh
La valeur du premier argument : 25
La valeur du second argument : +
La valeur du troisième argument : 15
Les valeurs de tous les arguments : 25 + 15
$

Tous les arguments doivent être séparés les uns des autres par un espace ou une tabulation

La commande shift

La commande shift permet de décaler la liste des arguments d'une ou plusieurs positions vers la gauche.

Cette commande est utilisée dans le cas où le premier argument n'a pas à subir le même traitement que les suivants.
Dans ce cas, le premier argument doit être sauvegardé dans une variable et l'exécution de la commande shift entraine le remplacement du premier argument par le second et ainsi de suite.

$ nl monTroisiemeScript.sh
     1  #! /bin/bash
     2  # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
     3  # Affichage des variables avant l'utilisation de la commande shift
     4  echo -e "Affichage avant exécution de la commande shift\n"
     5  echo "1er argument \$1 : $1"
     6  echo "1er argument \$2 : $2"
     7  echo "1er argument \$3 : $3"
     8  echo "1er argument \$4 : $4"
     9  echo "Tous les arguments \$* : $*"
    10  echo -e "Le nombre d'argument \$# : $#\n"
    11  # On sauvegarde le premier argument dans la variable rep
    12  rep=$1
    13  # On décale tous les arguments avec la commande shift
    14  shift
    15  # Affichage des variables après l'exécution de la commande shift
    16  echo -e "Affichage après exécution de la commande shift\n"
    17  echo "1er argument \$1 : $1"
    18  echo "1er argument \$2 : $2"
    19  echo "1er argument \$3 : $3"
    20  echo "1er argument \$4 : $4"
    21  echo "Tous les arguments \$* : $*"
    22  echo -e "Le nombre d'argument \$# : $#\n"
    23  # Création du répertoire
    24  mkdir $rep
    25  # On se positionne dans le nouveau répertoire
    26  cd $rep
    27  # Création des fichiers dans le nouveau répertoire
    28  for fichier in $*
    29  do
    30          touch $fichier
    31  done
$ chmod u+x monTroisiemeScript

Exécution du script :

$ ./monTroisiemeScript.sh test fic1 fic2 fic3 fic4 fic5 fic6
Affichage avant exécution de la commande shift
1er argument $1 : test
1er argument $2 : fic1
1er argument $3 : fic2
1er argument $4 : fic3
Tous les arguments $* : test fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 7
Affichage après exécution de la commande shift
1er argument $1 : fic1
1er argument $2 : fic2
1er argument $3 : fic3
1er argument $4 : fic4
Tous les arguments $* : fic1 fic2 fic3 fic4 fic5 fic6
Le nombre d'argument $# : 6
ls -l ./test/
total 0
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic1
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic2
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic3
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic4
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic5
-rw-r--r-- 1 toto toto 0 2011-10-25 09:01 fic6
$

Code de retour d'une commande

Toutes les commandes Linux retournent un code d'erreur compris entre 0 et 255.
La valeur 0 représente la valeur vrai (succès de la commande).
Les valeurs supérieur à 0 représente la valeur faux (échec de la commande).
Le code erreur de la dernière commande utilisée est contenu dans la variable $?

$ ls f*
f1chier               fichier159159.log  fichier159.log  fichier161.log  fichier1.abc  fichier1.c   fset
fichier159159159.log  fichier159160.log  fichier160.log  fichier1.a      fichier1.b    fichier.log
$ echo $?
0
$ ls 2*
ls: impossible d'accéder à 2*: Aucun fichier ou dossier de ce type
$ echo $?
2

Dans un script shell, le test du code de retour d'une commande permet d'effectuer différentes actions.

Un script shell étant lui-même une commande, il est possible de lui faire retourner un code d'erreur avec la commande exit.

Par exemple, il est possible d'indiquer dans un script exit 1 afin d'indiquer une erreur rencontrée et/ou exit 0 afin d'indiquer que tout c'est bien déroulé.

Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments et renvoi le code erreur 1 si c'est faux.

$ nl ./script.sh
1  #! /bin/bash
2  # Le script doit recevoir au minimum 2 arguments
3  if [ $# -lt 2 ]
4  then
5          # Si le nombre d'arguments est inférieur à 2
6          # on retourne le code erreur 1
7          echo "Nombre arguments incorrect"
8          exit 1
9  else
10          # Si le nombre d'arguments est supérieur ou égal à 2
11          # on retourne le code erreur 0
12          echo "Nombre arguments correct"
13          exit 0
14  fi
$

Exécution du script :

$ ./script.sh
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test
Nombre arguments incorrect
$ echo $?
1
$ ./script.sh test test2
Nombre arguments correct
$ echo $?
0
$

Autres variables spéciales

La variable $$ représente le PID du shell qui interprète le script.
La valeur de cette variable est la même pendant toute la durée d'exécution du script et différente à chaque utilisation du script.

Exemple :

Dans ce script, la variable $$ est utilisée pour générer le nom d'un dossier différent à chaque exécution du script.

$ nl monQuatriemeScript.sh
     1  #! /bin/bash
     2  dossierTemp=dossier_$$
     3  echo "Création du dossier \"$dossierTemp\""
     4  mkdir $dossierTemp
     5  cd $dossierTemp
     6  for (( i=0 ; i<10 ; i++)) do
     7          touch fichier_$i
     8  done
     9  exit 0
$

$ ./monQuatriemeScript.sh
Création du dossier "dossier_26563"
$ ./monQuatriemeScript.sh
Création du dossier "dossier_26581"
$

La variable $! représente le PID d'une commande exécutée en arrière plan.

exemple :

$ nl monCinquiemeScript.sh
     1  #! /bin/bash
     2  echo "Le script est exécuté sous le PID $$"
     3  find /etc -name $1 1> resultat 2> /dev/null &
     4  echo "La commande FIND est en cours d'exécution sous le PID $!"
     5  ps
     6  exit 0
$

$ ./monCinquiemeScript.sh hosts.allow
Le script est exécuté sous le PID 29999
La commande FIND est en cours d'exécution sous le PID 30000
  PID TTY          TIME CMD
19703 pts/0    00:00:00 bash
29999 pts/0    00:00:00 monCinquiemeScr
30000 pts/0    00:00:00 find
30001 pts/0    00:00:00 ps
$

La commande read

La commande read lit son entrée standard et affecte les valeurs saisies dans la ou les variables passées en argument.

Lecture au clavier

$ read var1
coucou
$ echo $var1
coucou
$

Tous les mots saisis sont stockés dans la variables var1

$ read var1
coucou tout le monde
$ echo $var1
coucou tout le monde
$

Le premier mot est stocké dans var1 et le second dans var2

$ read var1 var2
A bientot
$ echo $var1
A
$ echo $var2
bientot
$

Le premier mot est stocké dans var1 et les autres dans var2

$ read var1 var2
A bientot tout le monde
$ echo $var1
A
$ echo $var2
bientot tout le monde
$

Le mot est stocké dans var1 et var2 est vide

$ read var1 var2
Bonjour
$ echo $var1
Bonjour
$ echo $var2
 
$

En KSH, il est possible d'associer un message à la commande read

$ read var1?"Saisir une valeur : "
Saisir une valeur : coucou
$ echo $var1
coucou
$

Code de retour

Le code retour de la commande read est vrai (0) si un texte est saisi et faux (1) si la commande est interrompue en appuyant sur ctrl+d (^d)

$ read var1
coucou
$ echo $?
0
$ echo $var1
coucou
$

$ read var1
[Entrée]
$ echo $?
0
$ echo $var1
 
$

$ read var1
$ ^d
$ echo $?
1
$ echo $var1
 
$

La variable IFS

Cette variable contient les caractères qui permettent de scinder les entrées au clavier.
Par défaut elle contient les caractères espace, tabulation (\t) et saut de ligne (\n)

Le contenu de cette variable peut être modifié

$ OLDIFS="$IFS"
$ IFS="|"                                       # modification du caractère de séparation
$ read var1 var2 var3
colonne1|colonne2|colonne3
$ echo $var1
colonne1
$ echo $var2
colonne2
$ echo $var3
colonne3
$ IFS="$OLDIFS"
$

Les expression $IFS et $OLDIFS doivent obligatoirement être placées entre guillemets pour que les caractères internes ne soient pas interprétés par le shell

En BASH, il est possible d'associer un message à la commande read grâce à l'option -p :

$ read -p "Votre nom ? " NAME
Votre nom ? toto
$ echo $NAME
toto

En y ajoutant l'option -s , il est possible d'utiliser la commande read pour saisir un mot de passe :

$ read -s -p "Votre passwd ? " PASSWD; echo
Votre passwd ?
$ echo $PASSWD
aaaa

Avec l'option -s, il ne faut pas oublier d'ajouter la commande echo pour afficher un saut de ligne.

Exécution de tests

La commande test permet de faire des tests sur des fichiers, des chaines de caractères et des nombres.

Elle renvoie le code retour 0 (vrai) ou 1 (faux) qu'il est possible de consulter en affichant la valeur de $?

Il existe 2 syntaxes pour utiliser la commande test

$ test expression

$ [ expression ]

La paire de crochet signifie la même chose que la commande test.
Les crochets ouvrants et fermants doivent obligatoirement être suivis et précédés d'un espace.

La commande test

Tests sur les fichiers

Expression Code de retour
-b FILE Vrai si le fichier existe et est du type spécial bloc
-c FILE Vrai si le fichier existe et est du type spécial caractère
-d FILE Vrai si le fichier existe et est du type répertoire
-e FILE Vrai si le fichier existe
-f FILE Vrai si le fichier existe et est du type ordinaire
-G FILE Vrai si le fichier existe et si l'utilisateur appartient au groupe propriétaire du fichier
-h FILE Vrai si le fichier existe et est du type lien symbolique
-L FILE Vrai si le fichier existe et est du type lien symbolique (idem -h)
-O FILE Vrai si le fichier existe et si l'utilisateur est le propriétaire du fichier
-r FILE Vrai si le fichier existe et est accessible en lecture
-s FILE Vrai si le fichier existe et n'est pas vide
-S FILE Vrai si le fichier existe et est du type socket
-w FILE Vrai si le fichier existe et est accessible en écriture
-x FILE Vrai si le fichier existe et est exécutable
FILE1 -ef FILE2 Vrai si les fichiers ont le même lien physique
FILE1 -nt FILE2 Vrai si FILE1 est plus récent que FILE2
FILE1 -ot FILE2 Vrai si FILE1 est plus ancien que FILE2

Exemple :

Le fichier /etc/group est un fichier ordinaire

$ test -f /etc/group
$ echo $?
0
$

Le fichier /etc/groupe n'existe pas (test avec l'autre syntaxe)

$ [ -f /etc/groupe ]
$ echo $?
1
$

Le fichier /etc/init.d existe et est un répertoire

$ [ -d /etc/init.d ]
$ echo $?
0
$

Le fichier /etc/group n'est pas un répertoire ou il n'exsite pas

$ test -d /etc/group
$ echo $?
1
$

Le fichier contenu dans la variable file n'est pas un répertoire ou n'existe pas

$ file=/etc/group
$ test -d $file
$ echo $?
1
$

L'utilisateur n'a pas le droit d'écriture sur le fichier /etc/group

$ [ -w /etc/group ]
$ echo $?
1
$

Tests sur les chaines de caractères

Expression Code de retour
-n STRING Vrai si la longueur de la chaine n'est pas égale à 0
-z STRING Vrai si la longueur de la chaine est égale à 0
STRING1 = STRING2 Vrai si les 2 chaines sont égales
STRING1 != STRING2 Vrai si les 2 chaines sont différentes
STRING Vrai si la chaine n'est pas vide (idem -n)

Pour les tests sur les chaines de caractères, il est recommandé de mettre le nom des variables entre guillemets.

Exemple :

Avec les variables suivantes :

$ str1="test1"
$ str2="test2"
$ str3="test1"
$

La variable $str1 n'est pas vide

$ [ -n "$str1" ]
$ echo $?
0
$

$ [ "$str1" ]
$ echo $?
0

$ [ -z "$str1" ]
$ echo $?
1
$

La variable $str4 est vide

$ [ -n "$str4" ]
$ echo $?
1
$

$ [ -z "$str4" ]
$ echo $?
0
$

$ [ "$str4" ]
$ echo $?
1
$

$str1 & $str3 sont identiques

$ [ "$str1" = "$str3" ]
$ echo $?
0
$

$ [ "$str1" != "$str3" ]
$ echo $?
1
$

$str1 & $str2 sont différentes

$ [ "$str1" != "$str2" ]
$ echo $?
0
$

$ [ "$str1" = "$str2" ]
$ echo $?
1
$

Tests sur les nombres

Expression Code de retour
INT1 -eq INT2 Vrai si INT1 est égal à INT2 (=)
INT1 -ge INT2 Vrai si INT1 est supérieur ou égal à INT2 (>=)
INT1 -gt INT2 Vrai si INT1 est supérieur à INT2 (>)
INT1 -le INT2 Vrai si INT1 est inférieur ou égal à INT2 (<=)
INT1 -lt INT2 Vrai si INT1 est inférieur à INT2 (<)
INT1 -ne INT2 Vrai si INT1 est différent de INT2 (!=)

Exemple :

Avec les variables suivantes

$ int1=1
$ int2=2
$ int3=3
$ int4=2
$

$int2 & $int4 sont égaux

$ [ $int2 -eq $int4 ]
$ echo $?
0

$int2 est supérieur ou égal à $int4

$ [ $int2 -ge $int4 ]
$ echo $?
0

$int3 est supérieur ou égal à $int1

$ [ $int3 -ge $int1 ]
$ echo $?
0

$int4 n'est pas supérieur ou égal à $int3

$ [ $int4 -ge $int3 ]
$ echo $?
1

$int3 est supérieur à $int2

$ [ $int3 -gt $int2 ]
$ echo $?
0

$int2 n'est pas supérieur à $int3

$ [ $int2 -gt $int3 ]
$ echo $?
1

$int2 est inférieur ou égal à $int3

$ [ $int2 -le $int3 ]
$ echo $?
0

$int2 est inférieur à $int3

$ [ $int2 -lt $int3 ]
$ echo $?
0

$int2 est inférieur ou égal à $int4

$ [ $int2 -le $int4 ]
$ echo $?
0

$int2 est différent de $int3

$ [ $int2 -ne $int3 ]
$ echo $?
0

$int2 n'est pas différent de $int4

$ [ $int2 -ne $int4 ]
$ echo $?
1
$

Les opérateurs

Opérateur Signification
! Négation
-a ET
-o OU

Les opérateurs sont exécutés avec une priorité bien précise :

  1. ! (négation)
  2. -a (ET)
  3. -o (OU)

 

Le fichier /etc/group n'est pas un répertoire

$ [ ! -d /etc/group ]
$ echo $?
0
$

Le fichier monPremierScript.sh existe et est exécutable

$ [ -f monPremierScript.sh -a -x monPremierScript.sh ]
$ echo $?
0
$

Le fichier monPremierScript.sh n'est pas un répertoire mais il est exécutable

$ [ -d monPremierScript.sh -o -x monPremierScript.sh ]
$ echo $?
0
$

Il est possible de modifier la priorité d'exécution des opérateurs en utilisant des paranthèses.
\(........\)
Les parenthèses doivent être protégées par des antislashes afin de ne pas être interprétées par le shell comme étant un regroupement de commandes.

L'utilisateur doit avoir le droit d'écriture sur le fichier fic1 et le fichier fic4 ou fic7 doit exister

$ ls
fic1  fic2  fic3  fic4  fic5  fic6  script.sh
$ [ -w fic1 -a \( -e fic4 -o -e fic7 \) ]
$ echo $?
0
$

Il doit toujours y avoir un espace autour des opérateurs !, -a et -o.

Exemples d'utilisation

Utilisation de la commande test avec la structure de controle if.

Principe d'utilisation :

if  commande1 
then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

if  commande1  ; then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

La commande1 est exécutée, si son code retour ($?) vaut 0 (vrai) alors les commandes 2 & 3 sont exécutées, sinon c'est la commande4 qui est exécutée (code retour de la commande1 supérieur à 0 - faux).

Exemple :

Dans l'exemple suivant, le script test s'il reçoit bien au minimum 2 arguments.
Dans le cas contraire, le script affiche un message indiquant que le nombre d'arguments est incorrect et se termine en retournant un code erreur 1.

$ nl monTroisiemeScript.sh
     1  #! /bin/bash
     2  # Ce script doit recevoir en premier argument le nom d'un dossier puis des noms de fichiers pour les suivants
     4  # Ce script doit recevoir au minimum 2 arguments
     5  # Le premier étant le nom d'un dossier
     6  # Les suivants étant les noms des fichiers
     7  # On test si il y a 2 arguments au minimum et on retourne le code erreur 1 si c'est faux
     8  if  [ $# -lt 2 ] 
     9  then
    10          echo "Nombre d'arguments incorrect !!!"
    11          exit 1
    12  fi
    13  # On sauvegarde le premier argument dans la variable rep
    14  rep=$1
    15  # On décale tous les arguments avec la commande shift
    16  shift
    17  # Création du répertoire
    18  mkdir $rep
    19  # On se positionne dans le nouveau répertoire
    20  cd $rep
    21  # Création des fichiers dans le nouveau répertoire
    22  for fichier in $*
    23  do
    24          touch $fichier
    25  done
    26  exit 0

$ ./monTroisiemeScript.sh test
Nombre d'arguments incorrect !!!
$ echo $?
1
$

$ ./monTroisiemeScript.sh test3 coucou
$ echo $?
0
$

La commande [[ ]]

La commande [[ ]] est une version améliorée de la commande test.
Tous les opérateurs utilisés avec la commandes test restent valables à l'exception des opérateurs logiques -a et -o respectivement remplacés par && et ||.

Tests sur les chaines

Contrairement à la commande test, il n'est plus nécessaire d'entourer les variables avec des guillemets.

Les différentes syntaxes utilisables :

$ echo $null
 
$ test -z "$null"
$ echo $?
0
$ [ -z "$null" ]
$ echo $?
0
$ [[ -z $null ]]
$ echo $?
0
$ [[ -z "$null" ]]
$ echo $?
0
$ [[ $null = "" ]]
$ echo $?
0
$

Les opérateurs suivants ont été ajoutés :

Opérateurs Code de retour
$chaine = modele Vrai si $chaine correspond au modèle
$chaine != modele Vrai si $chaine ne correspond pas au modèle
$chaine1 < $chaine2 Vrai si $chaine1 est lexicographiquement avant $chaine2
$chaine1 > $chaine2 Vrai si $chaine1 est lexicographiquement après $chaine2

En utilisant les expressions, il est possible de comparer des chaines à des modèles identiques à celles permettant le remplacement des noms de fichiers.

Caractères spéciaux pour modèles de chaines de caractères Signification
Caractères spéciaux valables dans tous les shells :
* 0 à n caractères
? 1 caractère quelconque
[abc] 1 caractère parmis ceux inscrits entre les crochets
[!abc] 1 caractère ne faisant pas partie de ceux inscrits entre les crochets
Caractères spéciaux non valides en Bourne Shell.
En bash, il faut activer l'option extglob (shopt -s extglob)
?(expression) de 0 à 1 fois l'expression
*(expression) de 0 à n fois l'expression
+(expression) de 1 à n fois l'expression
@(expression) 1 fois l'expression
!(expression) 0 fois l'expression
?(expression1 | expression2 | ...)
*(expression1 | expression2 | ...)
+(expression1 | expression2 | ...)
@(expression1 | expression2 |...)
!(expression1 | expression2 | ...)
alternatives

Exemple :

Le script suivant test si le numéro de téléphone saisi correspond bien au format +33240346523 ou 0240346523

$ nl test_telephone.sh
     1  #! /bin/bash
 
     2  echo -e "Saisir un numéro de téléphone : \c"
     3  read telephone
 
     4  # Test si le téléphone saisi est de la forme
     5  # +33240346523 ou 0240346523
 
     6  if [[ $telephone = @(+33)@([1-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ||
     7        $telephone = @(0)@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9])@([0-9]) ]]
     8  then
     9          echo "Le numéro est correct"
    10          exit 0
    11  else
    12          echo "Le numéro est incorrect"
    13          exit 1
    14  fi
$ ./test_telephone.sh
Saisir un numéro de téléphone : 0240020202
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33256985478
Le numéro est correct
$ ./test_telephone.sh
Saisir un numéro de téléphone : 2356958457
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : +33025146987
Le numéro est incorrect
$ ./test_telephone.sh
Saisir un numéro de téléphone : g52365
Le numéro est incorrect
$

Tests logiques

Rappels :

  • Les opérateurs -a et -o sont respectivement remplacés par && et ||
  • Les parenthèses n'ont plus besoin d'être protégées
Commande test ( [ ] ) Commande [[ ]] Signification
\(.....\) (.....) Regroupement d'expressions
! ! Négation
-a && ET logique
-o || OU logique

Exemple avec la commande test :

if [ -w $fic1 -a \( -e $rep1 -o -e $rep2 \) ]
then
.....

Exemple avec la commande [[ ]] :

if [[ -w $fic1 && ( -e $rep1 || -e $rep2 ) ]]
then
.....

Les opérateurs du shell

Ces opérateurs permettent d'exécuter ou non une commande en fonction du code de retour d'une autre commande.

Opérateur Signification
&& ET logique
|| OU logique

 

Evaluation de l'opérateur &&

Syntaxe :

commande1 && commande2

La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 0 (vrai).

Le code de retour global est égal à 0 (vrai) si le code de retour de chaque commande est égal à 0 (vrai).

Exemple :

Le répertoire temp/temp2 n'existe pas donc la commande cd n'est pas exécutée

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $?                     # code de retour de la commande [[ ]]
1
$ pwd
/home/toto
$

Le répertoire temp/temp2 existe donc la commande cd est exécutée

$ mkdir temp/temp2
$ pwd
/home/toto
$ [[ -d temp/temp2 ]] && cd temp/temp2
$ echo $?                     # code de retour de la commande globale
0
$ pwd
/home/toto/temp/temp2
$

Le répertoire temp/temp2 existe mais le répertoire temp/temp3 n'existe pas donc la commande cd retourne un code erreur égal à 1

$ [[ -d temp/temp2 ]] && cd temp/temp3
-bash: cd: temp/temp3: Aucun fichier ou dossier de ce type
$ echo $?                     # code de retour de la commande cd
1
$

Ces actions peuvent également être exécutées avec la structure de commande if

$ pwd
/home/toto
$ ls -d temp/temp2
temp/temp2
$ if [[ -d temp/temp2 ]]
> then
> cd temp/temp2
> fi
$ echo $?
0
$ pwd
/home/toto/temp/temp2
$

Evaluation de l'opérateur ||

Syntaxe :

commande1 || commande2

La seconde commande (commande2) est exécutée uniquement si le code de retour de la commande (commande1) est égale à 1 (faux).

Le code de retour global est égal à 0 (vrai) si au moins une des commandes retourne un code égal à 0 (vrai).

Exemple :

Le répertoire temp/temp2 n'existe pas donc la commande echo est exécutée

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
Le répertoire n'existe pas
$ echo $?
0
$

Le répertoire temp/temp2 existe donc la commande echo n'est pas exécutée

$ mkdir temp/temp2
$ ls -d temp/temp2
temp/temp2
$ [[ -d temp/temp2 ]] || echo "Le répertoire n'existe pas"
$ echo $?
0
$

Ces actions peuvent également être exécutées avec la structure de commande if

$ ls -d temp/temp2
ls: impossible d'accéder à temp/temp2: Aucun fichier ou dossier de ce type
$ if [[ ! -d temp/temp2 ]]
> then
> echo "Le répertoire n'existe pas"
> fi
Le répertoire n'existe pas
$

Ne pas confondre les opérateurs du shell && et || qui effectuent une opération logique entre deux commandes et les opérateurs && et || de la commande [[ ]] qui sont internes à celle-ci

L'arithmétique

La commande expr

Syntaxe :

expr $nbr1 opérateur $nbr2
expr $chaine : expression régulière

Opérateurs :

Attention, certains opérateurs ayant une signification particulière pour le shell, ceux ci doivent être protégés par un antislash.

Opérateurs Signification
Opérateurs arithmétiques
$nb1 + $nb2 Addition
$nb1 - $nb2 Soustraction
$nb1 \* $nb2 Multiplication
$nb1 / $nb2 Division
$nb1 % $nb2 Modulo
Opérateurs de comparaison
$nb1 \> $nb2 VRAI si $nb1 est strictement supérieur à $nb2
$nb1 \>= $nb2 VRAI si $nb1 est supérieur ou égal à $nb2
$nb1 \< $nb2 VRAI si $nb1 est strictement inférieur à $nb2
$nb1 \<= $nb2 VRAI si $nb1 est inférieur ou égal à $nb2
$nb1 = $nb2 VRAI si $nb1 est égal à $nb2
$nb1 != $nb2 VRAI si $nb1 est diférent de $nb2
Opérateurs logiques
$chaine1 \& $chaine2 VRAI si les 2 chaines sont vraies
$chaine1 \| $chaine2 VRAI si l'une des 2 chaines est vraie
Opérateurs divers
-$nb1 Opposé de $nb1
\( expression \) Regroupement
$chaine : expression_reguliere Compare la chaine avec l'expression régulière

Les arguments de la commande expr doivent toujours être séparés par au moins un espace ou une tabulation.

Exemple :

$ nb=3
$ expr $nb + 5
8
$ expr $nb \* 6
18
$ expr $nb / 2
1
$ nb=10
$ expr $nb % 3
1
$ expr $nb - -5
15
$

Récupérer le résultat dans une variable :

$ nb2=`expr $nb - 2`
$ echo $nb2
8
$

Priorité d'exécution des opérateurs :

$ nb=5
$ nb2=`expr $nb \* 3 + 4`
$ echo $nb2
19
$ nb2=`expr $nb \* \( 3 + 4 \)`
$ echo $nb2
35
$

Résultat d'une comparaison VS valeur du code de retour :

$ nb=2
$ expr $nb \>= 1
1                            # Résultat de la comparaison
$ echo $?
0                            # Résultat du code de retour
$ expr $nb \>= 3
0                            # Résultat de la comparaison
$ echo $?
1                            # Résultat du code de retour
$

Différents types de comparaison :

$ nl comparaison.sh
     1  #!/bin/bash
 
     2  # Test sur le nombre d'arguments
     3  if [[ $# -ne 2 ]]
     4  then
     5          echo "Mauvais nombre d'arguments"
     6          echo "Utilisation : $0 expr1 expr2"
     7          exit 1
     8  fi
 
     9  # On compare expr1 et expr2 avec la commande EXPR et redirection du résultat dans /dev/null
    10  if expr $1 \> $2 > /dev/null
    11  then
    12          echo "Comparaison EXPR : $1 est supérieur à $2"
    13  else
    14          echo "Comparaison EXPR : $1 est inférieur à $2"
    15  fi
 
    16  # On compare expr1 et expr2 avec la commande [[ ]] (lexicographiquement)
    17  if [[ $1 > $2 ]]
    18  then
    19          echo "Comparaison lexico.. [[ ]] : $1 est supérieur à $2"
    20  else
    21          echo "Comparaison lexico.. [[ ]] : $1 est inférieur à $2"
    22  fi
 
    23  # On compare expr1 et expr2 avec la commande [[ ]]
    24  if [[ $1 -gt $2 ]]
    25  then
    26          echo "Comparaison [[ ]] : $1 est supérieur à $2"
    27  else
    28          echo "Comparaison [[ ]] : $1 est inférieur à $2"
    29  fi
 
    30  exit 0
$ ./comparaison.sh 9 5
Comparaison EXPR : 9 est supérieur à 5
Comparaison lexico.. [[ ]] : 9 est supérieur à 5
Comparaison [[ ]] : 9 est supérieur à 5
$ ./comparaison.sh 50 9
Comparaison EXPR : 50 est supérieur à 9
Comparaison lexico.. [[ ]] : 50 est inférieur à 9
Comparaison [[ ]] : 50 est supérieur à 9
$ ./comparaison.sh a b
Comparaison EXPR : a est inférieur à b
Comparaison lexico.. [[ ]] : a est inférieur à b
Comparaison [[ ]] : a est inférieur à b
$ ./comparaison.sh t r
Comparaison EXPR : t est supérieur à r
Comparaison lexico.. [[ ]] : t est supérieur à r
Comparaison [[ ]] : t est inférieur à r
$

Le résultat de la commande EXPR est toujours exact que ce soit des chiffres ou des caractères.

Etiquettes: 

La commande (( ))

Syntaxe :

((expression_arithmétique))

Utilisation :

La commande (( )) dispose de nombreux avantages par rapport à la commande expr

  • Opérateurs supplémentaires
  • Les arguments n'ont pas besoin d'être séparés par des espaces
  • Les variables n'ont pas besoin d'être préfixées par le symbole $
  • Les caractères spéciaux du shell n'ont pas besoin d'être protégés par des antislashes
  • Les affectations se font dans la commande
  • Son exécution est plus rapide

Une grande partie des opérateurs proviennent du langage C

Opérateurs Signification
Opérateurs arithmétiques
nbr1 + nbr2 Addition
nbr1 - nbr2 Soustraction
nbr1 * nbr2 Multiplication
nbr1 / nbr2 Division
nbr1 % nbr2 Modulo
Opérateurs travaillant sur les bits
~nbr1 Complément à 1
nbr1 >> nbr2 Décalage sur nbr1 de nbr2 bits à droite
nbr1 << nbr2 Décalage sur nbr1 de nbr2 bits à gauche
nbr1 & nbr2 ET bit à bit
nbr1 | nbr2 OU bit à bit
nbr1 ^ nbr2 OU exclusif bit à bit
Opérateurs de comparaison
nbr1 > nbr2 VRAI si nbr1 est strictement supérieur à nbr2
nbr1 >= nbr2 VRAI si nbr1 est supérieur ou égal à nbr2
nbr1 < nbr2 VRAI si nbr1 est strictement inférieur à nbr2
nbr1 <= nbr2 VRAI si nbr1 est inférieur ou égal à nbr2
nbr1 == nbr2 VRAI si nbr1 est égal à nbr2
nbr1 != nbr2 VRAI si nbr1 est différent de nbr2
Opérateurs logiques
!nbr1 Inverse la valeur de vérité de nbr1
&& ET
|| OU
Opérateurs divers
-nbr1 Opposé de nbr1
nbr1 = expression Assignement
(expression) Regroupement
nbr1 binop= nbr2 binop représente l'un des opérateurs suivants : +, -, /, *, %, >>, <<, &, |, ^. Equivalent à nbr1 = nbr1 binop nbr2

Exemples :

Ajouter 10 à nbr1 (2 méthodes différentes)

$ nbr1=10
$ ((nbr1=nbr1+10))
$ echo $nbr1
20
$ nbr1=10
$ ((nbr1+=10))
$ echo $nbr1
20
$

Test si nbr1 est supérieur à nbr2 et inversement

$ nbr1=5
$ nbr2=6
$ ((nbr1>nbr2))
$ echo $?
1                           # Code retour 1 (faux) car nbr1 n'est pas supérieur à nbr2
$ ((nbr1<nbr2))
$ echo $?
0                           # Code retour 0 (vrai) car nbr1 est inférieur à nbr2
$

Le script suivant compare les 2 arguments passés en paramètre

$ nl comparaison2.sh
     1  #!/bin/bash
 
     2  # Test sur le nombre d'arguments
     3  if (($#!=2))
     4  then
     5          echo "Mauvais nombre d'arguments"
     6          echo "Utilisation : $0 nbr1 nbr2"
     7          exit 1
     8  fi
 
     9  # On compare nbr1 et nbr2 avec la commande (( ))
    10  if (($1<$2))
    11  then
    12          echo "$1 est inférieur à $2"
    13  else
    14          if (($1>$2))
    15          then
    16                  echo "$1 est supérieur à $2"
    17          else
    18                  if (($1==$2))
    19                  then
    20                          echo "$1 est égal à $2"
    21                  else
    22                          echo "Comparaison impossible"
    23                  fi
    24          fi
    25  fi
 
    26  exit 0
$ ./comparaison2.sh 2 8
2 est inférieur à 8
$ ./comparaison2.sh 8 2
8 est supérieur à 2
$ ./comparaison2.sh 8 8
8 est égal à 8
$

Regroupements et tests logiques

$ nbr1=2
$ nbr2=5
$ if (( (nbr1>0) && (nbr2>nbr1) ))
> then
> echo "nbr1 est supérieur à 0 et inférieur à nbr2"
> else
> echo "nbr1 est égal à 0 ou supérieur à nbr2"
> fi
nbr1 est supérieur à 0 et inférieur à nbr2
$

La commande let

La commande let est équivalente à ((expression))

Syntaxe :

let "expression"

Exemple :

Multiplier nbr1 par 3

$ nbr1=5
$ let "nbr1=nbr1*3"
$ echo $nbr1
15
$

Calculer le modulo de nbr1 par 2 et l'affecter à la variable nbr2

$ nbr1=5
$ let "nbr2=nbr1%2"
$ echo $nbr1
5
$ echo $nbr2
1
$

Etiquettes: 

Substitution d'expressions arithmétiques

Il existe les caractères de substitution de commandes mais il existe également les caractères de substitution d'expressions arithmétiques.

Syntaxe :

commande argument1 $((expression_arithmetique)) ... argumentn

Exemple :

Rappel sur la substitution de commandes

$ echo "Nombre de users connectes : `who | wc -l`"
Nombre de users connectes : 1
ou
$ echo "Nombre de users connectes : $(who | wc -l)"
Nombre de users connectes : 1

Substitution d'expressions arithmétiques

$ cpt=1
$ ((cpt+=1))                               # Le résultat de la commande n'est pas affichée
$ echo $cpt
2
$

$ cpt=1
$ echo "Nouveau compteur : `((cpt+=1))`"
Nouveau compteur :                  # Le résultat de la commande n'est pas affichée et le compteur n'est pas incrémenté
$ echo $cpt
1
$

$ cpt=1
$ echo "Nouveau compteur : $((cpt+=1))"
Nouveau compteur : 2                # Le résultat de la commande est affiché et le compteur est incrémenté
$ echo $cpt
2
$

Il ne faut pas confondre (( )) et $(( )).
(( )) est une commande interne au shell.
$(( )) sont des caractères spéciaux du shell à l'égal de `` ou $().

Mise au point d'un script

Le shell propose quelques options qui permettent de débugger des scripts shell.

Option -x

L'option -x permet de débugger un script shell en affichant l'exécution des commandes après traitement des caractères spéciaux du shell.

Les différentes syntaxes permettant d'activer l'option -x :

Activer l'option

  1. set -x
  2. set -o xtrace
  3. bash -x nom_du_script

Désactiver l'option

  1. set +x
  2. set +o xtrace

Exemple :

Dans l'exemple suivant, la variable fichier n'a pas été préfixée par le symbole $.

$ nl liste.sh
     1  #!/bin/bash
     2  for fichier in `ls`
     3  do
     4          if [[ -f fichier ]]
     5          then
     6                  echo "$fichier"
     7          fi
     8  done
$

L'exécution du script ne retourne aucun résultat malgré la présence de fichiers dans le dossier d'exécution du script.

$ ./liste.sh
$

L'exécution du script avec activation de l'option -x démontre effectivement que la variable fichier n'est pas remplacée par sa valeur.

$ bash -x liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
+ for fichier in '`ls`'
+ [[ -f fichier ]]
...
$

L'activation de l'option de débuggage peut également être lancée directement dans le script.

$ nl liste.sh
     1  #!/bin/bash
     2  set -x
     3  for fichier in `ls`
     4  do
     5          if [[ -f fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$

Après correction de l'erreur et exécution du script en mode débuggage.

$ nl liste.sh
     1  #!/bin/bash
     2  set -x
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier" ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
++ ls
+ for fichier in '`ls`'
+ [[ -f 1coucou ]]
+ echo 1coucou
1coucou
+ for fichier in '`ls`'
+ [[ -f 24902 ]]
+ for fichier in '`ls`'
+ [[ -f 25013 ]]
+ for fichier in '`ls`'
+ [[ -f 25031 ]]
+ for fichier in '`ls`'
+ [[ -f 25043 ]]
+ for fichier in '`ls`'
+ [[ -f comparaison2.sh ]]
+ echo comparaison2.sh
comparaison2.sh
...
$

 

Autres options

Fonction Bourne shell, ksh, bash ksh, bash
Lecture des commandes sans exécution et détection des erreurs de syntaxe. set -n
set +n
set -o noexec
set +o noexec
Affichage des commandes avant substitution des caractères spéciaux du shell set -v
set +v
set -o verbose
set +o verbose

Exemple :

Dans l'exemple suivant, un guillemet a été volontairement omis.

$ nl liste.sh
     1  #!/bin/bash
     2  set -n
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$

$ nl liste.sh
     1  #!/bin/bash
     2  set -v
     3  for fichier in `ls`
     4  do
     5          if [[ -f "$fichier ]]
     6          then
     7                  echo "$fichier"
     8          fi
     9  done
$ ./liste.sh
for fichier in `ls`
do
        if [[ -f "$fichier ]]
        then
                echo "$fichier"
        fi
done
./liste.sh: line 7: Caractère de fin de fichier (EOF) prématuré lors de la recherche du « " » correspondant
./liste.sh: line 10: argument inattendu pour l'opérateur conditionnel à un argument
./liste.sh: line 10: Erreur de syntaxe : fin de fichier prématurée
$

Les structures de contrôle

if

La structure de controle if permet de réaliser des tests.
La commande située à droite du if est exécutée.
Si le code retour de la commande ($?) est égal à 0 (vrai), les commandes situées dans le bloc then sont exécutées.
Si le code de retour est supérieur à 0 (faux), ce sont les commandes situées dans le bloc else (optionnel) qui sont exécutées.
Dans le cas où le bloc else n'est pas spécifié, le shell continue à la première commande située sous le fi.

Les différentes syntaxes :

if, then, else, fi

if commande1
then
     commande2
     commande3
     ...
else
     commande4
     ...
fi

if, then, fi

if commande1
then
     commande2
     commande3
     ...
fi

if, then, elif, else, fi

if commande1
then
     commande2
     commande3
     ...
elif commande4
then
     commande5
     ...
else
     commande6
     ...
fi

Le mot clé fi permet de fermer la structure de controle if.
Le mot clé elif n'a pas de fermeture.

Autres syntaxes :

Le mot clé then peut être placé sur la même ligne que le if à condition de les séparer à l'aide d'un point virgule.

if commande1 ; then
     commande2
     commande3
     ...
fi

Plusieurs structures de controles if peuvent être imbriquées les unes dans les autres.

if commande1
then
     commande2
     ...
else
     if commande3
     then
          commande4
          ...
     else
          commande5
          ...
     fi
fi

Exemple :

Le script suivant vérifie si un argument est passé en paramètre et dans le cas contraire demande une saisie clavier à l'utilisateur.
Le script vérifie ensuite que le user saisi existe bien dans le fichier /etc/passwd.

$ nl user_passwd.sh
     1  #!/bin/bash
     2  if [[ $# -ne 1 ]]
     3  then
     4          echo -e "Saisir le nom d'un user : \c"
     5          read user
     6  else
     7          user=$1
     8  fi
     9  if grep -q "^$user:" /etc/passwd
    10  then
    11          echo "Le user $user existe"
    12          echo "Son UID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f3)"
    13          echo "Son GID est le : $(grep "^$user:" /etc/passwd | cut -d":" -f4)"
    14  else
    15          echo "Le user $user n'existe pas !!!"
    16  fi
    17  exit 0
$ ./user_passwd.sh root
Le user root existe
Son UID est le : 0
Son GID est le : 0
$ ./user_passwd.sh
Saisir le nom d'un user : www-data
Le user www-data existe
Son UID est le : 33
Son GID est le : 33
$

Etiquettes: 

case

La structure de controle case permet elle aussi d'effectuer des tests.
Elle permet d'orienter la suite du programme en fonction d'un choix de différentes valeurs.
Quand il y a un nombre important de choix, la commande case est plus appropriée que la commande if.

Syntaxe :

case $variable in
modele1) commande1
     ...
     ;;
modele2) commande2
     ...
     ;;
modele3 | modele4 | modele5 ) commande3
     ...
     ;;
esac

Le shell compare la valeur de la variable aux différents modèle renseignés.
Lorsque la valeur correspond au modèle, les commandes faisant partie du bloc sont exécutées.
Les caractères ;; permettent de fermer le bloc et de mettre fin au case.
Le shell continue à la première commande située sous esac.

Il ne faut surtout pas oublier les caractères ;; car cela engendrera une erreur.

Rappel des caractères spéciaux :

Caractères spéciaux pour modèles de chaines de caractères Signification
Caractères spéciaux valables dans tous les shells :
* 0 à n caractères
? 1 caractère quelconque
[abc] 1 caractère parmis ceux inscrits entre les crochets
[!abc] 1 caractère ne faisant pas partie de ceux inscrits entre les crochets
Caractères spéciaux non valides en Bourne Shell.
En bash, il faut activer l'option extglob (shopt -s extglob)
?(expression) de 0 à 1 fois l'expression
*(expression) de 0 à n fois l'expression
+(expression) de 1 à n fois l'expression
@(expression) 1 fois l'expression
!(expression) 0 fois l'expression
?(expression1 | expression2 | ...)
*(expression1 | expression2 | ...)
+(expression1 | expression2 | ...)
@(expression1 | expression2 |...)
!(expression1 | expression2 | ...)
alternatives

Exemple :

Le script suivant permet de créer, modifier, visualiser et supprimer un fichier dans le répertoire d'exécution du script.
Il prend en argument un nom de fichier et affiche un menu.
Utilisation de case avec imbrication de if.

$ nl file.sh
     1  #!/bin/bash
     2  #set -x
     3  # Si le nombre d'arguments est different de 1 on quitte avec code 1
     4  if [[ $# -ne 1 ]]
     5  then
     6          echo "Nombre d'arguments incorrect"
     7          echo "Usage : $0 file"
     8          exit 1
     9  fi
    10  # On affiche le menu
    11  echo -e "1(Creer) "
    12  echo -e "2(Editer) "
    13  echo -e "3(Afficher) "
    14  echo -e "4(Supprimer)"
    15  echo -e "Votre choix : \c"
    16  # On recupere la valeur saisi
    17  read choix
    18  # Si la valeur saisi est differente de 1, 2, 3 ou 4 on quitte avec code 1
    19  if [[ "$choix" != [1-4] ]]
    20  then
    21          echo "Choix incorrect"
    22          exit 1
    23  fi
    24  # En fonction de la valeur saisi on execute les differentes actions
    25  case "$choix" in
    26  # Si choix = 1 --> creation
    27  1)      if [[ -e "$1" ]]
    28          then
    29                  if [[ -f "$1" ]]
    30                  then
    31                          echo "Fichier $1 deja existant"
    32                  elif [[ -d "$1" ]]
    33                  then
    34                          echo "$1 est un repertoire"
    35                  fi
    36                  exit 1
    37          else
    38                  touch "$1"
    39                  nano "$1"
    40          fi
    41  ;;
    42  # Si choix = 2 --> edition
    43  2)      if [[ -f "$1" ]]
    44          then
    45                  nano "$1"
    46          else
    47                  if [[ -d "$1" ]]
    48                  then
    49                          echo "$1 est un repertoire et ne peut etre edite"
    50                  else
    51                          echo "Fichier $1 inexistant"
    52                  fi
    53                  exit 1
    54          fi
    55  ;;
    56  # Si choix = 3 --> affichage
    57  3)      if [[ -f "$1" ]]
    58          then
    59                  more "$1"
    60          else
    61                  if [[ -d "$1" ]]
    62                  then
    63                          echo "$1 est un repertoire et ne peut etre visualise"
    64                  else
    65                          echo "Fichier $1 inexistant"
    66                  fi
    67                  exit 1
    68          fi
    69  ;;
    70  # Si choix = 4 --> suppression
    71  4)      if [[ -f "$1" ]]
    72          then
    73                  rm "$1"
    74          else
    75                  if [[ -d "$1" ]]
    76                  then
    77                          echo "$1 est un repertoire et ne peut etre supprime"
    78                  else
    79                          echo "Fichier $1 inexistant"
    80                  fi
    81                  exit 1
    82          fi
    83  ;;
    84  # Fin du case
    85  esac
    86  # Tout c'est bien deroule on quitte avec le code 0
    87  exit 0
$ ./file.sh test4
1(Creer)
2(Editer)
3(Afficher)
4(Supprimer)
Votre choix : 1
$

Etiquettes: 

Boucle for

Syntaxe :

La boucle for permet de traiter une liste de valeurs indiquée à droite du mot clé in.
A chaque tour de boucle, la variable var est initialisée avec une des valeurs de la liste.
Elles sont traitées dans l'ordre de leur énumération.

Liste de valeurs citée directement

for var in valeur1 valeur2 valeur3 ... valeurn
do
     commande
done

Liste de valeurs contenue dans une variable

for var in $variable
do
     commande
done

Liste de valeurs générée par substitution de commande

for var in `commande`
do
     commande
done

Liste de valeurs générée par substitution de caractères de génération de noms de fichiers

for var in *.ext
do
     commande
done

Liste par défaut : Arguments de la ligne de commande

for var
do
     commande
done

for var in $*
do
     commande
done

Avec incrémentation d'une variable

for (( var=valeurMin; var<=valeurMax; var++ ))
do
     commande
done

Exemple :

Un script compte à rebours

$ nl boucleFor01.sh
     1  #!/bin/bash
     2  for var in 10 9 8 7 6 5 4 3 2 1 0
     3  do
     4          echo "$var"
     5  done
     6  exit 0
$ ./boucleFor01.sh
10
9
8
7
6
5
...
$

Lister les fichiers d'un ou plusieurs dossiers

$ nl boucleFor02.sh
     1  #!/bin/bash
     2  if [[ $# -lt 1 ]]
     3  then
     4          echo "Nombre d'argument incorrect"
     5          echo "Utilisation $0 dossier1 dossier2 dossiern"
     6          exit 1
     7  fi
     8  for dossier in $*
     9  do
    10          if [[ -d $dossier ]]
    11          then
    12                  echo "Liste des fichiers du dossier $dossier"
    13                  for fichier in `ls $dossier`
    14                  do
    15                          echo "$fichier"
    16                  done
    17          fi
    18  done
    19  exit 0
$ ./boucleFor02.sh coucou 24902 25013 25031
Liste des fichiers du dossier coucou
test
Liste des fichiers du dossier 24902
fichier_0
fichier_1
fichier_2
fichier_3
Liste des fichiers du dossier 25013
fichier_0
fichier_1
fichier_2
Liste des fichiers du dossier 25031
fichier_0
fichier_1
fichier_2
fichier_3
$

En utilisant une variable incrémentée :

$ for (( i=0; i <= 10; i++ )); do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$

Idem avec la syntaxe suivante :

$ for i in {0..10}; do echo $i; done
0
1
2
3
4
5
6
7
8
9
10
$

Etiquettes: 

Boucle while

Syntaxe :

while commande1
do
     commande2
     ...
done

La boucle while permet d'exécuter les commandes présentes entre le do et le done tant que la commande1 placée à droite du while retourne un code vrai.

Exemple :

Le script suivant demande de saisir 53 et continue tant que c'est faux

$ nl boucleWhile01.sh
     1  #!/bin/bash
     2  nbr=0
     3  while ((nbr!=53))
     4  do
     5          echo -e "Saisir 53 : \c"
     6          read nbr
     7
     8  done
     9  exit 0
$ ./boucleWhile01.sh
Saisir 53 : rt
Saisir 53 : 54
Saisir 53 : R4
Saisir 53 : 53
$

Le script suivant affiche le compteur tant qu'il est inférieur à 10

$ nl boucleWhile02.sh
     1  #!/bin/bash
     2  cpt=0
     3  while ((cpt<10))
     4  do
     5          echo "Le compteur vaut : $cpt"
     6          ((cpt+=1))
     7  done
     8  exit 0
$ ./boucleWhile02.sh
Le compteur vaut : 0
Le compteur vaut : 1
Le compteur vaut : 2
Le compteur vaut : 3
Le compteur vaut : 4
Le compteur vaut : 5
Le compteur vaut : 6
Le compteur vaut : 7
Le compteur vaut : 8
Le compteur vaut : 9
$

Le script suivant effectue une somme des nombres saisis

$ nl boucleWhile03.sh
     1  #!/bin/bash
     2  somme=0
     3  echo "Saisir un nombre, ^d pour afficher la somme"
     4  while read nombre
     5  do
     6          if [[ $nombre != +([0-9]) ]]
     7          then
     8                  echo "$nombre n'est pas un nombre"
     9                  continue
    10          fi
    11          ((somme+=nombre))
    12  done
    13  echo "La somme est de : $somme"
    14  exit 0
$ ./boucleWhile03.sh
Saisir un nombre, ^d pour afficher la somme
56
32
89
9.6
9.6 n'est pas un nombre
g8
g8 n'est pas un nombre
54
La somme est de : 231
$

Le mot clé continue permet de remonter aussitôt à la boucle while sans exécuter la commande suivante

Attention aux boucles infinies

Ce script provoqe une boucle infinie car il manque l'incrémentation du compteur

$ nl boucleWhile04.sh
     1  #!/bin/bash
     2  cpt=0
     3  while ((cpt<10))
     4  do
     5          echo "Le compteur vaut : $cpt"
     6  done
     7  exit 0
$

Le shell propose également la commande interne : qui renvoie toujours vrai et permet donc de faire une boucle infinie avec un while.

$ nl boucleWhile05.sh
     1  #!/bin/bash
     2  while :
     3  do
     4          echo "Boucle infinie"
     5  done
     6  exit 0
$

En bash et ksh, la commande true propose exactement la même chose.

$ nl boucleWhile05.sh
     1  #!/bin/bash
     2  while true
     3  do
     4          echo "Boucle infinie"
     5  done
     6  exit 0
$

Etiquettes: 

until

Syntaxe :

until commande1
do
     commande2
     ...
done

A l'inverse de while, la commande until exécute les commandes situées entre le do et le done tant que la commande située à droite du until retourne un code faux.

Exemple :

Le script suivant boucle tant que le nombre saisi n'est pas égal à 53

$ nl boucleUntil01.sh
     1  #!/bin/bash
     2  nbr=0
     3  until ((nbr==53))
     4  do
     5          echo -e "Saisir 53 : \c"
     6          read nbr
     7  done
     8  exit 0
$ ./boucleUntil01.sh
Saisir 53 : 45
Saisir 53 : rt
Saisir 53 : fd
Saisir 53 : 53
$

Le script suivant permet, en tâche de fond, de surveiller un répertoire donné et d'informer l'utilisateur de l'arrivée d'un fichier attendu dans ce répertoire.
Pour plus de sécurité sur l'intégrité du fichier attendu, un fichier témoin devra être créé à la suite du fichier attendu puisque le principal contrôle se fera sur l'existence de ce fichier.

$ nl boucleUntil02.sh
     1    #!/bin/bash
     2    # Il doit y avoir au minimum 2 paramètres en arguments et un maximum de 3
     3    if [[ $# -lt 2 || $# -gt 3 ]]
     4    then
     5        echo "Utilisation : $0 repertoire fichier [ temoin ]"
     6        exit 1
     7    fi
     8    # Le premier argument doit être un répertoire
     9    if [[ ! -d $1 ]]
    10    then
    11        echo "$1 n'est pas un répertoire"
    12        exit 2
    13    fi
    14    # Nom du fichier témoin par défaut
    15    ficTemoin=${3:-temoin}
    16    # Exécution de la boucle en attendant l'arrivée du fichier témoin avec une pause toutes les 3 secondes
    17    until [[ -e $1/$ficTemoin ]]
    18    do
    19        sleep 3
    20    done
    21    # Vérification que le fichier attendu est bien présent
    22    if [[ ! -e $1/$2 ]]
    23    then
    24        echo "Le fichier témoin existe mais le fichier attendu est absent"
    25        exit 3
    26    fi
    27    # Sauvegarde du fichier attendu dans le HOME de l'utilisateur avec horodatage et suppression du fichier témoin
    28    date=$(date '+%Y%m%d_%H%M')
    29    newFichier=$2.$date
    30    mv $1/$2 $HOME/$newFichier
    31    rm $1/$ficTemoin
    32    # Envoi d'un mail à l'utilisateur
    33    mail $LOGNAME <<FIN
    34    Le fichier $HOME/$newFichier est bien arrivé.
    35    FIN
    36    echo "$0 : Vous avez reçu un message !!! "
    37    exit 0
$ ./boucleUntil02.sh /tmp monFichier &     # Lancement du script en arrière plan grâce à la commande &
[1] 2298
$ touch /tmp/monFichier                                   # Création du fichier attendu
$ touch /tmp/temoin                                           # Création du fichier témoin
$ ./boucleUntil02.sh : Vous avez reçu un message !!!     # Message généré par la ligne 36
 
[1]+  Done                    ./boucleUntil02.sh /tmp monFichier
$

il ne reste plus qu'à consulter sa boite mail pour lire le message envoyé par le script.

Etiquettes: 

break et continue

Les commandes break et continue peuvent s'utiliser à l'intérieur des boucles for, while, until et select.
La commande break permet de sortir d'une boucle.
La commande continue permet de remonter à la condition d'une boucle.

Syntaxe :

Quitter la boucle de premier niveau
break

Quitter la boucle de niveau n
break n

Remonter à la condition de la boucle de premier niveau
continue

Remonter à la condition de la boucle de niveau n
continue n

Exemple :

$ nl boucleWhile06.sh
     1    #!/bin/bash
     2    somme=0
     3    while true
     4    do
     5        echo "Saisir un nombre, ^d pour afficher la somme"
     6        if read nombre
     7        then
     8            if [[ $nombre != +([0-9]) ]]
     9            then
    10                echo "$nombre n'est pas un nombre"
    11                continue
    12            fi
    13            ((somme+=nombre))
    14        else
    15            break
    16        fi
    17    done
    18    echo "La somme est de : $somme"
    19    exit 0
$ ./boucleWhile06.sh
Saisir un nombre, ^d pour afficher la somme
23
Saisir un nombre, ^d pour afficher la somme
56
Saisir un nombre, ^d pour afficher la somme
54
Saisir un nombre, ^d pour afficher la somme
89
Saisir un nombre, ^d pour afficher la somme
La somme est de : 222
$

Etiquettes: 

Aspects avancés de la programmation shell

Comparatif des variables $* et $@

Utilisation de $* et de $@

Les variables $* et $@ contiennent la liste des arguments d'un script shell.
Lorsqu'elles ne sont pas entourées par des guillemets, elles sont équivalentes.

Exemple :

$ nl scr01.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \$*"
     4  for arg in $*
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  cpt=1
    10  echo "Utilisation de la variable \$@"
    11  for arg in $@
    12  do
    13          echo "Argument $cpt : $arg"
    14          ((cpt+=1))
    15  done
    16  exit 0
$ ./scr01.sh a b "c d e" f
Utilisation de la variable $*
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d
Argument 5 : e
Argument 6 : f
Utilisation de la variable $@
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d
Argument 5 : e
Argument 6 : f
$

Interprétation :

  1. $* et $@ contiennent exactement la même liste d'arguments.
  2. Les guillemets protégeant les arguments ne sont pas pris en compte
  3. Ce sont les espaces qui délimitent les arguments

Utilisation de "$*"

Les guillemets autour de $* supprime la signification des espaces contenus dans $*.

Exemple :

$ nl scr02.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \"\$*\""
     4  for arg in "$*"
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  exit 0
$ ./scr02.sh a b c "d e f" g
Utilisation de la variable "$*"
Argument 1 : a b c d e f g
$

Interprétation :

  1. Les guillemets entourant les arguments ne sont pas pris en compte.
  2. Tous les arguments sont considérés comme étant un seul argument.

Utilisation de "$@"

La variable $@ placée entre guillemets permet de conserver la protection des arguments par les guillemets.

Exemple :

$ nl scr03.sh
     1  #!/bin/bash
     2  cpt=1
     3  echo "Utilisation de la variable \"\$@\""
     4  for arg in "$@"
     5  do
     6          echo "Argument $cpt : $arg"
     7          ((cpt+=1))
     8  done
     9  exit 0
$ ./scr03.sh a b c "d e f" g
Utilisation de la variable "$@"
Argument 1 : a
Argument 2 : b
Argument 3 : c
Argument 4 : d e f
Argument 5 : g
$

Interprétation :

  1. Les espaces délimitent la liste des arguments.
  2. Les arguments placés entre guillemets sont considérés comme étant un seul argument

Substitution de variables

Les shells BASH et KSH offrent des fonctionnalités supplémentaires au niveau des substitution de variables.

Connaitre la longueur d'une chaine d'une variable :

Syntaxe :

${#variable}

Exemple :

$ variable="Une chaine de texte"
$ echo $variable
Une chaine de texte
$ echo "La chaine contient ${#variable} caractères"
La chaine contient 19 caractères
$

Manipuler des chaines de caractères :

Exemple avec la variable suivante :

$ variable="col1|col2|col3|col4|col5"
$ echo $variable
col1|col2|col3|col4|col5
$

Cette variable est constituée de 5 segments délimités par le caractère |

Le contenu de la variable n'est jamais modifié

Exclure le premier segment

Syntaxe :

${variable#modele}

modele est une chaine de caractère incluant les caractères spéciaux *, ?, [ ], ?(expression), +(expression), *(expression), @(expression), !(expression)

Rappel des expressions pour la substitution des noms de fichiers

Le caractère # signifie "Chaine la plus courte possible en partant de la gauche"

Exemple :

$ echo ${variable#*|}
col2|col3|col4|col5
$

Conserver le dernier segment

Syntaxe :

${variable##modele}

Les caractères ## signifient "Chaine la plus longue possible en partant de la gauche"

Exemple :

$ echo ${variable##*|}
col5
$

Exclure le dernier segment

Syntaxe :

${variable%modele}

Le caractère % signifie "Chaine la plus courte possible en partant de la droite"

Exemple :

$ echo ${variable%|*}
col1|col2|col3|col4
$

Conserver le premier segment

Syntaxe :

${variable%%modele}

Les caractères %% signifient "Chaine la plus longue possible en partant de la droite"

Exemple :

$ echo ${variable%%|*}
col1
$

En bash, ne pas oublier d'activer l'option extglob avec la commande shopt -s extglob afin de permettre au shell d'interpréter les modèles utilisant les expressions complexes.

Tableaux

Avec les shells récents, il est possible d'utiliser des tableaux à 1 dimension.
Les éléments du tableau commencent toujours à l'indice 0.

Ajouter un élément à un tableau :

Syntaxe :

tableau[indice]=valeur

Exemple :

$ tableau[0]=15
$ tableau[3]=20
$

Les indices non initialisés sont vides.

Afficher un élément d'un tableau :

Syntaxe :

${tableau[indice]}

Exemple :

$ echo ${tableau[3]}
20
$ echo ${tableau[0]}
15
$ echo ${tableau[2]}
 
$

L'indice 2 du tableau est vide.

Initialiser un tableau avec plusieurs valeurs :

Syntaxe :

tableau=(valeur1 valeur2 valeur3 ..... valeurn)

Exemple :

$ tableau=(02 40 35 68 98 45 52 03)
$ echo ${tableau[0]}
02
$ echo ${tableau[2]}
35
$

Toutes les précédentes valeurs contenues dans le tableaux sont effacées.

Afficher tous les éléments d'un tableau :

Syntaxe :

${tableau[*]}

Exemple :

$ echo ${tableau[*]}
02 40 35 68 98 45 52 03
$

Afficher le nombre d'éléments d'un tableau :

Syntaxe :

${#tableau[*]}

Exemple :

$ echo ${#tableau[*]}
8
$

Obtenir la longueur d'un élément d'un tableau :

Syntaxe :

${#tableau[indice]}

Exemple :

$ echo ${#tableau[0]}
2
$

Parcourir tous les éléments d'un tableau :

$ tab=("un" "deux" "trois")
$ for m in ${tab[@]}; do echo $m; done
un
deux
trois
$

Initialisation des paramètres positionnels avec set

La commande set utilisée sans option mais suivie d'arguments affecte ces derniers aux paramètres positionnels $1, $2, $3 ....., $*, $@ et $#.

Cela permet de manipuler plus facilement le résultat de diverses substitutions.

Exemple :

$ ls
fichier_0  fichier_2  fichier_4  fichier_6  fichier_8
fichier_1  fichier_3  fichier_5  fichier_7  fichier_9
$ set `ls`
$

La liste des fichiers obtenue avec la commande ls est maintenant affecté aux paramètres positionnels.

$ echo $#
10
$ echo $*
fichier_0 fichier_1 fichier_2 fichier_3 fichier_4 fichier_5 fichier_6 fichier_7 fichier_8 fichier_9
$ echo $1
fichier_0
$ echo $3
fichier_2
$ echo $9
fichier_8
$ echo ${10}
fichier_9
$

Les fonctions

Les fonctions permettent de regrouper et d'exécuter des commandes à différents endroits d'un script.

Cela permet de créer des fonctions personnalisées réutilisables.

Définition d'une fonction

Une fonction doit être définie au début d'un script, avant sa première utilisation.

Il existe 2 syntaxes permettant de définir une fonction.

Première syntaxe :

Ce sont les doubles parenthèses ( ) qui indique au shell la définition d'une fonction

Définition d'une fonction

maFonction () {
     commande1
     commande2
     .....
}

Appel d'une fonction

maFonction

Seconde syntaxe :

Le mot clé function remplace les doubles parenthèses ( )

Définition d'une fonction

function maFonction {
     commande1
     commande2
     .....
}

Appel d'une fonction

maFonction

Une fonction peut être appelée aussi bien à partir du programme principal qu'à partir d'une autre fonction.

Exemple :

$ nl fonction01.sh
     1  #!/bin/bash
 
     2  fctn01 () {
     3          echo "Fonction fctn01"
     4  }
 
     5  function fctn02 {
     6          echo "Fonction fctn02"
     7  }
 
     8  echo "Début du programme principal"
     9  echo "Appel de la fonction fctn01"
    10  fctn01
    11  echo "Appel de la fonction fctn02"
    12  fctn02
    13  echo "Fin du programme principal"
    14  exit 0
$ ./fonction01.sh
Début du programme principal
Appel de la fonction fctn01
Fonction fctn01
Appel de la fonction fctn02
Fonction fctn02
Fin du programme principal
$

Dès qu'une fonction est définie, celle-ci est considérée par le shell comme étant une commande interne.

Code de retour d'une fonction

Comme toutes commandes Linux, une fonction retourne également un code d'erreur.
Si le code erreur n'est pas spécifié, celui retourné par défaut correspond au code erreur de la dernière commande exécutée dans la fonction.
La commande return permet de retourner le code erreur de la fonction concernée. Ce code doit obligatoirement correspondre à un nombre compris entre 0 et 255.
Le code erreur retourné par la fonction est récupérable grâce à la variable $?.

Exemple :

Le script suivant test si l'utilisateur saisi existe sur le système.

$ nl fonction02.sh
     1  #!/bin/bash
 
     2  function pause {
     3          echo "Appuyer sur Entrée pour continuer"
     4          read x
     5  }
 
     6  function existUser {
     7          echo -e "Saisir le nom d'un utilisateur : \c"
     8          read user
     9          if grep -q "^$user:" /etc/passwd ; then
    10                  return 0
    11          fi
    12          return 1
    13  }
 
    14  while true
    15  do
    16          clear
    17          echo "- 1 - Savoir si un utilisateur existe"
    18          echo "- 2 - Connaitre l'UID d'un utilisateur"
    19          echo "- 3 - Fin"
    20          echo -e "Votre choix : \c"
    21          read choix
    22          case $choix in
    23                  1)      if existUser
    24                          then
    25                                  echo "L'utilisateur $user existe"
    26                          else
    27                                  echo "l'utilisateur $user n'existe pas"
    28                          fi
    29                          ;;
 
    30                  2)      echo "Option non disponible"
    31                          ;;
 
    32                  3)      exit 0
    33                          ;;
    34          esac
    35          pause
    36  done
$

Portée des variables

Dans un script shell, sans définition particulière, toutes les variables utilisées sont globales à tout le script.

Qu'une variable soit définie au niveau du programme principal ou d'une fonction, elle est accessible n'importe où dans le script.

Par exemple, dans le script fonction02.sh, la variable $user est initialisée dans la fonction existUser (ligne 8) et utilisée également au niveau du programme principal (ligne 25 et 27).

$ nl fonction02.sh
     1  #!/bin/bash
 
     2  function pause {
     3          echo "Appuyer sur Entrée pour continuer"
     4          read x
     5  }
 
     6  function existUser {
     7          echo -e "Saisir le nom d'un utilisateur : \c"
     8          read user
     9          if grep -q "^$user:" /etc/passwd ; then
    10                  return 0
    11          fi
    12          return 1
    13  }
 
    14  while true
    15  do
    16          clear
    17          echo "- 1 - Savoir si un utilisateur existe"
    18          echo "- 2 - Connaitre l'UID d'un utilisateur"
    19          echo "- 3 - Fin"
    20          echo -e "Votre choix : \c"
    21          read choix
    22          case $choix in
    23                  1)      if existUser
    24                          then
    25                                  echo "L'utilisateur $user existe"
    26                          else
    27                                  echo "l'utilisateur $user n'existe pas"
    28                          fi
    29                          ;;
 
    30                  2)      echo "Option non disponible"
    31                          ;;
 
    32                  3)      exit 0
    33                          ;;
    34          esac
    35          pause
    36  done
$

Définition de variables locales

La commande typeset permet de définir des variables locales à une fonction.

Syntaxe :

typeset variable
typeset variable=valeur

Exemple :

$ nl fonction03.sh
     1  #!/bin/bash
     2  function f1 {
     3          # var1 est une variable locale
     4          typeset var1
     5          echo "Dans la fonction f1 => var1 avant : $var1"
     6          var1=100
     7          echo "Dans la fonction f1 => var1 après : $var1"
     8          echo "Dans la fonction f1 => var2 avant : $var2"
     9          var2=200
    10          echo "Dans la fonction f1 => var2 après : $var2"
    11  }
    12  # var1 et var2 sont des variables globales
    13  var1=1
    14  var2=2
    15  echo "Dans le programme principal => var1 avant appel f1 : $var1"
    16  echo "Dans le programme principal => var2 avant appel f1 : $var2"
    17  f1
    18  echo "Dans le programme principal => var1 après appel f1 : $var1"
    19  echo "Dans le programme principal => var2 après appel f1 : $var2"
    20  exit 0
$ ./fonction03.sh
Dans le programme principal => var1 avant appel f1 : 1
Dans le programme principal => var2 avant appel f1 : 2
Dans la fonction f1 => var1 avant :
Dans la fonction f1 => var1 après : 100
Dans la fonction f1 => var2 avant : 2
Dans la fonction f1 => var2 après : 200
Dans le programme principal => var1 après appel f1 : 1
Dans le programme principal => var2 après appel f1 : 200
$

2 variables globales var1 et var2 sont définies et initialisées ligne 13 et 14.
1 variable locale var1 est définie dans la fonction ligne 4 et initialisée à 100 ligne 6.
Après exécution de la fonction f1, la variable globale var1 a conservée sa valeur (1) alors que la variable globale var2 a été modifiée.

Passage d'arguments

Dans un script shell, il est tout à fait possible de passer des arguments à une fonction étant donné qu'une fonction est reconnue par le shell comme étant une commande à part entière.

Ces arguments sont récupérables dans les fonctions grâce aux variables spéciales $1, $2, $3, ....., ${10} ......$*, $@ et $#. Ces variables sont aussi locales aux fonctions.

Par contre, la variable $0 contient toujours le nom du script.

Exemple :

$ nl fonction04.sh
     1  #!/bin/bash
 
     2  function f1 {
     3          echo "Arguments de la fonction f1 :"
     4          echo "\$0 => $0"
     5          echo "\$1 => $1"
     6          echo "\$2 => $2"
     7          echo "\$3 => $3"
     8          echo "\$* => $*"
     9          echo "\$# => $#"
    10  }
 
    11  function f2 {
    12          echo "Arguments de la fonction f2 :"
    13          echo "\$0 => $0"
    14          echo "\$1 => $1"
    15          echo "\$2 => $2"
    16          echo "\$3 => $3"
    17          echo "\$* => $*"
    18          echo "\$# => $#"
    19  }
 
    20  function f3 {
    21          echo "Arguments de la fonction f3 :"
    22          echo "\$0 => $0"
    23          echo "\$1 => $1"
    24          echo "\$2 => $2"
    25          echo "\$3 => $3"
    26          echo "\$* => $*"
    27          echo "\$# => $#"
    28  }
 
    29  echo "Arguments du programme principal :"
    30  echo "\$0 => $0"
    31  echo "\$1 => $1"
    32  echo "\$2 => $2"
    33  echo "\$3 => $3"
    34  echo "\$* => $*"
    35  echo "\$# => $#"
 
    36  # Appel de la fonction f1 avec 3 arguments
    37  f1 a b c
 
    38  # Appel de la fonction f2 avec 3 arguments
    39  f2 file.c 2000 500
 
    40  # Appel de la fonction f3 avec 2 arguments provenant du programme principal
    41  f3 $2 $3
 
    42  exit 0
$

Appel du script fonction04.sh avec 3 arguments :

$ ./fonction04.sh arg1 arg2 arg3
Arguments du programme principal :
$0 => ./fonction04.sh
$1 => arg1
$2 => arg2
$3 => arg3
$* => arg1 arg2 arg3
$# => 3
Arguments de la fonction f1 :
$0 => ./fonction04.sh
$1 => a
$2 => b
$3 => c
$* => a b c
$# => 3
Arguments de la fonction f2 :
$0 => ./fonction04.sh
$1 => file.c
$2 => 2000
$3 => 500
$* => file.c 2000 500
$# => 3
Arguments de la fonction f3 :
$0 => ./fonction04.sh
$1 => arg2
$2 => arg3
$3 =>
$* => arg2 arg3
$# => 2
$

Exploiter l'affichage d'une fonction

Comme n'importe quelle commande renvoyant un résultat, une fonction peut également être placée à l'intérieur de caractères de substitution de commande `` ou $( ).

Exemple :

$ nl fonction05.sh
     1  #!/bin/bash
 
     2  function getUid {
     3          grep "^$1:" /etc/passwd | cut -d':' -f3
     4  }
 
     5  # Initialisation de la variable globale uid
     6  uid=""
 
     7  # Appel de la fonction getUid avec l'argument du programme principal
     8  # Juste pour l'affichage
     9  getUid $1
 
    10  # Affectation du résultat de la fonction getUid à la variable uid
    11  uid=$(getUid $1)
 
    12  if [[ $uid != "" ]]
    13  then
    14          echo "L'utilisateur $1 a pour UID : $uid"
    15  else
    16          echo "L'utilisateur $1 n'existe pas"
    17  fi
 
    18  exit 0
$ ./fonction05.sh root
0
L'utilisateur root a pour UID : 0
$

Exemple complet

Exemple d'un script reprenant toutes les commandes et fonctions vues précédement

$ nl fonction06.sh
     1  #!/bin/bash
 
     2  # Pour faire une pause
     3  function pause {
     4          echo "Appuyer sur Entrée pour continuer"
     5          read x
     6  }
 
     7  # Pour savoir si un utilisateur existe
     8  function existUser {
     9          grep -qi "^$1:" /etc/passwd && return 0
    10          return 1
    11  }
 
    12  # Pour connaitre l'uid de l'utilisateur
    13  function getUid {
    14          grep -i "^$1:" /etc/passwd | cut -d':' -f3
    15  }
 
    16  # Initialisation des variables globales
    17  uid=""
    18  user=""
    19  choix=""
 
    20  while true
    21  do
    22          clear
    23          echo "- 1 - Savoir si un utilisateur existe"
    24          echo "- 2 - Connaitre l'UID d'un utilisateur"
    25          echo "- 3 - Fin"
    26          echo -e "Votre choix : \c"
    27          read choix
 
    28          if [[ $choix = @(1|2) ]] ; then
    29                  echo -e "Saisir le nom d'un utilisateur : \c"
    30                  read user
    31          fi
 
    32          case $choix in
 
    33                  1)      if existUser $user ; then
    34                                  echo "L'utilisateur $user existe"
    35                          else
    36                                  echo "l'utilisateur $user n'existe pas"
    37                          fi
    38                          ;;
 
    39                  2)      if existUser $user ; then
    40                                  uid=$(getUid $user)
    41                                  echo "l'UID de l'utilisateur $user est : $uid"
    42                          else
    43                                  echo "L'utilisateur $user n'existe pas"
    44                          fi
    45                          ;;
 
    46                  3)      exit 0
    47                          ;;
    48          esac
    49          pause
    50  done
$

Commandes d'affichage

La commande print

En KSH, la commande print apporte des fonctionnalités supplémentaires à la commande echo.

Exemple :

Utilisation simple

$ print coucou
coucou
$

Supprimer le saut de ligne

$ print -n coucou
coucou$

Afficher des arguments commançant par le caractère "-"

$ print - "-i : Option invalide"
-i : Option invalide
$

Ecrire sur un descripteur particulier

$ print -u2 "Message d'erreur"
Message d'erreur
$

Comparaison avec la commande echo

$ echo "Message d'erreur" 1>&2
Message d'erreur
$

 

La commande printf

En BASH, cette commande est identique à celle du langage C.
Elle permet de formater les affichages.

Syntaxe :

printf chaine expr1 expr2 ..... exprn

chaine représente la chaîne qui sera affichée à l'écran.
Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à sa suite.
Il doit y avoir autant de formats que d'expressions.

Exemple de formats utilisés.

%20s Affichage d'une chaine (string) sur 20 positions avec cadrage à droite
%-20s Affichage d'une chaine (string) sur 20 positions avec cadrage à gauche
%3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite
%03d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite et complété avec des 0 à gauche
%-3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à gauche
%+3d Affichage d'un entier (décimal) sur 3 positions avec cadrage à droite et affichage systématique du signe (un nombre négatif est toujours affiché avec son signe)
%10.2f Affichage d'un nombre flottant sur 10 positions dont 2 décimales
%+010.2f Affichage d'un nombre flottant sur 10 positions dont 2 décimales, complété par des 0 à gauche, avec cadrage à droite et affichage systématique du signe

Exemple :

$ article="Livres"
$ quantite=3
$ prix=3,5
$ printf "%-20s***%03d***%+10.2f\n" $article $quantite $prix
Livres              ***003***     +3,50
$

En utilisant un tableau

$ liste=(livre 10 3,5 cd 5 10,65 dvd 7 19,70 bd 80 5,25)
$ printf "%-20s***%03d***%+10.2f\n" ${liste[*]}
livre               ***010***     +3,50
cd                  ***005***    +10,65
dvd                 ***007***    +19,70
bd                  ***080***     +5,25
$

Gestion des entrées / sorties d'un script

Redirection des entrées/sorties standard

La commande interne exec permet de manipuler les descripteurs de fichier du shell courant.
Utilisée à l'intérieur d'un script, elle permet de rediriger de manière globale les entrées/sorties de celui-ci.

Rediriger l'entrée standard d'un script :

exec 0< fichier1

Toutes les commandes du script placées après cette directive et qui lisent leur entrée standard vont extraire leurs données à partir de fichier1.
Il n'y aura donc pas d'interaction avec le clavier.

Rediriger la sortie standard et la sortie d'erreur standard d'un script :

exec 1> fichier1 2> fichier2

Toutes les commandes du script placées après cette directive et qui écrivent sur leur sortie standard enverront leurs résultat dans fichier1.
Celles qui écrivent sur leur sortie d'erreur standard enverront leurs erreurs dans fichier2.

Rediriger la sortie standard et la sortie d'erreur standard d'un script dans un même fichier :

exec 1> fichier1 2>&1

Toutes les commandes du script placées après cette directive enverront leurs résultats et leurs erreurs dans fichier1

Premier exemple :

Redirection de la sortie standard vers /tmp/fichier1.log et redirection de la sortie d'erreur standard vers /tmp/fichier2.log.

$ nl test.sh
     1  #!/bin/bash
     2  exec 1> /tmp/fichier1.log 2> /tmp/fichier2.log
     3  echo "Début du traitement : $(date)"
     4  ls
     5  cp *.zzz /tmp
     6  rm *.zzz
     7  sleep 5
     8  echo "Fin du traitement : $(date)"
     9  exit 0
$

Exécution du script.

$ ./test.sh
$

Affichage du fichier /tmp/fichier1.log

$ nl /tmp/fichier1.log
     1  Début du traitement : lundi 12 décembre 2011, 08:23:33 (UTC+0100)
     2  1coucou
     3  24902
     4  25013
     5  25031
     6  25043
     7  boucleFor01.sh
     8  boucleFor02.sh
     9  boucleUntil01.sh
     .....
    87  Fin du traitement : lundi 12 décembre 2011, 08:23:38 (UTC+0100)
$

Affichage du fichier /tmp/fichier2.log (les fichiers *.zzz n'exsistant pas, 2 erreurs sont générées)

$ nl /tmp/fichier2.log
     1  cp: impossible d'évaluer «*.zzz»: Aucun fichier ou dossier de ce type
     2  rm: impossible de supprimer «*.zzz»: Aucun fichier ou dossier de ce type
$

Deuxième exemple :

Redirection de la sortie standard et de la sortie d'erreur standard vers le fichier /tmp/fichier3.log

$ nl ./test2.sh
     1  #!/bin/bash
     2  exec 1> /tmp/fichier3.log 2>&1
     3  echo "Début du traitement : $(date)"
     4  ls
     5  cp *.zzz /tmp
     6  rm *.zzz
     7  sleep 5
     8  echo "Fin du traitement : $(date)"
     9  exit 0
$

Exécution du script

$ ./test2.sh
$

Affichage du fichier /tmp/fichier3.log

$ nl /tmp/fichier3.log
     1  Début du traitement : lundi 12 décembre 2011, 12:56:35 (UTC+0100)
     2  1coucou
     3  24902
     4  25013
     5  25031
     6  25043
     .....
    88  cp: impossible d'évaluer «*.zzz»: Aucun fichier ou dossier de ce type
    89  rm: impossible de supprimer «*.zzz»: Aucun fichier ou dossier de ce type
    90  Fin du traitement : lundi 12 décembre 2011, 12:56:40 (UTC+0100)
$

Troisième exemple :

Redirection de l'entrée standard

$ nl test3.sh
     1  #!/bin/bash
     2  exec 0< /etc/passwd
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Lecture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done
    10  exit 0
$

Exécution du script

$ ./test3.sh
Lecture de la ligne 1
root:x:0:0:root:/root:/bin/bash
Lecture de la ligne 2
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Lecture de la ligne 3
bin:x:2:2:bin:/bin:/bin/sh
Lecture de la ligne 4
sys:x:3:3:sys:/dev:/bin/sh
Lecture de la ligne 5
sync:x:4:65534:sync:/bin:/bin/sync
Lecture de la ligne 6
games:x:5:60:games:/usr/games:/bin/sh
Lecture de la ligne 7
man:x:6:12:man:/var/cache/man:/bin/sh
.....
$

Gestion de fichiers

Les shells récents apportent une fonctionnalité supplémentaire.
La possibilité d'ouvrir et de manipuler des fichiers en utilisant des descripteurs compris entre 3 et 9 (en supplément de 0, 1 et 2).
L'avantage est de pouvoir manipuler des fichiers tout en conservant les descripteurs 0, 1 et 2 connectés sur le terminal.

Ouverture de fichier :

En lecture

exec  desc <fichier

En écriture

exec  desc >fichier

Lecture à partir d'un fichier :

read variable1 variable2 ..... variablen <&desc

ou

read -udesc variable1 variable2 ..... variablen

Ecriture dans un fichier :

echo variable1 variable2 ..... variablen >&desc

ou

print -udesc variable1 variable2 ..... variablen

Fermeture d'un fichier :

Syntaxe :

Ouvert en lecture

exec  desc <&-

Ouvert en écriture

exec  desc >&-

Exemple :

$ nl test4.sh
     1  #!/bin/bash
     2  # Ouverture du fichier /etc/passwd en lecture sous le descripteur 3
     3  # et du fichier /tmp/resultat.log en écriture sous le descripteur 4
     4  exec 3</etc/passwd 4>/tmp/resultat.log
     5  cpt=1
     6  # Lecture ligne par ligne du fichier /etc/passwd
     7  # correspondant au descripteur 3
     8  while read -u3 ligne
     9  do
    10          # Ecriture des données dans le fichier /tmp/resultat.log
    11          # correspondant au descripteur 4
    12          echo "Ecriture de la ligne $cpt" >&4
    13          echo $ligne >&4
    14          ((cpt+=1))
    15  done
    16  # Fermeture du fichier /etc/passwd correspondant au descripteur 3
    17  exec 3<&-
    18  # Fermeture du fichier /tmp/resultat.log correspondant au descripteur 4
    19  exec 4>&-
    20  exit 0
$

Résultat :

$ ./test4.sh
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
    .....
$

Traitement d'un fichier

Les diffétrentes façons d'exploiter un fichier

Rediriger l'exécution du script

Les redirections peuvent également être faites au moment de l'exécution du script.

Exemple :

Avec le script suivant

$ nl test5.sh
     1  #!/bin/bash
     2  cpt=1
     3  # Lecture ligne par ligne du fichier passé en paramètre
     4  # ou lecture de la saisie clavier si pas de fichier en paramètre
     5  while read ligne
     6  do
     7          # Ecriture des données dans le fichier passé en paramètre
     8          # ou affichage à l'écran si pas de fichier en paramètre
     9          echo "Ecriture de la ligne $cpt"
    10         echo $ligne
    11         ((cpt+=1))
    12  done
    13  exit 0
$

Exécution du script sans paramètre

$ ./test5.sh
saisie 1     # saisie
Ecriture de la ligne 1
saisie 1
saisie 2     # saisie
Ecriture de la ligne 2
saisie 2
saisie 3     # saisie
Ecriture de la ligne 3
saisie 3
^d
$

Exécution du script avec paramètre en entrée

$ ./test5.sh < /etc/passwd
Ecriture de la ligne 1
root:x:0:0:root:/root:/bin/bash
Ecriture de la ligne 2
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Ecriture de la ligne 3
bin:x:2:2:bin:/bin:/bin/sh
Ecriture de la ligne 4
sys:x:3:3:sys:/dev:/bin/sh
Ecriture de la ligne 5
sync:x:4:65534:sync:/bin:/bin/sync
Ecriture de la ligne 6
games:x:5:60:games:/usr/games:/bin/sh
Ecriture de la ligne 7
man:x:6:12:man:/var/cache/man:/bin/sh
.....
$

Exécution du script avec paramètres en entrée et en sortie

$ ./test5.sh < /etc/passwd > /tmp/resultat.log
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
     .....
$

Redirections internes au script

Les redirections d'entrée (0) et de sortie (1) standards peuvent également être faite à l'intérieur du script.

Exemple :

Avec le script suivant

$ nl test6.sh
     1  #!/bin/bash
     2  exec </etc/passwd >/tmp/resultat.log
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Ecriture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done
    10  exit 0
$

Exécution du script et affichage du résultat

$ ./test6.sh
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
     7  Ecriture de la ligne 4
     8  sys:x:3:3:sys:/dev:/bin/sh
     9  Ecriture de la ligne 5
    10  sync:x:4:65534:sync:/bin:/bin/sync
    11  Ecriture de la ligne 6
    12  games:x:5:60:games:/usr/games:/bin/sh
    13  Ecriture de la ligne 7
    14  man:x:6:12:man:/var/cache/man:/bin/sh
     .....
$

Redirection d'un bloc

Il est également possible de rediriger uniquement les commandes situées à l'intérieur d'une structure de contrôle.
Les redirections doivent être écrites derrière le mot clé qui ferme la structure de contrôle.
A l'exécution, elles sont mises en place avant le traitement de la structure de contrôle.*

Exemple :

Dans le script suivant, seules les commandes situées à l'intérieur de la boucle while seront redirigées.

$ nl test7.sh
     1  #!/bin/bash
     2  echo "Lancement du script"
     3  cpt=1
     4  while read ligne
     5  do
     6          echo "Ecriture de la ligne $cpt"
     7          echo $ligne
     8          ((cpt+=1))
     9  done </etc/passwd >/tmp/resultat.log
    10  exit 0
$ ./test7.sh
Lancement du script
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
$

Rediriger un bloc avec des fichiers ouverts en amont

$ nl test8.sh
     1  #!/bin/bash
     2  exec 3</etc/passwd 4>/tmp/resultat.log
     3  echo "Lancement du script"
     4  cpt=1
     5  while read ligne
     6  do
     7          # Ecriture des données dans le fichier /tmp/resultat.log
     8          # correspondant au descripteur 4
     9          echo "Ecriture de la ligne $cpt"
    10          echo $ligne
    11          ((cpt+=1))
    12  done <&3 >&4
    13  echo "Fin du script"
    14  exec 3<&-
    15  exec 4>&-
    16  exit 0
$ ./test8.sh
Lancement du script
Fin du script
$ nl /tmp/resultat.log
     1  Ecriture de la ligne 1
     2  root:x:0:0:root:/root:/bin/bash
     3  Ecriture de la ligne 2
     4  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     5  Ecriture de la ligne 3
     6  bin:x:2:2:bin:/bin:/bin/sh
$

Découper une ligne en champs

Si les lignes du fichier à traiter sont structurées en champs, il est très facile de récupérer chacun de ceux ci dans une variable.
Pour cela, il faut modifier la valeur de la variable IFS.

Exemple :

Le script suivant génère, à partir du fichier /etc/passwd, un affichage à l'écran du username suivi de son uid.
La variable IFS est sauvegardée (ligne 13) afin d'être restaurée (ligne 19) et son contenu est remplacé par ":" (ligne 14).
":" étant le caractère séparant les champs du fichier /etc/passwd.
La commande read reçoit 7 variables en argument (ligne 15). Autant de variables que de champs dans le fichier /etc/passwd.
Elle découpe donc la ligne lue (ligne 18) en champs en utilisant le caractère ":" comme séparateur.
La ligne est donc automatiquement découpée et les valeurs récupérables via les variables indiquées.

$ nl test9.sh
     1  #!/bin/bash
     2  if (( $# != 1 ))
     3  then
     4          echo "Mauvais nombre d'arguments"
     5          echo "Usage : $0 fichier"
     6          exit 1
     7  fi
     8  if [[ ( ! -f "$1" ) || ( ! -r "$1" ) ]]
     9  then
    10          echo "$1 n'est pas un fichier ordinaire ou n'est pas accessible en lecture"
    11          exit 2
    12  fi
    13  oldIFS="$IFS"
    14  IFS=":"
    15  while read username password uid gid nomComplet home shell
    16  do
    17          echo "$username ==> $uid"
    18  done < $1
    19  IFS="$oldIFS"
    20  exit 0
$ ./test9.sh /etc/passwd
root ==> 0
daemon ==> 1
bin ==> 2
sys ==> 3
$

Ce script peut très bien être dirigé dans un tube et exploité avec la commande grep

$ ./test9.sh /etc/passwd | grep "root"
root ==> 0
$

La commande eval

Syntaxe :

eval expr1 expr2 ..... exprn

La commande eval permet de faire subir à une ligne de commande une double évaluation.

Exemple :

Initialisation de la variable nom

$ nom=toto

Initialisation de la variable var avec le nom de la variable définie ci dessus

$ var=nom

Affichage des valeurs des variables

$ echo $nom
toto
$ echo $var
nom
$ echo \$$var
$nom
$

Pour obtenir $var=toto, il faut obligatoirement utiliser la commande eval de cette manière

$ eval echo \$$var
toto
$

La commande eval évalue en premier la variable $var, le premier $ étant protégé par un anti-slash

==> eval echo \$nom

puis évalue le résultat de la première évaluation, c'est à dire $nom (suppression de l'anti-slash)

==> eval echo $nom

Initialisation de la variable var2

$ var2=\$$var
$ echo $var2
$nom
$ eval var2=\$$var
$ echo $var2
toto
$

Gestion des signaux

Il est possible de modifier le comportement des signaux envoyés au shell en utilisant la commande trap.

Principaux signaux

Liste des signaux :

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
$

Détail des signaux :

  1. HUP    hangup : envoie un signal de réinitialisation
  2. INT    Interruption
  3. QUIT    Core dump
  4. ILL    Le programme tente d'exécuter du code malformé, inconnu ou avec de mauvais privilèges
  5. TRAP    Le programme envoie un signal au débugger (message capturé)
  6. IOT    idem SIGABRT : interruption du programme
  7. EMT    emulator trap : un programme émulé ou virtualisé a posé problème
  8. FPE    floating-point exception : le programme a réalisé une opération arithmétique erronée
  9. KILL    arrête le programme immédiatement
  10. BUS    Le programme a causé une erreur de bus
  11. SEGV    Segmentation fault : le programme fait référence à un mauvais emplacement de mémoire
  12. SYS    Un mauvais argument est passé en paramètre
  13. PIPE    Un programme tente d'écrire dans un pipe sans processus connecté à l'autre bout
  14. ALRM    La limite de temps est dépassée
  15. TERM    Envoie un signal au programme pour le terminer
  16. USR1/USR2    Envoie un signal dans des conditions définies par un utilisateur
  17. CHLD/CLD    child : signal envoyé par un programme lorsqu'un processus fils est achevé
  18. PWR    power : le système subit un problème d'alimentation
  19. VTALRM    virtual alarm : signal envoyé lorsque le temps limite a été dépassé
  20. PROF    profiler : signal envoyé lorsqu'un timer a expiré
  21. POLL    polling : un problème est survenu lors d'un événement I/O asynchrone
  22. WINCH    window [size] change : signal envoyé au programme lorsque la fenêtre de contrôle change de taille
  23. STOP    signal demandant au programme de se suspendre
  24. TSTP    tty stop : signal envoyé au programme lorsqu'un terminal suspend ses requêtes
  25. CONT    Redémarre un programme suspendu par STOP
  26. TTIN    Le programme tente de lire tty alors qu'il est en arrière-plan
  27. TTOU    Le programme tente d'écrire sur tty alors qu'il est en arrière-plan
  28. URG    Un socket a une donnée urgente à lire
  29. LOST    Le verrou d'un fichier a été perdu
  30. XCPU    Le programme a utilisé le CPU prend trop longtemps
  31. XFSZ    Le fichier a dépassé la taille maximale autorisée
  32. RTMIN/RTMIN+n    real-time minimum : signaux définis par l'application
  33. RTMAX/RTMAX-n    real-time maximum : signaux définis par l'application

Dans les commandes, les signaux peuvent être exprimés sous forme numérique ou symbolique.
Les signaux HUP, INT, TERM et KILL possèdent la même valeur numérique sur toutes les plates-formes Unix, ce qui n'est pas le cas de tous les signaux.
Il est donc préférable d'utiliser la forme symbolique.

Le signal INT est généré à partir du clavier.
Il est utilisé pour tuer le processus qui tourne en avant plan.
Pour connaitre la saisie clavier correspondant au signal INT, voir le paramètre intr de la commande stty -a.

$ stty -a
speed 38400 baud; rows 36; columns 134; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S;
susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
$

Ignorer un signal

Syntaxe :

trap ' ' sig1 sig2

Exemple :

Le shell courant correspond au PID 30819

$ echo $$
30819

Modification des signaux HUP et TERM

$ trap '' HUP TERM

Envoi des signaux HUP et TERM

$ kill -HUP 30819
$ kill -TERM 30819

Les signaux sont ignorés et le processus est toujours actif

$ echo $$
30819
$

Modifier le traitement associé à un signal

Syntaxe :

trap 'cmd1 ; cmd2 ; cmd3 ; ..... ; cmdn' sig1 sig2

Exemple :

Le shell courant correspond au PID 23217

$ ps
  PID TTY          TIME CMD
22109 pts/0    00:00:00 bash
23217 pts/0    00:00:00 bash
23465 pts/0    00:00:00 ps
$ echo $$
23217

Modification du traitement associé au signal ^C (ctrl+c)
On demande au shell d'afficher le message "Signal INT reçu" après avoir appuyer sur ^C (ctrl+c)

$ trap 'echo "Signal INT reçu" ; exit 1' INT
$ ^C     # Saisie
$ Signal INT reçu

Le shell courant correspondant au PID 23217 n'existe plus

$ ps
  PID TTY          TIME CMD
22109 pts/0    00:00:00 bash
24229 pts/0    00:00:00 ps
$ echo $$
22109
$

Repositionner le traitement par défaut du shell vis-à-vis d'un signal

Syntaxe :

trap - sig1 sig2 ..... sign

Exemple :

Création du fichier /tmp/fichier

$ > /tmp/fichier
$ ls /tmp/fichier
/tmp/fichier

Modification du traitement associé au signal INT et TERM
On demande au shell de supprimer le fichier "/tmp/fichier" après avoir appuyer sur ^C (ctrl+c)

$ trap 'rm -f /tmp/fichier' INT TERM

Le shell courant correspond au PID 22109

$ echo $$
22109

Envoi du signal INT "^C" (ctrl+c)

$ ^C

Le fichier a bien été suprimé

$ ls /tmp/fichier
ls: impossible d'accéder à /tmp/fichier: Aucun fichier ou dossier de ce type

Création d'un nouveau fichier /tmp/fichier

$ > /tmp/fichier
$ ls /tmp/fichier
/tmp/fichier

Traitement associé au signal INT et TERM remis par défaut

$ trap - INT TERM

Le shell courant correspond au PID 22109

$ echo $$
22109

Envoi du signal INT "^C" (ctrl+c)

$ ^C

Le fichier n'a pas été supprimé

$ ls /tmp/fichier
/tmp/fichier
$

Utiliser trap à partir d'un script shell

L'utilisation de trap dans un script shell va permettre de gérer des actions en fonctions de différents signaux reçus.

Exemple :

Dans le script suivant, à la réception d'un signal HUP INT ou TERM, la fonction "fin" est appelée et le fichier $fileTmp est supprimé.

$ nl signaux.sh
     1  #!/bin/bash
     2
     3  # Nom du fichier temporaire
     4  fileTmp=/tmp/fileTemp
     5
     6  # Fonction appelée lors de la réception d'un signal HUP INT TERM
     7  function fin {
     8          echo -e "\nSuppression du fichier $fileTmp"
     9          echo "Fin du script"
    10          rm -f $fileTmp
    11          ls $fileTmp
    12          exit 1
    13  }
    14
    15  # Paramétrage de la fonction "fin" à la réception d'un signal HUP INT TERM
    16  trap fin HUP INT TERM
    17
    18  # Création du fichier temporaire
    19  > $fileTmp
    20
    21  echo "Lancement du script"
    22  # Vérification de la création du fichier temporaire
    23  ls $fileTmp
    24  sleep 100
    25  echo "Arrêt du script"
    26  exit 0
$

Exécution du script :

$ ./signaux.sh
Lancement du script
/tmp/fileTemp
^C     # Envoi du signal
Suppression du fichier /tmp/fileTemp
Fin du script
ls: impossible d'accéder à /tmp/fileTemp: Aucun fichier ou dossier de ce type
$

Gestion de menu avec select

Syntaxe :

select var in item1 item2 item3 ..... itemn
do
     commandes
done

La commande interne select est une structure de contrôle de type boucle qui permet d'afficher de manière cyclique un menu.
La liste des items sera affichée à l'écran à chaque tour de boucle.
Les items sont indicés automatiquement.
La variable var sera initialisée avec l'item correspondant au choix de l'utilisateur.

Cette commande utilise également deux variables réservées :

  • La variable PS3 représente le prompt utilisé pour la saisiedu choix de l'utilisateur.
    Sa valeur par défaut est #?. Elle peut être modifiée si on le souhaite.
  • La variable REPLY qui contient l'indice de l'item sélectionné.

La variable var contient le libellé du choix et REPLY l'indice de ce dernier.

Exemple :

$ nl select.sh
     1  #!/bin/bash
     2
     3  function sauve {
     4          echo "Lancement de la sauvegarde"
     5  }
     6
     7  function restaure {
     8          echo "Lancement de la restauration"
     9  }
    10
    11  PS3="Votre choix : "
    12
    13  select item in "- Sauvegarde -" "- Restauration -" "- Fin -"
    14  do
    15          echo "Vous avez choisi l'item $REPLY : $item"
    16          case $REPLY in
    17                  1)
    18                          # Appel de la fonction sauve
    19                          sauve
    20                          ;;
    21                  2)
    22                          # Appel de la fonction restaure
    23                          restaure
    24                          ;;
    25                  3)
    26                          echo "Fin du script"
    27                          exit 0
    28                          ;;
    29                  *)
    30                          echo "Choix incorrect"
    31                          ;;
    32          esac
    33  done
$

Exécution :

$ ./select.sh
1) - Sauvegarde -
2) - Restauration -
3) - Fin -
Votre choix : 2
Vous avez choisi l'item 2 : - Restauration -
Lancement de la restauration

La saisie de la touche [Entrée] permet de réafficher le menu :

$ .
Votre choix :     # Appui sur la touche [Entrée]
1) - Sauvegarde -
2) - Restauration -
3) - Fin -
Votre choix : 3
Vous avez choisi l'item 3 : - Fin -
Fin du script
$

Analyse des options d'un script avec getopts

$ type getopts
getopts est une primitive du shell
$

Syntaxe :

getopts listeOptionsAttendues option

La commande interne getopts permet à un script d'anayser les options passées en argument.
Chaque appel à la commande getopts analyse l'option suivante de la ligne de commande.
Pour vérifier la validité de chacune des options, il faut appeler getopts à partir d'une boucle.

Définition d'une option

Pour getopts, une option est composée d'un caractère précédé du signe "+" ou "-".

Premier exemple :

$ ls -l *.sh

Par exemple, pour la commande ls, "-l" est une option et "*.sh" est un argument.
Une option peut fonctionner seule ou être associée à un argument.

Second exemple :

Le script suivant détail la manière d'utiliser la commande getopts.

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts "abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29          esac
    30  done
    31  echo "Analyse des options terminée"
    32  exit 0
$

L'appel à la commande getopts récupère l'option suivante et retourne un code vrai tant qu'il reste des options à analyser.
La liste des options utilisables avec ce script sont définies à la ligne 3 (getopts "abcd:e:" option). Il s'agit des options -a, -b, -c, -d et -e.
Le caractère ":" inscrit après les options "d" et "e" (getopts "abcd:e:" option) indique que ces options doivent être suivies obligatoirement d'un argument.
La variable "option" (getopts "abcd:e:" option) permet de récupérer la valeur de l'option en cours de traitement par la boucle while.
La variable réservée "$OPTIND" contient l'indice de la prochaine option à traiter.
La variable réservée "$OPTARG" contient l'argument associé à l'option.

Exécution du script avec des options valides :

$ ./test_getopts_1.sh -a -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 8
Analyse des options terminée
$

Option invalide

Lorsque la commande getopts détecte une option invalide, la variable option est initialisée avec la caractère "?" et un message d'erreur est affiché à l'écran.
Les options suivantes sont analysées.

Exemple :

L'option -z ne fait pas partie de la liste des options attendues.

$ ./test_getopts_1.sh -a -z -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
./test_getopts_1.sh : option non permise -- z
getopts a trouvé l'option ?

getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 5
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 7
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 9
Analyse des options terminée
$

Gestion des erreurs

Si le caractère ":" est placé en première position dans la liste des options à traiter (ligne 3), les erreurs sont gérées différemment.

En cas d'option invalide :
- getopts n'affichera pas de message d'erreur.
- la variable OPTARG sera initialisée avec la valeur de l'option incorrecte (ligne 29).

Exemple :

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts ":abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29                  \?)
    30                          echo "$OPTARG : option invalide"
    31                          exit 1
    32                          ;;
    33          esac
    34  done
    35  echo "Analyse des options terminée"
    36  exit 0
$

Le message d'erreur généré automatiquement par getopts n'apparait plus et la variable $OPTARG a été substituée par la valeur de l'option incorrecte.

Ligne 29, le "?" doit être protégé par un anti-slash pour ne pas être interprété par le shell.

$ ./test_getopts_1.sh -a -z -b -c -d toto -e tata,tutu
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option ?
z : option invalide
$

Option valide avec argument manquant

Lorsque l'argument d'une option est absent, la variable option est initialisée avec le caractère ":" et OPTARG contient la valeur de l'option concernée (ligne 29).

Exemple :

$ nl test_getopts_1.sh
     1  #!/bin/bash
     2
     3  while getopts ":abcd:e:" option
     4  do
     5          echo "getopts a trouvé l'option $option"
     6          case $option in
     7                  a)
     8                          echo "Exécution des commandes de l'option a"
     9                          echo "Indice de la prochaine option à traiter : $OPTIND"
    10                          ;;
    11                  b)
    12                          echo "Exécution des commandes de l'option b"
    13                          echo "Indice de la prochaine option à traiter : $OPTIND"
    14                          ;;
    15                  c)
    16                          echo "Exécution des commandes de l'option c"
    17                          echo "Indice de la prochaine option à traiter : $OPTIND"
    18                          ;;
    19                  d)
    20                          echo "Exécution des commandes de l'option d"
    21                          echo "Liste des arguments à traiter : $OPTARG"
    22                          echo "Indice de la prochaine option à traiter : $OPTIND"
    23                          ;;
    24                  e)
    25                          echo "Exécution des commandes de l'option e"
    26                          echo "Liste des arguments à traiter : $OPTARG"
    27                          echo "Indice de la prochaine option à traiter : $OPTIND"
    28                          ;;
    29                  :)
    30                          echo "L'option $OPTARG requiert un argument"
    31                          exit 1
    32                          ;;
    33                  \?)
    34                          echo "$OPTARG : option invalide"
    35                          exit 1
    36                          ;;
    37          esac
    38  done
    39  echo "Analyse des options terminée"
    40  exit 0
$

Exécution du script en oubliant l'argument de l'option -e

$ ./test_getopts_1.sh -a -b -c -d toto -e
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option :
L'option e requiert un argument
$

Gestion d'arguments supplémentaires

Les options sont stockées dans les paramètres positionnels $1, $2 ..... $n.
Une fois que celles ci sont analysées, il est possible de s'en débarasser avec la commande shift.
Ceci est intéressant s'il reste des arguments à traiter derrière les options.

Exemple :

$ nl test_getopts_1.sh | tail
    37          esac
    38  done
    39  echo "Analyse des options terminée"
    40  echo "Avant shift : "
    41  echo "Liste des arguments : $*"
    42  echo "Indice de la prochaine option à traiter : $OPTIND"
    43  shift $((OPTIND-1))
    44  echo "Après shift : "
    45  echo "Liste des arguments : $*"
    46  exit 0
$

L'instruction à la ligne 43 permet de retirer les options de la liste des arguments.
L'expression OPTIND-1 représente le nombre d'options analysées, donc la valeur du décalage à réaliser.

Exécution du script avec des arguments supplémentaires :

$ ./test_getopts_1.sh -a -b -c -d toto -e tata,tutu arg1 arg2 arg3 arg4
getopts a trouvé l'option a
Exécution des commandes de l'option a
Indice de la prochaine option à traiter : 2
getopts a trouvé l'option b
Exécution des commandes de l'option b
Indice de la prochaine option à traiter : 3
getopts a trouvé l'option c
Exécution des commandes de l'option c
Indice de la prochaine option à traiter : 4
getopts a trouvé l'option d
Exécution des commandes de l'option d
Liste des arguments à traiter : toto
Indice de la prochaine option à traiter : 6
getopts a trouvé l'option e
Exécution des commandes de l'option e
Liste des arguments à traiter : tata,tutu
Indice de la prochaine option à traiter : 8
Analyse des options terminée
Avant shift :
Liste des arguments : -a -b -c -d toto -e tata,tutu arg1 arg2 arg3 arg4
Indice de la prochaine option à traiter : 8
Après shift :
Liste des arguments : arg1 arg2 arg3 arg4

$

Gestion d'un processus en arrière plan

La commande wait permet au shell d'attendre la terminaison d'un processus lancé en arrière-plan.

Syntaxes :

Attendre la terminaison du processus dont le PID est donné en argument :

wait pid1

Attendre la terminaison de tous les processus lancés en arrière-plan à partir du shell courant :

wait

Attendre la terminaison du processus dont le numéro de job est donné en argument :

wait %job

Exemples :

La commande find est lançée en arrière-plan et a pour PID 2878 :

$ find / -name /etc/passwd 1>/tmp/resu 2>/dev/null &
[1] 2878
$ jobs
[1]+  Running                 find / -name /etc/passwd > /tmp/resu 2> /dev/null &
$

Le shell s'endort en attendant la terminaison du processus 2878 :

$ wait 2878     # Ou wait %1

Le shell est réveillé lorsque le processus 2878 est terminé :

$ .
[1]+  Exit 1                  find / -name /etc/passwd > /tmp/resu 2> /dev/null
$

Le PID de la dernière commande lançée en arrière-plan est contenu dans la variable spéciale $!

Le script suivant lance en arrière-plan une recherche du fichier /etc/passwd (ligne 3).
D'autres actions peuvent être exécutées en attendant (ligne 5 à 8) puis le shell attend la fin de la recherche (ligne 9) avant d'afficher à l'écran le contenu du fichier /tmp/resu (ligne 12).

$ nl test_wait_1.sh
     1  #!/bin/bash
     2
     3  find / -name /etc/passwd 1>/tmp/resu 2>&1 &
     4  echo "Le PID du script lancé en arrière-plan est le : $!"
     5  echo "Début des autres commandes"
     6
     7  echo "Fin des autres commandes"
     8  echo "Recherche en cours - Attente de la fin de la recherche"
     9  wait $!
    10  echo "La recherche est terminée"
    11  echo "Affichage du résultat"
    12  most /tmp/resu
    13  exit 0
$
$ ./test_wait_1.sh
Le PID du script lancé en arrière-plan est le : 11697
Début des autres commandes
Fin des autres commandes
Recherche en cours - Attente de la fin de la recherche
.....      # Le shell s'endort
.....
La recherche est terminée
Affichage du résultat
$

Script d'archivage incrémental et transfert sftp automatique

Le script suivant utilise toutes les techniques vues précédemment

CPIO

Pré-requis pour que le script suivant fonctionne :

1 - Paramétrer sur le poste à sauvegarder un accès ssh au serveur de sauvegarde via un système d'authentification par clé privée / publique.

Tout est expliqué ici : http://quennec.fr/linux/utilisation/connexion-a-une-machine-distante-sans-saisir-le-mot-de-passe

2 - Le programme bzip2 doit être installé sur le poste à sauvegarder.

$ apt-get install bzip2

3 - Rendre les scripts exécutables

$ chmod +x uploadBackup.sh && chmod +x fonctions.inc.sh

Script d'archivage incrémental et transfert sftp automatique

Ce script permet d'effectuer une sauvegarde incrémentale d'un répertoire vers un serveur de sauvegarde.
Il utilise un système de niveau correspondant au jour du mois - 1.
Tous les 1er de chaque mois, une sauvegarde totale (niveau 0) est effectuée.
Une sauvegarde incrémentale est effectuée les jours suivants.

Le script est composé de 2 fichiers.
Un fichier comportant le script principal (uploadBackup.sh) et un fichier comportant les fonctions personnalisées utiles au script principal (fonctions.inc.sh).

Les fichiers à sauvegarder (variable DATADIR) sont enregistrés dans une archive CPIO compressée avec BZIP2 et stockée dans un répertoire local sur le poste à sauvegarder (variable ARCHIVEDIR).
Avant chaque sauvegarde, un fichier de niveau est créé dans le répertoire local.
Toutes les logs sont enregistrées dans un fichier stocké également dans le répertoire local.
La sauvegarde incrémentale utilise le fichier niveau précédent afin de sauvegarder tous les fichiers modifiés depuis la sauvegarde précédente grâce à la commande find et l'option -newer.
Enfin, l'archive CPIO est envoyée sur le serveur de sauvegarde (variable serveur_sauvegarde) via SFTP et stockée dans des sous-répertoires correspondant à l'année et au mois de la sauvegarde dans le dossier de sauvegarde (variable dossier_distant) .

Détail du fichier uploadBackup.sh

$ nl uploadBackup.sh
     1  #!/bin/bash
     2  # set -x
     3  # Répertoire des scripts shell
     4  SCRIPTDIR="/root/script_archivage_incremental"
     5  # Répertoire des fichiers à sauvegarder
     6  DATADIR="/root/dossier_a_sauvegarder"
     7  # Répertoire local des archives
     8  ARCHIVEDIR="/root/local_backup"
     9  # Inclure les fonctions
    10  . $SCRIPTDIR/fonctions.inc.sh
    11  # Fichier de log
    12  LOG=$ARCHIVEDIR/`getDate`.log
    13  # Redirection de toutes les sorties du script vers le fichier de log
    14  exec 1>$LOG 2>&1
    15  # Déterminer le niveau de sauvegarde
    16  # Le 1er du mois => niveau 0
    17  jourDuMois=`getDayForCalcul`
    18  ((niveau=$jourDuMois-1))
    19  case $niveau in
    20          0) # Sauvegarde totale
    21                  # Nettoyage du répertoire d'archive
    22                  rm -i $ARCHIVEDIR/*.bz2 $ARCHIVEDIR/niveau*
    23                  # Création du fichier de niveau (niveau 0)
    24                  touch $ARCHIVEDIR/niveau0
    25                  archive="$ARCHIVEDIR/`getDate`_niveau0.cpio"
    26                  find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    27                  ;;
    28          *)
    29                  # Creation du fichier de niveau
    30                  touch $ARCHIVEDIR/niveau$niveau
    31                  archive="$ARCHIVEDIR/`getDate`_niveau${niveau}.cpio"
    32                  # Determination du niveau precedent
    33                  ((niveauPrec=$niveau-1))
    34                  # Test si le fichier de niveau precedent existe
    35                  if [[ ! -f $ARCHIVEDIR/niveau$niveauPrec ]]
    36                  then
    37                          # Si il n'existe pas, sauvegarde integrale du repertoire
    38                          echo "Fichier de niveau $niveauPrec inexistant"
    39                          echo "Execution d'une sauvegarde integrale en cours de mois"
    40                          find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    41                  else
    42                          # Sauvegarde incrementale
    43                          find $DATADIR -newer $ARCHIVEDIR/niveau$niveauPrec | cpio -ocv | bzip2 -c > $archive.bz2
    44                  fi
    45                  ;;
    46  esac
    47  # Vérification de la validité de l'archive
    48  if isArchiveInvalide $archive.bz2 ; then
    49          echo "Archive $archive.bz2 INVALIDE - Fichier non transfere"
    50          exit 1
    51  fi
    52  # Transfert du fichier vers le serveur de sauvegarde
    53  if transfert ${archive}.bz2 ; then
    54          echo "Transfert realise avec succes"
    55          exit 0
    56  fi
    57  # Si le transfert a echoue
    58  echo "Echec de transfert"
    59  exit 1
$

Détail du fichier fonctions.inc.sh

$ nl fonctions.inc.sh
     1  #!/bin/bash
     2  function transfert {
     3          typeset mois
     4          typeset annee
     5          # Recuperation de la valeur du premier argument passe a la fonction
     6          # Recuperation du nom de l'archive a envoyer au serveur de sauvegarde
     7          typeset ficATransferer=$1
     8          # Adresse du serveur de sauvegarde
     9          typeset serveur_sauvegarde="192.168.1.113"
    10          # Compte utilise pour se connecter au serveur de sauvegarde
    11          typeset user="root"
    12          # Chemin absolu du dossier ou sera stocke la sauvegarde
    13          typeset dossier_distant="/root/sauvegarde_dossier"
    14          mois=`getMonth`
    15          annee=`getYear`
    16          # Test si le dossier de sauvegarde existe sur le serveur de sauvegarde
    17          # Connexion au serveur avec le user root
    18          ssh $user@$serveur_sauvegarde test -d $dossier_distant/$annee/$mois
    19          # Test sur le code retour de la commande precedente
    20          case $? in
    21                  0)
    22                          ;;
    23                  255)
    24                          echo "Echec de la commande SSH"
    25                          return 1
    26                          ;;
    27                  *)
    28                          # Si code retour different de 0 et 255
    29                          # Creation du repertoire de la sauvegarde
    30                          ssh $user@$serveur_sauvegarde mkdir -p $dossier_distant/$annee/$mois
    31                          ;;
    32          esac
    33          # Connexion au serveur de sauvegarde en FTP sécurisé
    34          # Ne pas oublier les doubles chevrons << avant le mot cle FIN
    35          # Ne pas mettre d'espace entre << et FIN
    36          sftp -b - $user@$serveur_sauvegarde <<FIN
    37          # Positionnement dans le repertoire de la sauvegarde
    38          cd $dossier_distant/$annee/$mois
    39          pwd
    40          # Envoi de la sauvegarde
    41          put $ficATransferer
    42  FIN
    43  # Ne pas mettre de tabulation ou d'espace devant le mot clé FIN
    44  # Sinon celui-ci n'est pas reconnu
    45          # Test sur le code retour de la commande SFTP
    46          # Si le code retour est different de 0
    47          # Une anomalie a ete rencontree
    48          (( $? != 0 )) && return 1
    49          # Tester si archive valide sur serveur de sauvegarde
    50          ficSurMachineCible=$(basename $ficATransferer)
    51          ssh $user@$serveur_sauvegarde bzip2 -t $dossier_distant/$annee/$mois/$ficSurMachineCible
    52          case $? in
    53                  0)
    54                          ;;
    55                  255)
    56                          echo "Echec de la commande SSH"
    57                          return 1
    58                          ;;
    59                  *)
    60                          # Si code retour different de 0 et 255
    61                          # Alors l'archive est invalide
    62                          echo "Archive $dossier_distant/$annee/$mois/$ficSurMachineCible INVALIDE"
    63                          return 1
    64                          ;;
    65          esac
    66          return 0
    67  }
    68  function isArchiveInvalide {
    69          typeset archive=$1
    70          bzip2 -t $archive 2>/dev/null && return 1
    71          return 0
    72  }
    73  function getDate {
    74          date '+%Y_%m_%d'
    75  }
    76  function getYear {
    77          date '+%Y'
    78  }
    79  function getMonth {
    80          date '+%m'
    81  }
    82  function getDayForCalcul {
    83          date '+%e' | sed 's/ //'
    84  }
$

Les expressions régulières

Les expressions régulières sont utilisées par un certain nombre de commandes UNIX.

Les expressions régulières sont composées de caractères ordinaires et de caractères spéciaux qui ont une signification particulière.

Il existe 2 types d'expressions régulières :

  • Les expressions régulières basiques (ERb)
  • Les expressions régulières étendues (ERe)

Les ERb sont utilisées par les commandes suivantes :

  • vi
  • grep
  • expr
  • sed

Les ERe sont utilisées par les commandes suivantes :

  • grep avec l'option -e (egrep)
  • awk

 

Caractères communs aux ERb et ERe

Le tableau suivant liste les caractères spéciaux communs aux ERb et aux ERe

Caractère spécial Signification
^ Début de ligne
$ Fin  de ligne
. (point) Un caractère quelconque
[liste_de_caractères] Un caractère cité dans la liste
[^liste_de_caractères] Un caractère qui n'est pas cité dans la liste
* 0 à n fois le caractère ou regroupement précédent
\<expression Début d'un mot. Caractères pouvant faire partie d'un mot : [A-Za-z0-9_]
expression\> Fin d'un mot
\<expression\> Mot complet
\c Protection du caractère spécial "c"

Exemples :

Les expressions "space" et "tab" représentent les touches [espace] et [tabulation].
La tabulation est représentée par les caractères "\t".

Expression régulière Signification
soleil Chaine contenant "soleil".
Exemple de correspondance :
Aujourd'hui il y a du soleil, mais demain il pleut !
soleil toujours.
Passer ses vacances au soleil.
^soleil Chaine commençant par "soleil".
Exemple de correspondance :
soleil toujours.
soleil$ Chaine se terminant par "soleil".
Exemple de correspondance :
Passer ses vacances au soleil.
^[A-Z][5-9].$ Chaine composée de 3 caractères.
Le premier est une majuscule, le second est un chiffre entre 5 et 9 et le dernier un caractère quelconque.
Exemple de correspondance :
B6z
Z5*
^$ Chaine vide (aucun caractère entre ^ et $)
^[space tab]*$ Chaine contenant entre 0 et n caractères espace ou tabulation
7space[0-79]A* Chaine contenant le chiffre 7, suivi d'un espace, suivi de n'importe quel chiffre sauf 8, suivi de la lettre A 0 à n fois.
Exemple de correspondance :
x7 6
abc7 9Axy
7 1AAAAAAAAAAAAAAAAbfgddghjgdhj
[0-9][^A-Z_]$ Chaine dont l'avant dernier caractère est un chiffre et le dernier n'est ni une lettre majuscule ni un caractère souligné.
Exemple de correspondance :
AZER1a
3*
440008b
\<tout Chaine contenant un mot commençant par "tout".
Exemple de correspondance :
bonjour tout le monde
il faut dire toutefois
Exemple de non correspondance :
partout dans le monde
\<tout\>

Chaine contenant le mot "tout".
Exemple de correspondance :
bonjour tout le monde
Exemple de non correspondance :
partout dans le monde
il faut dire toutefois

[0-9][0-9]\.[0-9] Chaine contenant 2 chiffres suivi du caractère . (point) suivi d'un chiffre.
Exemple de correspondance :
4576.45

Les marqueurs ^ et $ perdent leur signification s'ils ne sont pas placés respectivement en début et en fin de l'expression régulière.

Etiquettes: 

Caractères spécifiques aux ERb

Caractère spécial Signification
\{n\} n fois le caractère précédent
\{n,\} Au moins n fois le caractère précédent
\{n,x\} Entre n et x fois le caractère précédent
\(ERb\) Mémorisation d'une ERb
\1, \2, ... Rappel de mémorisation

Dans les ERB, le caractère "\" donne une signification spéciale aux parenthèses et accolades.

Exemple :

Expression régulière Signification
\-[A-Z]\{2\}\- Chaine contenant un mot de 2 lettres majuscules entouré par des tirets (-)
Exemples de correspondance :
BOUCHES-DU-RHONE
EURE-ET-LOIR
VAL-DE-MARNE

Avec la commande grep (ou autre), le tiret (-) doit être protégé par un antislash (\) pour éviter d'être interprété comme une option.

Caractères spécifiques aux ERe

Caractère spécial Signification
? 0 ou 1 fois le caractère ou regroupement précédent
+ 1 à n fois le caractère ou regroupement précédent
{n} n fois le caractère ou regroupement précédent
{n,} Au moins n fois le caractère ou regroupement précédent
{n,x} Entre n et x fois le caractère ou regroupement précédent
(er1) Regroupement
er1 | er2 | er3 Alternatives

Dans les ERe, les accolades et parenthèses perdent leur signification spéciale si elles sont précédées d'un antislash.

Exemples :

Expression régulière Signification
^[+-]?[0-9]+$

Chaine représentant un nombre entier d'au moins 1 chiffre précédé éventuellement du signe + ou -
Exemples de correspondance :
2
-56
+235
789654

\([a-zA-Z]{3}\) Chaine contenant 3 lettres minuscules et/ou majuscules et entourés de parenthèses.
Exemple de correspondance :
HERBIERS        (Les)   Herbiers
\([-a-zA-Z _]{4,}\) Chaine contenant au moins 4 caractères composées de lettres minuscules et/ou majuscules, de tiret (-), d'espace et de tiret bas (_) et le tout entouré de parenthèses.
Exemples de correspondance :
1er (Ouest)
CASTILLON (CANTON DE LEMBEYE)
2e  (Nord-Ouest)
^41|yotte$ Chaine commençant par "41" ou finissant par "yotte"
Exemples de correspondance :
41      57      57463   3       MOSELLE Moselle
41      88      88160   4       VOSGES  Vosges
06      976     97608   0       MAYOTTE Mayotte
(in|re)+

Chaine composée de 1 à n occurences de "in" ou de "re" (en minuscule)
Exemples de correspondance :
Finistère
Indre-et-Loire
Martinique

 

Exploitation des expressions régulières par les commandes

La commande grep

La commande grep permet l'utilisation d'expressions régulières normalisées par POSIX.

Par défaut, la commande grep utilise les ERb.

Associée à l'option -E, elle utilise les ERe.

Exemples :

Avec le fichier depts2012.txt (téléchargeable sur le site de l'INSEE à cette adresse : http://www.insee.fr/fr/methodes/nomenclatures/cog/telechargement.asp)

$ head depts2012.txt && tail depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
..............
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Rechercher la chaine "loire-atlantique" sans tenir compte de la casse :

$ cat depts2012.txt | grep -i 'loire-atlantique'
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
$

Rechercher les lignes commançant par 21 :

$ cat depts2012.txt | grep '^21'
21      08      08105   4       ARDENNES        Ardennes
21      10      10387   5       AUBE    Aube
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
$

Rechercher les lignes se terminant par "ique" :

$ cat depts2012.txt | grep -i 'ique$'
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
02      972     97209   3       MARTINIQUE      Martinique
$

Rechercher les lignes ayant 2 occurences de la lettre "s" :

$ cat depts2012.txt | grep -i 'ss'
11      91      91228   5       ESSONNE Essonne
$

Idem mais avec une ERb :

$ cat depts2012.txt | grep -i 's\{2\}'
11      91      91228   5       ESSONNE Essonne
$

Idem mais avec une ERe :

$ cat depts2012.txt | grep -iE 's{2}'
11      91      91228   5       ESSONNE Essonne
$

Rechercher les lignes ayant un tiret (-) :

$ cat depts2012.txt | grep '\-'
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhone
$

Rechercher les lignes contenant les chaines "loir" ou "tique" (peu importe la casse) :

$ cat depts2012.txt | grep -iE 'loir|tique'
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrenees-Atlantiques
26      71      71270   0       SAONE-ET-LOIRE  Saone-et-Loire
$

Etiquettes: 

La commande expr

Syntaxe :

expr chaine-de-caracteres : expression-reguliere-basique

La commande expr propose l'opérateur ":" qui permet de mettre en correspondance une chaine de caractères avec une expression régulière.

Fonctionnement de l'opérateur ":" :

  • Le nombre de caractères de chaine-de-caracteres correspondant à l'ERb expression-reguliere-basique est affiché à l'écran.
  • Si chaine-de-caracteres correspond à expression-reguliere, la commande retourne un code vrai ($? = 0). Elle retourne un code faux dans le cas contraire ($? = 1).
  • L'expression régulière est comparée par rapport au début de la variable (le "^" est implicite dans l'ERb).
  • Si une partie de l'expression régulière est mémorisée avec \( \), la commande affiche sur le terminal la portion de la chaine correspondante.

Exemples :

Vérifier que la chaine saisie est un nombre.

$ read nb1
34657
$ read nb2
456G43
$

La variable nb1 contient uniquement des chiffres.

$ expr "$nb1" : '[0-9]*$'                   # Equivaut à '^[0-9]*$'
5
$ echo $?
0
$

La variable nb2 contient une lettre.

$ expr "$nb2" : '[0-9]*$'
0
$ echo $?
1
$

Compter le nombre de caractères contenus dans une variable.

$ chaine="Ceci est une ligne de texte"
$ expr "$chaine" : '.*'
27
$

Afficher la partie de la chaine correspondant à la mémorisation (ici, le chiffre entouré de pipe "|")

$ chaine="un|deux|trois|4|cinq|six|sept"
$ expr "$chaine" : '.*|\([0-9]\{1\}\)'
4
$

Le caractère * recherche toujours la chaine la plus longue. Le "|" le plus à droite.

$ expr "$chaine" : '\(.*\)|'
un|deux|trois|4|cinq|six
$

Solution permettant de s'arrêter au premier "|".

$ expr "$chaine" : '\([^|]*\)|'
un
$

Le script suivant test si la saisie correspond à un nombre (positif ou négatif) et effectue la somme des nombres saisis.

$ nl ./sumnb.sh
     1  #!/bin/bash
     2  somme=0
     3  while :
     4  do
     5          echo -e "Saisir un nombre entier : \c"
     6          if read nombre
     7          then
     8                  if ( expr "$nombre" : '[0-9]*$' || expr "$nombre" : '-[0-9]*$' ) > /dev/null
     9                  then
    10                          somme=`expr $somme + $nombre`
    11                  else
    12                          echo "Saisie incorrecte"
    13                  fi
    14          else
    15                  break
    16          fi
    17  done
    18  echo -e "\nResultat : $somme"
    19  exit 0
$

Exécution du script.

$ ./sumnb.sh
Saisir un nombre entier : 45
Saisir un nombre entier : 69
Saisir un nombre entier : er
Saisie incorrecte
Saisir un nombre entier : 9y
Saisie incorrecte
Saisir un nombre entier : 2
Saisir un nombre entier : -89
Saisir un nombre entier : ^d                                # Saisie clavier ctrl+d
Resultat : 27
$

Etiquettes: 

La commande sed

sed

La commande sed (stream editor) est un éditeur de texte non intéractif. Elle permet d'automatiser le traitement de fichiers texte.

Etiquettes: 

Utilisation de la commande sed

Syntaxe de base :

sed [-n] action [fichier1 fichier2 ... fichierx]
sed [-n] -e action1 [-e action2 ...] [fichier1 fichier2 ... fichierx]
sed -f script-sed [fichier1 fichier2 ... fichierx]

Les actions spécifiées sont exécutées sur chaque ligne du ou des fichiers. Le résultat du traitement est affiché sur la sortie standard. Si plusieurs actions sont spécifiées sur la ligne de commande, chacune doit être précédée de l'option -e.

La commande sed ne modifie pas le fichier d'origine sauf si elle est exécutée avec l'option -i.

Syntaxe d'une action :

[adresse[,adresse]] commande [arguments]

Une action est syntaxiquement composée de :

  • Une partie adresse qui permet de spécifier sur quelles lignes doit s'effectuer la commande.
  • La commande à exécuter.
  • Les arguments de la commande.

Syntaxe d'une adresse :

Type d'adresse Lignes traitées
Aucune adresse Toutes les lignes
Adresses de type 1
n Ligne n.
$ Dernière ligne.
/ERb/ Lignes correspondant à l'expression régulière
Adresses de type 2
n1, n2 Ligne n1 jusqu'à ligne n2.
/ERb1/,/ERb2/ La première ligne traitée sera la première trouvée correspondant à ERb1. Le traitement se poursuivra sur toutes les lignes jusqu'à ce que sed rencontre une ligne correspondant à ERb2. Cette dernière sera également traitée.

Syntaxe de la commande :

Commande Argument Type d'adresse supportée (maximum) Signification
d Aucun 2 Ne pas afficher les lignes spécifiées (delete).
p Aucun 2 Afficher les lignes spécifiées (print).
s /ERb/remplacement/[g] 2 Effectuer une substitution sur les lignes spécifiées (substitute). Le caractère optionnel "g" indique si la substitution doit être globale sur les lignes.
w fichier 2 Ecrire les lignes spécifiées dans un fichier (write).
= Aucun 1 Afficher le numéro de la ligne spécifiée.
Dans les commandes ci-dessous, l'antislash rend le caractère "saut de ligne" invisible, ce qui permet de pouvoir spécifier plusieurs lignes de texte. Le dernier saut de ligne n'est pas masqué et représente la fin de la commande.
a\

texte\[entrée]
texte\[entrée]
texte [entrée]

1 Ajouter les lignes de texte après chaque ligne spécifiée (add).
i\ texte\[entrée]
texte\[entrée]
texte [entrée]
1 Insérer les lignes de texte avant chaque ligne spécifiée (insert).
c\ texte\[entrée]
texte\[entrée]
texte [entrée]
2 Remplacer les ligne spécifiées par les lignes de texte (change).
Négation de la commande
!commande La commande s'exécutera sur toutes les lignes sauf celles spécifiées dans l'adresse.

 

Exemples

Utilisation de sed en ligne de commande

Exemples avec le fichier depts2012.txt (téléchargeable sur le site de l'INSEE à cette adresse : http://isbeta.fr/f8c21)

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
...
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

La commande d (delete)

La commande d permet de ne pas afficher à l'écran les lignes sélectionnées par la partie adresse.

Exemple :

Ne pas afficher les lignes contenant les caractères de a à h (en minuscule)

$ sed '/[a-h]/d' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
73      46      46042   2       LOT     Lot
$

La commande p (print)

La commande p permet d'afficher à l'écran les lignes sélectionnées par la partie adresse. Par défauf, sed affiche également tout le contenu du fichier. Pour modifier ce comportement, il faut utiliser l'option -n.

Exemple :

Afficher les lignes 1 à 4. Par défaut, sed affiche en plus tout le contenu du fichier. Les lignes demandées apparaissent donc en double.

$ sed '1,4p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
....
$

L'option -n permet de ne pas afficher le reste du fichier.

$ sed -n '1,4p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
$

Il est également possible d'afficher 1 ligne toutes les n lignes

$ sed -n '1~4p' depts2012.txt

Cette commande permet d'afficher à partir de la ligne 1 du fichier puis toutes les 4 lignes, c'est à dire les lignes 1, 5, 9, 13 etc etc

$ sed -n '0~4p' depts2012.txt

Cette commande permet d'afficher à partir de la ligne 4  du fichier puis toutes les 4 lignes, c'est à dire les lignes 4, 8, 12, 16 etc etc

Pour info, ce type d'adressage est valable pour les commandes delete, print, write

La commande suivante permet de rechercher un motif précis et d'afficher uniquement la ligne suivante.

$ sed -n '/ANSWER SECTION/{n;p}' <(dig a quennec.fr)
quennec.fr.             906     IN      A       51.159.70.99

La première partie '/ANSWER SECTION/' correspond au motif recherché.
Ensuite, '{n;p}', un bloc d'instruction qui est exécuté si le motif recherché est trouvé.
La command 'n' permet de sauter à la ligne suivante.
La commande 'p' permet d'afficher la ligne courante.

et si l'on souhaite afficher plusieurs lignes après le motif recherché

$ dig any quennec.fr | sed -n '/ANSWER SECTION/{:a ; n ; /^$/q ; p ; ba}'
quennec.fr.             3183    IN      A       51.159.70.99
quennec.fr.             21400   IN      NS      ns42.infomaniak.com.
quennec.fr.             21400   IN      NS      ns41.infomaniak.com.
quennec.fr.             21400   IN      SOA     ns41.infomaniak.com. hostmaster.infomaniak.ch. 2024091604 10800 3600 605800 86400
quennec.fr.             3400    IN      MX      5 mta-gw.infomaniak.ch.
quennec.fr.             3368    IN      TXT     "v=spf1 a mx ip4:99.162.140.135ip4:51.159.70.99 include:spf.infomaniak.ch -all"

Ce qui change vis à vis de la commande précédente:
Dans le bloc d'instruction '{:a ; n ; /^$/q ; p ; ba}', la première commande ':a' permet d'initialiser un label, la commande 'n' permet de passer à la ligne suivante (pas de changement), la commande '/^$/q' permet de mettre fin au traitement dès qu'une ligne vide est rencontrée, la commande 'p' permet d'afficher la ligne en cours de traitement (pas de changement), et enfin, la commande 'ba' permet de revenir au label 'a' précédement initialisé et de continuer le traitement sur la ligne suivante (à voir comme une boucle) etc etc ...

La commande w (write)

La commande w permet d'écrire dans un fichier les lignes sélectionnées par la partie adresse. Comme pour la commande p, sed affiche également tout le contenu du fichier à l'écran. Pour modifier ce comportement, il faut utiliser l'option -n.

Exemple :

Stocker dans un fichier "depts3x" toutes les villes dont le code postal commence par 3 et dans le fichier "depts6x" toutes les villes dont le code postal commence par 6.

$ sed -n -e '/3[0-9]\{4\}/w depts3x' -e '/6[0-9]\{4\}/w depts6x' depts2012.txt
$ cat depts3x
91      30      30189   2       GARD    Gard
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
91      34      34172   5       HERAULT Hérault
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
$ cat depts6x
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
$

Négation d'une commande (!)

Le caractère ! placé devant une commande permet d'exécuter cette dernière sur toutes les lignes sauf sur celles correspondant à la partie adresse.

Exemple :

Ne pas afficher les lignes contenant les caractères de a à h (en minuscule)

$ sed -n '/[a-h]/!p' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
73      46      46042   2       LOT     Lot
$

La commande s (substitution)

La commande s permet de substituer une chaine de caractères par une autre sur les lignes sélectionnées par la partie adresse.

Pour substituer des noms de fichiers avec la commande sed, utiliser le pipe '|' comme séparateur à cause du slash '/' dans les noms de fichiers.

$ sed -i "s|/fichier1|/fichier2|g" maListeDeFichiers

Premier exemple :

Remplacer toutes les chaines contenant '-et-' ou '-ET-' par ' & '

$ sed -n '/-et-/p' depts2012.txt
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
$

$ sed -e 's/-et-/ \& /g' -e 's/-ET-/ \& /g' depts2012.txt | sed -n '/ \& /p'
24      28      28085   1       EURE & LOIR     Eure & Loir
53      35      35238   1       ILLE & VILAINE  Ille & Vilaine
24      37      37261   1       INDRE & LOIRE   Indre & Loire
24      41      41018   0       LOIR & CHER     Loir & Cher
72      47      47001   0       LOT & GARONNE   Lot & Garonne
52      49      49007   0       MAINE & LOIRE   Maine & Loire
41      54      54395   0       MEURTHE & MOSELLE       Meurthe & Moselle
26      71      71270   0       SAONE & LOIRE   Saône & Loire
11      77      77288   0       SEINE & MARNE   Seine & Marne
73      82      82121   0       TARN & GARONNE  Tarn & Garonne
$

Second exemple :

Travailler sur le contenu d'une variable.

$ arg=fic1,fic2,fic3
$ echo $arg
fic1,fic2,fic3
$ echo $arg | sed 's/,/ /g'        # On remplace les virgules par des espaces
fic1 fic2 fic3
$ liste_arg=$(echo $arg | sed 's/,/ /g')
$ echo $liste_arg
fic1 fic2 fic3
$ for argt in $liste_arg
> do
> echo $argt
> done
fic1
fic2
fic3
$

Troisième exemple :

Le caractère "&" utilisée dans la partie remplacement représente la chaine correspondant à l'expression régulière.

$ tail depts2012.txt
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$ tail depts2012.txt | sed 's/.*/|&|/'
|11     91      91228   5       ESSONNE Essonne|
|11     92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine|
|11     93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis|
|11     94      94028   2       VAL-DE-MARNE    Val-de-Marne|
|11     95      95500   2       VAL-D'OISE      Val-d'Oise|
|01     971     97105   3       GUADELOUPE      Guadeloupe|
|02     972     97209   3       MARTINIQUE      Martinique|
|03     973     97302   3       GUYANE  Guyane|
|04     974     97411   0       LA REUNION      La Réunion|
|06     976     97608   0       MAYOTTE Mayotte|
$

Toutes les chaines ont été encadrées par des pipes "|"

Quatrième exemple :

Récupérer les codes postaux et les noms des départements (en majuscule) afin d'effectuer une mise en forme particulière.
Utilisation de la mémorisation grâce aux caractères \( \) afin de les réafficher avec \1 et \2.

$ tail depts2012.txt
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte

$ tail depts2012.txt | sed "s/^[0-9]\{2\}[ \t]*[0-9]\{2,3\}[ \t]*\([0-9]\{5\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/"
Code postal : 91228     Departement : ESSONNE
Code postal : 92050     Departement : HAUTS-DE-SEINE
Code postal : 93008     Departement : SEINE-SAINT-DENIS
Code postal : 94028     Departement : VAL-DE-MARNE
Code postal : 95500     Departement : VAL-D'OISE
Code postal : 97105     Departement : GUADELOUPE
Code postal : 97209     Departement : MARTINIQUE
Code postal : 97302     Departement : GUYANE
Code postal : 97411     Departement : LA REUNION
Code postal : 97608     Departement : MAYOTTE
$

Cinquième exemple :

Exemples détaillés de l'option g dans la commande de substitution s.

$ arg=val1,val2,val3,val4
$ echo $arg
val1,val2,val3,val4

Remplacement de la globalité des virgules par des espaces grâce à l'option g.

$ echo $arg | sed 's/,/ /g'
val1 val2 val3 val4

Sans l'option g, seule la première virgule rencontrée est remplacée par un espace.

$ echo $arg | sed 's/,/ /'
val1 val2,val3,val4

Le chiffre 1 permet le remplacement de la première virgule rencontrée. Identique à la commande précédente.

$ echo $arg | sed 's/,/ /1'
val1 val2,val3,val4

Le chiffre 3 permet le remplacement de la troisième virgule rencontrée.

$ echo $arg | sed 's/,/ /3'
val1,val2,val3 val4

 

 

 

 

 

 

 

 

 

 

 

Etiquettes: 

Script sed

Un script sed est un fichier texte contenant une suite d'actions sed qui seront exécutées sur les lignes d'un ou plusieurs fichiers passés en paramètre ou sur l'entrée standard de la commande sed.

Toutes les actions sont évaluées et appliquées sur toutes les lignes du ou des fichiers.

Exemple :

$ nl script.sed
     1  1d
     2  2i\
     3  ------------------------\
     4  Liste des departements :\
     5  ------------------------
     6  s/^[0-9]\{2\}[ \t]*[0-9]\{2,3\}[ \t]*\([0-9]\{5\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/
     7  s/^[0-9]\{2\}[ \t]*[0-9][A-B][ \t]*\([0-9][A-B][0-9]\{3\}\)[ \t]*[0-9][ \t]*\([-A-Z_\. ']\{1,\}\)[ \t]*.\{1,\}$/Code postal : \1\tDepartement : \2/
     8  $a\
     9  ---------------\
    10  Fin de la liste\
    11  ---------------
$

  • Ligne 1 : Suppression de la première ligne du fichier.
  • Ligne 2 : Affichage du contenu des lignes 3 à 5 du script avant la ligne 2 du fichier.
  • Ligne 6 : Mise en forme des données du fichier
  • Ligne 7 : Identique à la ligne 6 mais avec la prise en compte des départements de La Corse
  • Ligne 8 : Affichage du contenu des lignes 9 à 11 du script après la dernière ligne ($) du fichier.

$ sed -f script.sed depts2012.txt
------------------------
Liste des departements :
------------------------
Code postal : 01053     Departement : AIN
Code postal : 02408     Departement : AISNE
Code postal : 03190     Departement : ALLIER
Code postal : 04070     Departement : ALPES-DE-HAUTE-PROVENCE
Code postal : 05061     Departement : HAUTES-ALPES
Code postal : 06088     Departement : ALPES-MARITIMES
Code postal : 07186     Departement : ARDECHE
Code postal : 08105     Departement : ARDENNES
Code postal : 09122     Departement : ARIEGE
Code postal : 10387     Departement : AUBE
Code postal : 11069     Departement : AUDE
Code postal : 12202     Departement : AVEYRON
Code postal : 13055     Departement : BOUCHES-DU-RHONE
Code postal : 14118     Departement : CALVADOS
Code postal : 15014     Departement : CANTAL
Code postal : 16015     Departement : CHARENTE
Code postal : 17300     Departement : CHARENTE-MARITIME
Code postal : 18033     Departement : CHER
Code postal : 19272     Departement : CORREZE
Code postal : 2A004     Departement : CORSE-DU-SUD
Code postal : 2B033     Departement : HAUTE-CORSE
Code postal : 21231     Departement : COTE-D'OR
Code postal : 22278     Departement : COTES-D'ARMOR
Code postal : 23096     Departement : CREUSE
Code postal : 24322     Departement : DORDOGNE
Code postal : 25056     Departement : DOUBS
Code postal : 26362     Departement : DROME
Code postal : 27229     Departement : EURE
Code postal : 28085     Departement : EURE-ET-LOIR
Code postal : 29232     Departement : FINISTERE
Code postal : 30189     Departement : GARD
Code postal : 31555     Departement : HAUTE-GARONNE
Code postal : 32013     Departement : GERS
Code postal : 33063     Departement : GIRONDE
Code postal : 34172     Departement : HERAULT
Code postal : 35238     Departement : ILLE-ET-VILAINE
Code postal : 36044     Departement : INDRE
Code postal : 37261     Departement : INDRE-ET-LOIRE
Code postal : 38185     Departement : ISERE
Code postal : 39300     Departement : JURA
Code postal : 40192     Departement : LANDES
Code postal : 41018     Departement : LOIR-ET-CHER
Code postal : 42218     Departement : LOIRE
Code postal : 43157     Departement : HAUTE-LOIRE
Code postal : 44109     Departement : LOIRE-ATLANTIQUE
Code postal : 45234     Departement : LOIRET
Code postal : 46042     Departement : LOT
Code postal : 47001     Departement : LOT-ET-GARONNE
Code postal : 48095     Departement : LOZERE
Code postal : 49007     Departement : MAINE-ET-LOIRE
Code postal : 50502     Departement : MANCHE
Code postal : 51108     Departement : MARNE
Code postal : 52121     Departement : HAUTE-MARNE
Code postal : 53130     Departement : MAYENNE
Code postal : 54395     Departement : MEURTHE-ET-MOSELLE
Code postal : 55029     Departement : MEUSE
Code postal : 56260     Departement : MORBIHAN
Code postal : 57463     Departement : MOSELLE
Code postal : 58194     Departement : NIEVRE
Code postal : 59350     Departement : NORD
Code postal : 60057     Departement : OISE
Code postal : 61001     Departement : ORNE
Code postal : 62041     Departement : PAS-DE-CALAIS
Code postal : 63113     Departement : PUY-DE-DOME
Code postal : 64445     Departement : PYRENEES-ATLANTIQUES
Code postal : 65440     Departement : HAUTES-PYRENEES
Code postal : 66136     Departement : PYRENEES-ORIENTALES
Code postal : 67482     Departement : BAS-RHIN
Code postal : 68066     Departement : HAUT-RHIN
Code postal : 69123     Departement : RHONE
Code postal : 70550     Departement : HAUTE-SAONE
Code postal : 71270     Departement : SAONE-ET-LOIRE
Code postal : 72181     Departement : SARTHE
Code postal : 73065     Departement : SAVOIE
Code postal : 74010     Departement : HAUTE-SAVOIE
Code postal : 75056     Departement : PARIS
Code postal : 76540     Departement : SEINE-MARITIME
Code postal : 77288     Departement : SEINE-ET-MARNE
Code postal : 78646     Departement : YVELINES
Code postal : 79191     Departement : DEUX-SEVRES
Code postal : 80021     Departement : SOMME
Code postal : 81004     Departement : TARN
Code postal : 82121     Departement : TARN-ET-GARONNE
Code postal : 83137     Departement : VAR
Code postal : 84007     Departement : VAUCLUSE
Code postal : 85191     Departement : VENDEE
Code postal : 86194     Departement : VIENNE
Code postal : 87085     Departement : HAUTE-VIENNE
Code postal : 88160     Departement : VOSGES
Code postal : 89024     Departement : YONNE
Code postal : 90010     Departement : TERRITOIRE DE BELFORT
Code postal : 91228     Departement : ESSONNE
Code postal : 92050     Departement : HAUTS-DE-SEINE
Code postal : 93008     Departement : SEINE-SAINT-DENIS
Code postal : 94028     Departement : VAL-DE-MARNE
Code postal : 95500     Departement : VAL-D'OISE
Code postal : 97105     Departement : GUADELOUPE
Code postal : 97209     Departement : MARTINIQUE
Code postal : 97302     Departement : GUYANE
Code postal : 97411     Departement : LA REUNION
Code postal : 97608     Departement : MAYOTTE
---------------
Fin de la liste
---------------
$

Etiquettes: 

Le langage de programmation awk

Etiquettes: 

Principe

 Extrait de Wikipédia :

Awk est le plus souvent utilisé pour la production de fichiers plats aux spécifications particulières (échanges entre différents systèmes d'informations hétérogènes). Il est aussi utilisé comme "parser" de fichiers XML ou de fichiers textes pour générer des commandes SQL à partir des données extraites. Il peut être utilisé aussi pour des opérations de calculs complexes et mise en forme de données brutes pour faire des tableaux statistiques.

On distingue awk, la commande originale, du new awk (nawk), arrivée un peu plus tard sur le marché. Les implémentations GNU de awk, sont en fait des new awk. On trouve en général la commande awk dans /usr/bin sous Unix. Certains systèmes GNU/Linux le mettent dans /bin. En général, elle est dans la variable d'environnement PATH. Cependant, on peut faire des scripts en awk et le shebang (#!/usr/bin/awk -f) devient faux. Le script est donc inutilisable si le binaire n’est pas là où on l’attend.

Il agit comme un filtre programmable prenant une série de lignes en entrée (sous forme de fichiers ou directement via l'entrée standard) et écrivant sur la sortie standard, qui peut être redirigée vers un autre fichier ou programme. Un programme Awk est composé de trois blocs distincts utilisables ou non pour le traitement d'un fichier (prétraitement, traitement, posttraitement). Awk lit sur l'entrée ligne par ligne, puis sélectionne (ou non) les lignes à traiter par des expressions rationnelles (et éventuellement des numéros de lignes). Une fois la ligne sélectionnée, elle est découpée en champs selon un séparateur d'entrée indiqué dans le programme awk par le symbole FS (qui par défaut correspond au caractère espace ou tabulation). Puis les différents champs sont disponibles dans des variables : $1 (premier champ), $2 (deuxième champ), $3 (troisième champ), ..., $NF (dernier champ).

« awk » est aussi l'extension de nom de fichier utilisée pour les scripts écrits dans ce langage.

Etiquettes: 

Syntaxe

awk [-F] '{action-awk}' [ fichier1 fichier2 ..... fichiern ]

awk [-F] -f script-awk [ fichier1 fichier2 ..... fichiern ]

La commande awk prend en argument la liste des fichiers à traiter. En l'absence de noms de fichiers sur la ligne de commande, awk travaille sur les données arrivant sur son entrée standard. Cette commande peut donc être placée derrière un tube de communication.

L'option "-F" permet d'initialiser, si besoin, la variable "FS" (Field Separator) correspondant au caractère séparateur de champ.

Etiquettes: 

Variables spéciales

Variables prédéfinies au lancement de awk

Le tableau suivant présente les principales variables internes du langage awk présentes en mémoire dès le lancement de la commande. La valeur de ces variables peut éventuellement être modifiée en fonction de la structure des données à traiter.

Nom de la variable Valeur par défaut Rôle de la variable
RS Newline (\n) Record Separator : Caractère séparateur d'enregistrement (lignes).
FS Suite d'espaces et/ou de tabulations Field Separator : Caractères séparateurs de champs.
OFS Espace Output Field Separator : Séparateur de champ utilisé pour l'affichage.
ORS Newline (\n) Output Record Separator : Caractère séparateur d'enregistrement en sortie.
ARGV - Tableau initialisé avec les arguments de la ligne de commande (options et nom du script awk exclus).
ARGC - Nombre d'éléments contenus dans le tableau ARGV.
ENVIRON Variables d'environnement exportées par le shell. Tableau contenant les variables d'environnement exportées par le shell.
CONVFMT %.6g Format de conversion des nombres en String.
OFMT %.6g Format de sortie des nombres.
SUBSEP \034 Caractère de séparation pour les routines internes des tableaux.

Par défaut, un enregistrement correspond donc à une ligne (suite de caractères terminée par "\n").

Lorsque la variable FS est initialisée avec un minimum de 2 caractères, cette valeur est interprétée comme une expression régulière.

Variables initialisées lors du traitement d'une ligne

Les enregistrements sont traités successivement. L'enregistrement courant est automatiquement découpé en champs et un certain nombre de variables internes awk sont initialisées. Le tableau suivant donne la liste des principales variables.

Nom de la variable Valeur de la variable
$0 Valeur de l'enregistrement courant
NF Number of Field : Nombre de champs de l'enregistrement courant.
$1, $2, ... $NF $1 contient la valeur du 1er champ, $2 la valeur du 2ème champ etc etc ... et $NF la valeur du dernier champ (NF est remplacé par sa valeur).
NR Number : Indice de l'enregistrement courant (NR vaut 1 quand la 1ère ligne est en cours de traitement, puis s'incrémente dès que awk change d'enregistrement).
FNR File Number : Indice de l'enregistrement courant relatif au fichier en cours de traitement.
FILENAME Nom du fichier en cours de traitement.
RLENGTH Longueur du string trouvé par la fonction match()
RSTART Première position du string trouvé par la fonction match()

Contrairement aux variables du shell, le symbole "$" des variables awk $1, $2 etc etc... fait partie du nom des variables.

Exemples simples

Premier exemple :

Ici, awk travaille sur le résultat de la commande ps -ef. La partie en rouge représente l'action que awk doit exécuter sur chaque ligne. Les simples quotes sont indispensables pour empêcher le shell d'interpréter les caractères destinés à la commande awk. Les instructions doivent être placées entre accolades. La fonction intégrée print va afficher à l'écran les champs 1 et 8 de chaque ligne.

$ ps -ef | awk '{print $1,$8}'
UID CMD
root init
...
www-data /usr/sbin/apache2
www-data /usr/sbin/apache2
root vzctl:
root -bash
postfix pickup
root ps
root awk
root /usr/lib/postfix/master
postfix qmgr
$

Deuxième exemple :

La fonction print peut également recevoir des chaines de caractères en argument.

$ ps -ef | awk '{print "User : " , $1, "\tCommande : " , $8}'
User :  UID     Commande :  CMD
User :  root    Commande :  init
User :  root    Commande :  [kthreadd/113]
User :  root    Commande :  [khelper/113]
User :  root    Commande :  [init-logger]
...
User :  root    Commande :  vzctl:
User :  root    Commande :  -bash
User :  root    Commande :  ps
User :  root    Commande :  awk
User :  root    Commande :  /usr/lib/postfix/master
$

\t représente le caractère tabulation.

Troisième exemple :

Modification du séparateur de champ "FS" grâce à l'option "-F"

$ cat /etc/passwd | awk -F : '{print $1,$7}'
root /bin/bash
...
nobody /bin/sh
libuuid /bin/sh
postfix /bin/false
sshd /usr/sbin/nologin
mysql /bin/false
$

Quatrième exemple :

$ cat /etc/passwd | awk -F : '{print "User : " , $1 , "\tShell : " , $7}'
User :  root    Shell :  /bin/bash
User :  daemon  Shell :  /bin/sh
User :  bin     Shell :  /bin/sh
User :  sys     Shell :  /bin/sh
User :  sync    Shell :  /bin/sync
User :  games   Shell :  /bin/sh
User :  man     Shell :  /bin/sh
User :  lp      Shell :  /bin/sh
User :  mail    Shell :  /bin/sh
User :  news    Shell :  /bin/sh
User :  uucp    Shell :  /bin/sh
User :  proxy   Shell :  /bin/sh
User :  www-data        Shell :  /bin/sh
User :  backup  Shell :  /bin/sh
User :  list    Shell :  /bin/sh
User :  irc     Shell :  /bin/sh
User :  gnats   Shell :  /bin/sh
User :  nobody  Shell :  /bin/sh
User :  libuuid         Shell :  /bin/sh
User :  postfix         Shell :  /bin/false
User :  sshd    Shell :  /usr/sbin/nologin
User :  mysql   Shell :  /bin/false
$

Si la fonction print ne reçoit pas d'argument, elle affiche $0.

$ cat /etc/passwd | awk -F : '{print}'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Etiquettes: 

Critères de sélection

Il est possible de sélectionner les enregistrements sur lesquels l'action doit être exécutée.

Syntaxe :

awk [-F] 'critère {action-awk}' [fichier1 fichier2 ... fichiern]

Le critère de sélection peut s'exprimer de différentes manières.

Expressions régulières

Les enregistrements à traiter peuvent être sélectionnés en utilisant les expressions régulières étendues (ERe).

Attention, pour utiliser les quantificateurs {x,y} d'une ERe, il faut utiliser awk avec l'option --posix

Premier exemple :

Afficher les lignes du fichier /etc/passwd contenant /bin/false

$ awk -F':' '/\/bin\/false/ {print $0}' /etc/passwd
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Par défaut, le critère est mis en correspondance avec $0.

Il est possible de mettre un champ particulier en correspondance avec une expression régulière. Dans ce cas, il faut utiliser l'opérateur de concordance "~" ou de non-concordance "!~".

Deuxième exemple :

Afficher les lignes du fichier /etc/passwd dont le 6ème champ commence par /usr.

$ awk -F':' '$6 ~ /^\/usr/ {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
$

Et à l'inverse

$ awk -F':' '$6 !~ /^\/usr/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
...
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Tests logiques

Le critère peut être une expression d'opérateurs et renvoyant la valeur de vérité vrai ou faux.

Opérateurs de tests courants

Opérateur Signification
< Inférieur
> Supérieur
<= Inférieur ou égal
>= Supérieur ou égal
== Test d'égalité
!= Test d'inégalité
~ Correspondance avec une expression régulière
!~ Non-correspondance avec une expression régulière
! Négation
&& Et logique
|| Ou logique
(expression) Regroupement

Exemple :

Afficher les champs 1 et 7 de la ligne 4 et 8 du fichier /etc/passwd

$ awk -F':' 'NR == 4 || NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
lp ==> /bin/sh
$

Le chiffre 0 et la chaîne vide sont des valeurs fausses. Toute autre valeur est vraie.

Afficher uniquement les lignes impaires du fichier /etc/passwd.

Le résultat de l'opération NR%2 représentant le reste de la division par 2 du numéro de ligne en cours de traitement, cela permet d'afficher uniquement les lignes impaires du fichier car seulement les nombres impairs renvoient un reste de division différent de 0 (donc vrai). Equivalent à NR%2!=0

$ nl /etc/passwd
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     3  bin:x:2:2:bin:/bin:/bin/sh
     4  sys:x:3:3:sys:/dev:/bin/sh
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/bin/sh
     7  man:x:6:12:man:/var/cache/man:/bin/sh
     8  lp:x:7:7:lp:/var/spool/lpd:/bin/sh
     9  mail:x:8:8:mail:/var/mail:/bin/sh
    10  news:x:9:9:news:/var/spool/news:/bin/sh
    11  uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
    12  proxy:x:13:13:proxy:/bin:/bin/sh
    13  www-data:x:33:33:www-data:/var/www:/bin/sh
    14  backup:x:34:34:backup:/var/backups:/bin/sh
    15  list:x:38:38:Mailing List Manager:/var/list:/bin/sh
    16  irc:x:39:39:ircd:/var/run/ircd:/bin/sh
    17  gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
    18  nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
    19  libuuid:x:100:101::/var/lib/libuuid:/bin/sh
    20  postfix:x:101:104::/var/spool/postfix:/bin/false
    21  sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
    22  mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

$ awk -F':' 'NR%2 {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:2:2:bin:/bin:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
$

A l'inverse, afficher uniquement les lignes paires.

$ awk -F':' 'NR%2==0 {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Afficher les champs 1 et 7 de la ligne 4 à 8 du fichier /etc/passwd

$ awk -F':' 'NR == 4 , NR == 8 {print $1,"==>",$7}' /etc/passwd
sys ==> /bin/sh
sync ==> /bin/sync
games ==> /bin/sh
man ==> /bin/sh
lp ==> /bin/sh
$

Etiquettes: 

Structure d'un script awk

Lorsqu'il y a un certain nombre d'actions à réaliser sur les données, il est plus confortable d'écrire un script awk. Un script awk peut contenir une section BEGIN, une section END, et 0 à x sections intermédiaires. Toute section est facultative.

BEGIN

La section BEGIN est exécutée avant le traitement du premier enregistrement des données. Elle est utilisée essentiellement pour initialiser le contexte d'exécution.

Sections intermédiaires

Il peut y avoir plusieurs sections intermédiaires qui seront exécutées sur chaque enregistrement.

END

La section END est exécutée après le traitement du dernier enregistrement des données. Elle est utilisée pour exploiter les résultats issus du traitement des données.

Commentaires

Un commentaire commence par le caractère "#" et se termine au caractère "\n" (fin de la ligne).

Variables

Des variables personnelles peuvent être créées. Une variable est définie dès qu'elle est initialisée et n'a pas besoin d'être typée. L'utilisation d'une variable qui n'a jamais été définie a pour valeur 0 dans un contexte numérique et chaine vide dans un contexte de chaine.

Exemple :

$ nl script1.awk
     1  # Section BEGIN
     2  BEGIN {
     3          print "Section BEGIN"
     4          nb_0=0
     5          nb_1=0
     6          nb_2=0
     7          nb_3=0
     8          nb_4=0
     9          nb_5=0
    10          nb_6=0
    11          nb_7=0
    12          nb_8=0
    13          nb_9=0
    14  }
    15  # Section intermediaire
    16  # Traitement des departements commancant par 0
    17  $2 ~ /^0/ {
    18          print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
    19          nb_0+=1
    20  }
    21  # Section intermediaire
    22  # Traitement des departements commancant par 1
    23  $2 ~ /^1/ {
    24          print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
    25          nb_1+=1
    26  }
    27  # Section intermediaire
    28  # Traitement des departements commancant par 2
    29  $2 ~ /^2/ {
    30          print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
    31          nb_2+=1
    32  }
    33  # Section intermediaire
    34  # Traitement des departements commancant par 3
    35  $2 ~ /^3/ {
    36          print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
    37          nb_3+=1
    38  }
    39  # Section intermediaire
    40  # Traitement des departements commancant par 4
    41  $2 ~ /^4/ {
    42          print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
    43          nb_4+=1
    44  }
    45  # Section intermediaire
    46  # Traitement des departements commancant par 5
    47  $2 ~ /^5/ {
    48          print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
    49          nb_5+=1
    50  }
    51  # Section intermediaire
    52  # Traitement des departements commancant par 6
    53  $2 ~ /^6/ {
    54          print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
    55          nb_6+=1
    56  }
    57  # Section intermediaire
    58  # Traitement des departements commancant par 7
    59  $2 ~ /^7/ {
    60          print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
    61          nb_7+=1
    62  }
    63  # Section intermediaire
    64  # Traitement des departements commancant par 8
    65  $2 ~ /^8/ {
    66          print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
    67          nb_8+=1
    68  }
    69  # Section intermediaire
    70  # Traitement des departements commancant par 9
    71  $2 ~ /^9/ {
    72          print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
    73          nb_9+=1
    74  }
    75  # Section END
    76  END {
    77          print "Section END"
    78          print "Nombre total de lignes : " , NR
    79          print "Nombre de departements commencant par 0 : " , nb_0
    80          print "Nombre de departements commencant par 1 : " , nb_1
    81          print "Nombre de departements commencant par 2 : " , nb_2
    82          print "Nombre de departements commencant par 3 : " , nb_3
    83          print "Nombre de departements commencant par 4 : " , nb_4
    84          print "Nombre de departements commencant par 5 : " , nb_5
    85          print "Nombre de departements commencant par 6 : " , nb_6
    86          print "Nombre de departements commencant par 7 : " , nb_7
    87          print "Nombre de departements commencant par 8 : " , nb_8
    88          print "Nombre de departements commencant par 9 : " , nb_9
    89  }
$

Section BEGIN

Initialisation des variables personnelles servant de compteur.

Sections intermédiaires

Exécution des traitements spécifiques en fonction du début du numéro des départements.

Section END

Affichage du nombre total de lignes traitées et du nombre de départements regroupés par dizaine du numéro de départements.

Exécution du script

$ awk -f script1.awk depts2012.txt
Section BEGIN
Departement commancant par 0 ==> CP :  01053 DEPT :  AIN
Departement commancant par 0 ==> CP :  02408 DEPT :  AISNE
Departement commancant par 0 ==> CP :  03190 DEPT :  ALLIER
Departement commancant par 0 ==> CP :  04070 DEPT :  ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP :  05061 DEPT :  HAUTES-ALPES
Departement commancant par 0 ==> CP :  06088 DEPT :  ALPES-MARITIMES
Departement commancant par 0 ==> CP :  07186 DEPT :  ARDECHE
Departement commancant par 0 ==> CP :  08105 DEPT :  ARDENNES
Departement commancant par 0 ==> CP :  09122 DEPT :  ARIEGE
Departement commancant par 1 ==> CP :  10387 DEPT :  AUBE
Departement commancant par 1 ==> CP :  11069 DEPT :  AUDE
Departement commancant par 1 ==> CP :  12202 DEPT :  AVEYRON
Departement commancant par 1 ==> CP :  13055 DEPT :  BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP :  14118 DEPT :  CALVADOS
Departement commancant par 1 ==> CP :  15014 DEPT :  CANTAL
Departement commancant par 1 ==> CP :  16015 DEPT :  CHARENTE
Departement commancant par 1 ==> CP :  17300 DEPT :  CHARENTE-MARITIME
Departement commancant par 1 ==> CP :  18033 DEPT :  CHER
Departement commancant par 1 ==> CP :  19272 DEPT :  CORREZE
Departement commancant par 2 ==> CP :  2A004 DEPT :  CORSE-DU-SUD
Departement commancant par 2 ==> CP :  2B033 DEPT :  HAUTE-CORSE
Departement commancant par 2 ==> CP :  21231 DEPT :  COTE-D'OR
Departement commancant par 2 ==> CP :  22278 DEPT :  COTES-D'ARMOR
Departement commancant par 2 ==> CP :  23096 DEPT :  CREUSE
Departement commancant par 2 ==> CP :  24322 DEPT :  DORDOGNE
Departement commancant par 2 ==> CP :  25056 DEPT :  DOUBS
Departement commancant par 2 ==> CP :  26362 DEPT :  DROME
Departement commancant par 2 ==> CP :  27229 DEPT :  EURE
Departement commancant par 2 ==> CP :  28085 DEPT :  EURE-ET-LOIR
Departement commancant par 2 ==> CP :  29232 DEPT :  FINISTERE
Departement commancant par 3 ==> CP :  30189 DEPT :  GARD
Departement commancant par 3 ==> CP :  31555 DEPT :  HAUTE-GARONNE
Departement commancant par 3 ==> CP :  32013 DEPT :  GERS
Departement commancant par 3 ==> CP :  33063 DEPT :  GIRONDE
Departement commancant par 3 ==> CP :  34172 DEPT :  HERAULT
Departement commancant par 3 ==> CP :  35238 DEPT :  ILLE-ET-VILAINE
Departement commancant par 3 ==> CP :  36044 DEPT :  INDRE
Departement commancant par 3 ==> CP :  37261 DEPT :  INDRE-ET-LOIRE
Departement commancant par 3 ==> CP :  38185 DEPT :  ISERE
Departement commancant par 3 ==> CP :  39300 DEPT :  JURA
Departement commancant par 4 ==> CP :  40192 DEPT :  LANDES
Departement commancant par 4 ==> CP :  41018 DEPT :  LOIR-ET-CHER
Departement commancant par 4 ==> CP :  42218 DEPT :  LOIRE
Departement commancant par 4 ==> CP :  43157 DEPT :  HAUTE-LOIRE
Departement commancant par 4 ==> CP :  44109 DEPT :  LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP :  45234 DEPT :  LOIRET
Departement commancant par 4 ==> CP :  46042 DEPT :  LOT
Departement commancant par 4 ==> CP :  47001 DEPT :  LOT-ET-GARONNE
Departement commancant par 4 ==> CP :  48095 DEPT :  LOZERE
Departement commancant par 4 ==> CP :  49007 DEPT :  MAINE-ET-LOIRE
Departement commancant par 5 ==> CP :  50502 DEPT :  MANCHE
Departement commancant par 5 ==> CP :  51108 DEPT :  MARNE
Departement commancant par 5 ==> CP :  52121 DEPT :  HAUTE-MARNE
Departement commancant par 5 ==> CP :  53130 DEPT :  MAYENNE
Departement commancant par 5 ==> CP :  54395 DEPT :  MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP :  55029 DEPT :  MEUSE
Departement commancant par 5 ==> CP :  56260 DEPT :  MORBIHAN
Departement commancant par 5 ==> CP :  57463 DEPT :  MOSELLE
Departement commancant par 5 ==> CP :  58194 DEPT :  NIEVRE
Departement commancant par 5 ==> CP :  59350 DEPT :  NORD
Departement commancant par 6 ==> CP :  60057 DEPT :  OISE
Departement commancant par 6 ==> CP :  61001 DEPT :  ORNE
Departement commancant par 6 ==> CP :  62041 DEPT :  PAS-DE-CALAIS
Departement commancant par 6 ==> CP :  63113 DEPT :  PUY-DE-DOME
Departement commancant par 6 ==> CP :  64445 DEPT :  PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP :  65440 DEPT :  HAUTES-PYRENEES
Departement commancant par 6 ==> CP :  66136 DEPT :  PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP :  67482 DEPT :  BAS-RHIN
Departement commancant par 6 ==> CP :  68066 DEPT :  HAUT-RHIN
Departement commancant par 6 ==> CP :  69123 DEPT :  RHONE
Departement commancant par 7 ==> CP :  70550 DEPT :  HAUTE-SAONE
Departement commancant par 7 ==> CP :  71270 DEPT :  SAONE-ET-LOIRE
Departement commancant par 7 ==> CP :  72181 DEPT :  SARTHE
Departement commancant par 7 ==> CP :  73065 DEPT :  SAVOIE
Departement commancant par 7 ==> CP :  74010 DEPT :  HAUTE-SAVOIE
Departement commancant par 7 ==> CP :  75056 DEPT :  PARIS
Departement commancant par 7 ==> CP :  76540 DEPT :  SEINE-MARITIME
Departement commancant par 7 ==> CP :  77288 DEPT :  SEINE-ET-MARNE
Departement commancant par 7 ==> CP :  78646 DEPT :  YVELINES
Departement commancant par 7 ==> CP :  79191 DEPT :  DEUX-SEVRES
Departement commancant par 8 ==> CP :  80021 DEPT :  SOMME
Departement commancant par 8 ==> CP :  81004 DEPT :  TARN
Departement commancant par 8 ==> CP :  82121 DEPT :  TARN-ET-GARONNE
Departement commancant par 8 ==> CP :  83137 DEPT :  VAR
Departement commancant par 8 ==> CP :  84007 DEPT :  VAUCLUSE
Departement commancant par 8 ==> CP :  85191 DEPT :  VENDEE
Departement commancant par 8 ==> CP :  86194 DEPT :  VIENNE
Departement commancant par 8 ==> CP :  87085 DEPT :  HAUTE-VIENNE
Departement commancant par 8 ==> CP :  88160 DEPT :  VOSGES
Departement commancant par 8 ==> CP :  89024 DEPT :  YONNE
Departement commancant par 9 ==> CP :  90010 DEPT :  TERRITOIRE
Departement commancant par 9 ==> CP :  91228 DEPT :  ESSONNE
Departement commancant par 9 ==> CP :  92050 DEPT :  HAUTS-DE-SEINE
Departement commancant par 9 ==> CP :  93008 DEPT :  SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP :  94028 DEPT :  VAL-DE-MARNE
Departement commancant par 9 ==> CP :  95500 DEPT :  VAL-D'OISE
Departement commancant par 9 ==> CP :  97105 DEPT :  GUADELOUPE
Departement commancant par 9 ==> CP :  97209 DEPT :  MARTINIQUE
Departement commancant par 9 ==> CP :  97302 DEPT :  GUYANE
Departement commancant par 9 ==> CP :  97411 DEPT :  LA
Departement commancant par 9 ==> CP :  97608 DEPT :  MAYOTTE
Section END
Nombre total de lignes :  102
Nombre de departements commencant par 0 :  9
Nombre de departements commencant par 1 :  10
Nombre de departements commencant par 2 :  11
Nombre de departements commencant par 3 :  10
Nombre de departements commencant par 4 :  10
Nombre de departements commencant par 5 :  10
Nombre de departements commencant par 6 :  10
Nombre de departements commencant par 7 :  10
Nombre de departements commencant par 8 :  10
Nombre de departements commencant par 9 :  11
$

Etiquettes: 

Opérateurs

Liste des opérateurs disponible dans awk.

Opérateurs arithmétiques

Opérateur Arité Signification
+ Binaire Addition
- Binaire Soustraction
* Binaire Multiplication
/ Binaire Division
% Binaire Modulo
^ Binaire Exponentiation
++ Unaire Incrémentation d'une variable d'une unité
-- Unaire Décrémentation d'une variable d'une unité
+= Binaire a+=b equivalent à a=a+b
-= Binaire a-=b équivalent à a=a-b
*= Binaire a*=b équivalent à a=a*b
/= Binaire a/=b équivalent à a=a/b
%= Binaire a%=b équivalent à a=a%b
^= Binaire a^=b équivalent à a=a^b

Opérateurs de tests

Opérateur Arité Signification
< Binaire Inférieur
> Binaire Supérieur
<= Binaire Inférieur ou égal
>= Binaire Supérieur ou égal
== Binaire Test d'égalité
!= Binaire Test d'inégalité
~ Binaire Correspondance avec une ERe
!~ Binaire Non correspondance avec une ERe

Opérateurs logiques

Opérateur Arité Signification
! Binaire Négation
&& Binaire ET logique
|| Binaire OU logique

Divers

Opérateur Arité Signification
= Binaire Affectation
e1 ? e2 : e3 Ternaire Le résultat est égal à e2 si e1 est vrai, égal à e3 si e1 est faux
e1 e2 Binaire Concaténation de e1 et e2

 

Etiquettes: 

La fonction printf

awk propose la fonction intégrée printf similaire à celle du langage C. Elle permet de formater les affichages.

printf ("chaine",field1,field2,field3...fieldx)

chaine représente la chaine affichée à l'écran. Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à la suite. Il doit y avoir autant de formats que d'expressions.

Formats souvent utilisés

Format Signification
%20s Affichage d'une chaine (string) sur 20 caractères (cadrage à droite par défaut)
%-20s Affichage d'une chaine (string) sur 20 caractères avec cadrage à gauche
%3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite)
%03d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) complété par des 0 à gauche
%-3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à gauche)
%+3d Affichage d'un entier/décimal sur 3 chiffres (cadrage à droite) avec affichage systématique du signe (un nombre négatif est toujours affiché avec son signe)
%10.2f Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales. (cadrage à droite)
%+010.2f Affichage d'un nombre flottant sur 10 chiffres dont 2 décimales, cadrage à droite, affichage systématique du signe, complétion par des 0 à gauche.

Exemple :

$ date | awk '{printf "%10s\n%-10s\n%d\n%15d\n%015d\n%-10d\n%+10d\n%10.2f\n%+010.2f\n" , $1 , $1 , $4 , $4 , $4 , $4 , $4 , 5.2 , 5.2}'
      mer.       # %10s
mer.       # %-10s
2012       # %d
           2012       # %15d
000000000002012       # %015d
2012       # %-10d
     +2012       # %+10d
      5.20       # %10.2f
+000005.20       # %+010.2f
$

Etiquettes: 

Redirections

Il est possible de rediriger les sorties du script vers un fichier ou vers une commande du système.

Syntaxe

instruction > "fichier" Au premier appel, le fichier est ouvert en mode "écrasement", puis écriture. Les écritures suivantes se font en mode "ajout"
instruction >> "fichier" Au premier appel, le fichier est ouvert en mode "ajout", puis écriture. Les écritures suivantes se font également en mode "ajout"
print[f] "..." | "commande" Le résultat de la fonction print/printf est transmise sur l'entrée standard de la commande système par l'intermédiaire d'un tube (pipe)

Exemple 1

Ouverture en mode "écrasement"

$ nl script2.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          print "Ligne 1" > fichier
     4          print "Ligne 2" > fichier
     5          print "Ligne 3" > fichier
     6          close(fichier)
     7  }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
 13:47:18 up 7 days,  5:28,  0 users,  load average: 0.00, 0.00, 0.00
$ awk -f script2.awk
$ cat /root/fichier1.txt
Ligne 1
Ligne 2
Ligne 3

$

Exemple 2

Ouverture en mode "ajout"

$ nl script3.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          print "Ligne 1" >> fichier
     4          print "Ligne 2" > fichier
     5          print "Ligne 3" > fichier
     6          close(fichier)
     7  }
$ uptime > /root/fichier1.txt
$ cat /root/fichier1.txt
 13:50:40 up 7 days,  5:31,  0 users,  load average: 0.00, 0.00, 0.00
$ awk -f script3.awk
$ cat /root/fichier1.txt
 13:50:40 up 7 days,  5:31,  0 users,  load average: 0.00, 0.00, 0.00
Ligne 1
Ligne 2
Ligne 3

$

Exemple 3

Ecriture dans un tube. Trier les lignes par users croissants.
La commande sort est exécutée une seule fois puis fermée dans la section END.

$ nl /etc/passwd
     1  root:x:0:0:root:/root:/bin/bash
     2  daemon:x:1:1:daemon:/usr/sbin:/bin/sh
     3  bin:x:2:2:bin:/bin:/bin/sh
     4  sys:x:3:3:sys:/dev:/bin/sh
     5  sync:x:4:65534:sync:/bin:/bin/sync
     6  games:x:5:60:games:/usr/games:/bin/sh
     7  man:x:6:12:man:/var/cache/man:/bin/sh
     8  lp:x:7:7:lp:/var/spool/lpd:/bin/sh
     9  mail:x:8:8:mail:/var/mail:/bin/sh
    10  news:x:9:9:news:/var/spool/news:/bin/sh
    11  uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
    12  proxy:x:13:13:proxy:/bin:/bin/sh
    13  www-data:x:33:33:www-data:/var/www:/bin/sh
    14  backup:x:34:34:backup:/var/backups:/bin/sh
    15  list:x:38:38:Mailing List Manager:/var/list:/bin/sh
    16  irc:x:39:39:ircd:/var/run/ircd:/bin/sh
    17  gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
    18  nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
    19  libuuid:x:100:101::/var/lib/libuuid:/bin/sh
    20  postfix:x:101:104::/var/spool/postfix:/bin/false
    21  sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
    22  mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$ awk -F':' '{print $0 | "sort"} END {close("sort")}' /etc/passwd
backup:x:34:34:backup:/var/backups:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
news:x:9:9:news:/var/spool/news:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
root:x:0:0:root:/root:/bin/bash
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
sys:x:3:3:sys:/dev:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
$

Etiquettes: 

Lecture de la ligne suivante : next

L'instruction next interrompt le traitement de la ligne courante et déclenche la lecture de la ligne suivante.

Exemple

Dans le script suivant, les instruction next permettent d'accélérer l'exécution en évitant le traitement de la ligne courante dans les sections suivantes celle où la ligne a été traitée.

$ nl script4.awk
     1  # Section BEGIN
     2  BEGIN {
     3          nb_0=0
     4          nb_1=0
     5          nb_2=0
     6          nb_3=0
     7          nb_4=0
     8          nb_5=0
     9          nb_6=0
    10          nb_7=0
    11          nb_8=0
    12          nb_9=0
    13  }
    14  # Section intermediaire
    15  # Traitement des departements commancant par 0
    16  $2 ~ /^0/ {
    17          print "Departement commancant par 0 ==> CP : " , $3 , "DEPT : " , $5
    18          nb_0+=1
    19          next
    20  }
    21  # Section intermediaire
    22  # Traitement des departements commancant par 1
    23  $2 ~ /^1/ {
    24          print "Departement commancant par 1 ==> CP : " , $3 , "DEPT : " , $5
    25          nb_1+=1
    26          next
    27  }
    28  # Section intermediaire
    29  # Traitement des departements commancant par 2
    30  $2 ~ /^2/ {
    31          print "Departement commancant par 2 ==> CP : " , $3 , "DEPT : " , $5
    32          nb_2+=1
    33          next
    34  }
    35  # Section intermediaire
    36  # Traitement des departements commancant par 3
    37  $2 ~ /^3/ {
    38          print "Departement commancant par 3 ==> CP : " , $3 , "DEPT : " , $5
    39          nb_3+=1
    40          next
    41  }
    42  # Section intermediaire
    43  # Traitement des departements commancant par 4
    44  $2 ~ /^4/ {
    45          print "Departement commancant par 4 ==> CP : " , $3 , "DEPT : " , $5
    46          nb_4+=1
    47          next
    48  }
    49  # Section intermediaire
    50  # Traitement des departements commancant par 5
    51  $2 ~ /^5/ {
    52          print "Departement commancant par 5 ==> CP : " , $3 , "DEPT : " , $5
    53          nb_5+=1
    54          next
    55  }
    56  # Section intermediaire
    57  # Traitement des departements commancant par 6
    58  $2 ~ /^6/ {
    59          print "Departement commancant par 6 ==> CP : " , $3 , "DEPT : " , $5
    60          nb_6+=1
    61          next
    62  }
    63  # Section intermediaire
    64  # Traitement des departements commancant par 7
    65  $2 ~ /^7/ {
    66          print "Departement commancant par 7 ==> CP : " , $3 , "DEPT : " , $5
    67          nb_7+=1
    68          next
    69  }
    70  # Section intermediaire
    71  # Traitement des departements commancant par 8
    72  $2 ~ /^8/ {
    73          print "Departement commancant par 8 ==> CP : " , $3 , "DEPT : " , $5
    74          nb_8+=1
    75          next
    76  }
    77  # Section intermediaire
    78  # Traitement des departements commancant par 9
    79  $2 ~ /^9/ {
    80          print "Departement commancant par 9 ==> CP : " , $3 , "DEPT : " , $5
    81          nb_9+=1
    82          next
    83  }
    84  # Section END
    85  END {
    86          print "Nombre total de lignes : " , NR
    87          print "Nombre de departements commencant par 0 : " , nb_0
    88          print "Nombre de departements commencant par 1 : " , nb_1
    89          print "Nombre de departements commencant par 2 : " , nb_2
    90          print "Nombre de departements commencant par 3 : " , nb_3
    91          print "Nombre de departements commencant par 4 : " , nb_4
    92          print "Nombre de departements commencant par 5 : " , nb_5
    93          print "Nombre de departements commencant par 6 : " , nb_6
    94          print "Nombre de departements commencant par 7 : " , nb_7
    95          print "Nombre de departements commencant par 8 : " , nb_8
    96          print "Nombre de departements commencant par 9 : " , nb_9
    97  }
$

$ awk -f script4.awk depts2012.txt
Departement commancant par 0 ==> CP :  01053 DEPT :  AIN
Departement commancant par 0 ==> CP :  02408 DEPT :  AISNE
Departement commancant par 0 ==> CP :  03190 DEPT :  ALLIER
Departement commancant par 0 ==> CP :  04070 DEPT :  ALPES-DE-HAUTE-PROVENCE
Departement commancant par 0 ==> CP :  05061 DEPT :  HAUTES-ALPES
Departement commancant par 0 ==> CP :  06088 DEPT :  ALPES-MARITIMES
Departement commancant par 0 ==> CP :  07186 DEPT :  ARDECHE
Departement commancant par 0 ==> CP :  08105 DEPT :  ARDENNES
Departement commancant par 0 ==> CP :  09122 DEPT :  ARIEGE
Departement commancant par 1 ==> CP :  10387 DEPT :  AUBE
Departement commancant par 1 ==> CP :  11069 DEPT :  AUDE
Departement commancant par 1 ==> CP :  12202 DEPT :  AVEYRON
Departement commancant par 1 ==> CP :  13055 DEPT :  BOUCHES-DU-RHONE
Departement commancant par 1 ==> CP :  14118 DEPT :  CALVADOS
Departement commancant par 1 ==> CP :  15014 DEPT :  CANTAL
Departement commancant par 1 ==> CP :  16015 DEPT :  CHARENTE
Departement commancant par 1 ==> CP :  17300 DEPT :  CHARENTE-MARITIME
Departement commancant par 1 ==> CP :  18033 DEPT :  CHER
Departement commancant par 1 ==> CP :  19272 DEPT :  CORREZE
Departement commancant par 2 ==> CP :  2A004 DEPT :  CORSE-DU-SUD
Departement commancant par 2 ==> CP :  2B033 DEPT :  HAUTE-CORSE
Departement commancant par 2 ==> CP :  21231 DEPT :  COTE-D'OR
Departement commancant par 2 ==> CP :  22278 DEPT :  COTES-D'ARMOR
Departement commancant par 2 ==> CP :  23096 DEPT :  CREUSE
Departement commancant par 2 ==> CP :  24322 DEPT :  DORDOGNE
Departement commancant par 2 ==> CP :  25056 DEPT :  DOUBS
Departement commancant par 2 ==> CP :  26362 DEPT :  DROME
Departement commancant par 2 ==> CP :  27229 DEPT :  EURE
Departement commancant par 2 ==> CP :  28085 DEPT :  EURE-ET-LOIR
Departement commancant par 2 ==> CP :  29232 DEPT :  FINISTERE
Departement commancant par 3 ==> CP :  30189 DEPT :  GARD
Departement commancant par 3 ==> CP :  31555 DEPT :  HAUTE-GARONNE
Departement commancant par 3 ==> CP :  32013 DEPT :  GERS
Departement commancant par 3 ==> CP :  33063 DEPT :  GIRONDE
Departement commancant par 3 ==> CP :  34172 DEPT :  HERAULT
Departement commancant par 3 ==> CP :  35238 DEPT :  ILLE-ET-VILAINE
Departement commancant par 3 ==> CP :  36044 DEPT :  INDRE
Departement commancant par 3 ==> CP :  37261 DEPT :  INDRE-ET-LOIRE
Departement commancant par 3 ==> CP :  38185 DEPT :  ISERE
Departement commancant par 3 ==> CP :  39300 DEPT :  JURA
Departement commancant par 4 ==> CP :  40192 DEPT :  LANDES
Departement commancant par 4 ==> CP :  41018 DEPT :  LOIR-ET-CHER
Departement commancant par 4 ==> CP :  42218 DEPT :  LOIRE
Departement commancant par 4 ==> CP :  43157 DEPT :  HAUTE-LOIRE
Departement commancant par 4 ==> CP :  44109 DEPT :  LOIRE-ATLANTIQUE
Departement commancant par 4 ==> CP :  45234 DEPT :  LOIRET
Departement commancant par 4 ==> CP :  46042 DEPT :  LOT
Departement commancant par 4 ==> CP :  47001 DEPT :  LOT-ET-GARONNE
Departement commancant par 4 ==> CP :  48095 DEPT :  LOZERE
Departement commancant par 4 ==> CP :  49007 DEPT :  MAINE-ET-LOIRE
Departement commancant par 5 ==> CP :  50502 DEPT :  MANCHE
Departement commancant par 5 ==> CP :  51108 DEPT :  MARNE
Departement commancant par 5 ==> CP :  52121 DEPT :  HAUTE-MARNE
Departement commancant par 5 ==> CP :  53130 DEPT :  MAYENNE
Departement commancant par 5 ==> CP :  54395 DEPT :  MEURTHE-ET-MOSELLE
Departement commancant par 5 ==> CP :  55029 DEPT :  MEUSE
Departement commancant par 5 ==> CP :  56260 DEPT :  MORBIHAN
Departement commancant par 5 ==> CP :  57463 DEPT :  MOSELLE
Departement commancant par 5 ==> CP :  58194 DEPT :  NIEVRE
Departement commancant par 5 ==> CP :  59350 DEPT :  NORD
Departement commancant par 6 ==> CP :  60057 DEPT :  OISE
Departement commancant par 6 ==> CP :  61001 DEPT :  ORNE
Departement commancant par 6 ==> CP :  62041 DEPT :  PAS-DE-CALAIS
Departement commancant par 6 ==> CP :  63113 DEPT :  PUY-DE-DOME
Departement commancant par 6 ==> CP :  64445 DEPT :  PYRENEES-ATLANTIQUES
Departement commancant par 6 ==> CP :  65440 DEPT :  HAUTES-PYRENEES
Departement commancant par 6 ==> CP :  66136 DEPT :  PYRENEES-ORIENTALES
Departement commancant par 6 ==> CP :  67482 DEPT :  BAS-RHIN
Departement commancant par 6 ==> CP :  68066 DEPT :  HAUT-RHIN
Departement commancant par 6 ==> CP :  69123 DEPT :  RHONE
Departement commancant par 7 ==> CP :  70550 DEPT :  HAUTE-SAONE
Departement commancant par 7 ==> CP :  71270 DEPT :  SAONE-ET-LOIRE
Departement commancant par 7 ==> CP :  72181 DEPT :  SARTHE
Departement commancant par 7 ==> CP :  73065 DEPT :  SAVOIE
Departement commancant par 7 ==> CP :  74010 DEPT :  HAUTE-SAVOIE
Departement commancant par 7 ==> CP :  75056 DEPT :  PARIS
Departement commancant par 7 ==> CP :  76540 DEPT :  SEINE-MARITIME
Departement commancant par 7 ==> CP :  77288 DEPT :  SEINE-ET-MARNE
Departement commancant par 7 ==> CP :  78646 DEPT :  YVELINES
Departement commancant par 7 ==> CP :  79191 DEPT :  DEUX-SEVRES
Departement commancant par 8 ==> CP :  80021 DEPT :  SOMME
Departement commancant par 8 ==> CP :  81004 DEPT :  TARN
Departement commancant par 8 ==> CP :  82121 DEPT :  TARN-ET-GARONNE
Departement commancant par 8 ==> CP :  83137 DEPT :  VAR
Departement commancant par 8 ==> CP :  84007 DEPT :  VAUCLUSE
Departement commancant par 8 ==> CP :  85191 DEPT :  VENDEE
Departement commancant par 8 ==> CP :  86194 DEPT :  VIENNE
Departement commancant par 8 ==> CP :  87085 DEPT :  HAUTE-VIENNE
Departement commancant par 8 ==> CP :  88160 DEPT :  VOSGES
Departement commancant par 8 ==> CP :  89024 DEPT :  YONNE
Departement commancant par 9 ==> CP :  90010 DEPT :  TERRITOIRE
Departement commancant par 9 ==> CP :  91228 DEPT :  ESSONNE
Departement commancant par 9 ==> CP :  92050 DEPT :  HAUTS-DE-SEINE
Departement commancant par 9 ==> CP :  93008 DEPT :  SEINE-SAINT-DENIS
Departement commancant par 9 ==> CP :  94028 DEPT :  VAL-DE-MARNE
Departement commancant par 9 ==> CP :  95500 DEPT :  VAL-D'OISE
Departement commancant par 9 ==> CP :  97105 DEPT :  GUADELOUPE
Departement commancant par 9 ==> CP :  97209 DEPT :  MARTINIQUE
Departement commancant par 9 ==> CP :  97302 DEPT :  GUYANE
Departement commancant par 9 ==> CP :  97411 DEPT :  LA
Departement commancant par 9 ==> CP :  97608 DEPT :  MAYOTTE
Nombre total de lignes :  102
Nombre de departements commencant par 0 :  9
Nombre de departements commencant par 1 :  10
Nombre de departements commencant par 2 :  11
Nombre de departements commencant par 3 :  10
Nombre de departements commencant par 4 :  10
Nombre de departements commencant par 5 :  10
Nombre de departements commencant par 6 :  10
Nombre de departements commencant par 7 :  10
Nombre de departements commencant par 8 :  10
Nombre de departements commencant par 9 :  11
$

Etiquettes: 

Structures de controle

Awk propose des structures de controle que l'on retrouve dans la plupart des langages de programmation. La syntaxe est héritée du langage C.

1 - if

La partie else est facultative.

Syntaxe

if (condition) {
     instruction
     ...
}
else {
     instruction
     ...
}

2 - for

Première syntaxe

for (initialisation ; condition ; incrementation) {
     instruction
     ...
}

Deuxième syntaxe

for (cle in tableau) {
     print cle , tableau[cle]
}

3 - while

Syntaxe

while (condition) {
     instruction
     ...
}

4 - do-while

Syntaxe

do {
     instruction
     ...
} while (condition)

5 - break

Le mot clé break permet d'interrompre une boucle.

Principe

while (1) {
     if (condition) break ;
     instruction ;
}

6 - continue

Le mot clé continue permet de remonter immédiatement à la condition, sans exécuter la suite de la boucle.

Principe

while (1) {
     if (condition) continue ;
     instruction ;
}

Etiquettes: 

Terminer un script

L'instruction exit permet de terminer un script à tout moment en retournant un statut au système.

Exemple

{
     if ( NF < 3 ) exit 1 ;
     print $1, $2, $3
}
 
END {
     exit 0
}

Etiquettes: 

Tableaux

Dans le langage awk, il existe 2 types de tableaux

Etiquettes: 

Tableaux indicés par un entier

L'indice de départ est au choix.

Exemple

Traitement du fichier /etc/passwd.
Ligne 2, initialisation de la variable FS.
Ligne 5, le contenu de la variable $1 correspondant au user est stocké dans le tableau user[] indicé à partir de 1.
Ligne 8 à 10, on parcourt le tableau et on affiche le contenu en formatant l'affichage avec printf.

$ nl script5.awk
     1  BEGIN {
     2          FS=":"
     3  }
     4  {
     5          user[NR]=$1
     6  }
     7  END {
     8          for (indice = 1 ; indice <= NR ; indice++ ) {
     9                  printf ("User num %2d : %-20s\n" , indice , user[indice]) ;
    10          }
    11  }

Exécution

$ awk -f script5.awk /etc/passwd
User num  1 : root
User num  2 : daemon
User num  3 : bin
User num  4 : sys
User num  5 : sync
User num  6 : games
User num  7 : man
User num  8 : lp
User num  9 : mail
User num 10 : news
User num 11 : uucp
User num 12 : proxy
User num 13 : www-data
User num 14 : backup
User num 15 : list
User num 16 : irc
User num 17 : gnats
User num 18 : nobody
User num 19 : libuuid
User num 20 : postfix
User num 21 : sshd
User num 22 : mysql
$

Etiquettes: 

Tableaux associatifs

Initialiser un tableau

Les tableaux associatifs ont leurs éléments indicés par une chaine de caractères. Cet indice alphanumérique est considéré comme la clé et l'élément correspondant est nommé valeur.

Exemple

Le fichier depts2012.txt liste tous les départements par numéro de région.

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
...
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant doit compter le nombre de départements qu'il y a par numéro de région.
La section BEGIN est vide car il n'y a pas besoin d'initialiser des variables.
Ligne 3, une condition est renseignée afin de ne pas traiter la première ligne du fichier qui correspond aux en-têtes.
Ligne 4, mise à jour du tableau nbdepts[] ayant comme clé la valeur du premier champ correspondant au numéro de la région et incrémenté de 1 à chaque fois que le champ 1 de la ligne traitée correspond à la clé.
Ligne 7 à 9, on parcourt le tableau et on affiche les résultats.

$ nl script6.awk
     1  BEGIN {
     2  }
     3  NR != 1{
     4          nbdepts[$1]+=1
     5  }
     6  END {
     7          for ( region in nbdepts) {
     8                  printf("Region num : %02d ==> %3d departement(s)\n" , region , nbdepts[region])
     9          }
    10  }
$

Exécution

$ awk -f script6.awk depts2012.txt
Region num : 01 ==>   1 departement(s)
Region num : 02 ==>   1 departement(s)
Region num : 03 ==>   1 departement(s)
Region num : 04 ==>   1 departement(s)
Region num : 11 ==>   8 departement(s)
Region num : 06 ==>   1 departement(s)
Region num : 21 ==>   4 departement(s)
Region num : 22 ==>   3 departement(s)
Region num : 23 ==>   2 departement(s)
Region num : 31 ==>   2 departement(s)
Region num : 24 ==>   6 departement(s)
Region num : 25 ==>   3 departement(s)
Region num : 26 ==>   4 departement(s)
Region num : 41 ==>   4 departement(s)
Region num : 42 ==>   2 departement(s)
Region num : 43 ==>   4 departement(s)
Region num : 52 ==>   5 departement(s)
Region num : 53 ==>   4 departement(s)
Region num : 54 ==>   4 departement(s)
Region num : 72 ==>   5 departement(s)
Region num : 73 ==>   8 departement(s)
Region num : 74 ==>   3 departement(s)
Region num : 82 ==>   8 departement(s)
Region num : 83 ==>   4 departement(s)
Region num : 91 ==>   5 departement(s)
Region num : 93 ==>   6 departement(s)
Region num : 94 ==>   2 departement(s)
$

Tester l'existence d'un élément

Le mot clé in permet de tester l'existence d'une clé dans un tableau associatif. Cette expression retourne vrai si la clé est présente, faux dans le cas contraire.

cle in tableau

Cette expression peut donc être utilisée comme condition d'une structure de contrôle.

if ( cle in tableau ) {
     ...
     ...
}

Supprimer un élément

Il est possible de supprimer un élément d'un tableau associatif en utilisant la syntaxe suivante.

delete tableau[cle]

La paire clé-valeur est supprimée.

Etiquettes: 

Les arguments de la ligne de commande

awk fourni un mécanisme qui permet de passer des arguments à un script au moment de son appel. Les variables ARGC et ARGV sont initialisées par awk et permettent de traiter les valeurs passées sur la ligne de commandes. La syntaxe doit être obligatroirement du genre var=value et placé avant le ou les fichiers à traiter.

Exemple

Avec le fichier depts2012.txt. Ecrire un script awk permettant de rechercher dans le fichier des enregistrements bien précis en lui passant comme argument un code région (champ 1), un code département (champ 2), un code postal (champ3) ou un département (champ5).

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
...
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant traitera au maximum 4 arguments. La variable $1 devra être strictement égale à la valeur de l'argument coderegion, la variable $2 devra être strictement égale à la valeur de l'argument codedept, la variable $3 devra être strictement égale à la valeur de l'argument codepostal et enfin la variable $5 devra être strictement égale à la valeur de l'argument dept. Tous ces arguments sont bien sûr facultatifs. Les instructions next permettent de ne pas afficher plusieurs fois une même ligne correspondante à plusieurs sections intermédiaires.

$ nl script9.awk
     1  BEGIN {
     2          print "ARGC = " , ARGC
     3          for (i=0 ; i<ARGC ; i++) {
     4                  printf ("ARGV[%d] = %s\n", i , ARGV[i])
     5          }
     6  }
     7  $1 == coderegion {
     8          print $0
     9          next
    10  }
    11  $2 == codedept {
    12          print $0
    13          next
    14  }
    15  $3 == codepostal {
    16          print $0
    17          next
    18  }
    19  $5 == dept {
    20          print $0
    21          next
    22  }
$

Exécution du script ayant comme argument de recherche coderegion=93

$ awk -f script9.awk coderegion=93 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = depts2012.txt
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
$

Exécution du script ayant comme argument de recherche codedept=44

$ awk -f script9.awk codedept=44 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = codedept=44
ARGV[2] = depts2012.txt
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
$

Exécution du script ayant comme argument de recherche codepostal=85191

$ awk -f script9.awk codepostal=85191 depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = codepostal=85191
ARGV[2] = depts2012.txt
52      85      85191   3       VENDEE  Vendée
$

Exécution du script ayant comme argument de recherche dept=VAR

$ awk -f script9.awk dept=VAR depts2012.txt
ARGC =  3
ARGV[0] = awk
ARGV[1] = dept=VAR
ARGV[2] = depts2012.txt
93      83      83137   2       VAR     Var
$

Exécution du script avec tous les arguments renseignés

$ awk -f script9.awk coderegion=93 codedept=44 codepostal=75056 dept=HAUTE-SAVOIE depts2012.txt
ARGC =  6
ARGV[0] = awk
ARGV[1] = coderegion=93
ARGV[2] = codedept=44
ARGV[3] = codepostal=75056
ARGV[4] = dept=HAUTE-SAVOIE
ARGV[5] = depts2012.txt
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
$

Etiquettes: 

Fonctions intégrées

Etiquettes: 

Les fonctions sur les chaines de caractères

Les chaines de caractères

En plus de fonctions de base, awk dispose également de fonctions dédiées aux traitements des chaines de caractères, facilitant ce genre d'opérations. La liste de ces fonctions est la suivante :

Fonction de string Description
gsub(exp,sub,str) Substitue globalement par la chaine sub chaque expression régulière exp trouvée dans la chaine str et retourne le nombre de substitutions. Si str n'est pas indiquée, par défaut $0 est utilisé.
index(str,st)

Retourne la position du string st dans la chaine str, ou 0 si non trouvé.

length(str) Retourne la longueur de la chaine str. Si str n'est pas indiquée, par défaut $0 est utilisé.
match(str,exp) Retourne la position de l'expression régulière exp dans la chaine str, ou 0 si non trouvé. Affecte les valeurs aux variables RSTART et RLENGTH.
split(str,tab,sep) Sépare la chaine str en éléments dans un tableau tab et en utilisant le séparateur sep. Si sep n'est pas renseigné, FS est utilisé par défaut.
sprintf("format",exp) Retourne une chaine au lieu de l'affichage vers la sortie standard, contrairement à printf().
sub(exp,sub,str) Comme gsub(), mais ne substitue par sub que la première expression exp trouvée dans str.
substr(str,pos,long) Retourne une partie du string str commançant à la position pos et de longueur long. Si long n'est pas indiqué, substr() utilise tout le reste de str.
tolower(str) Met en minuscules toute la chaine str et retourne la nouvelle chaine.
toupper(str) Met en majuscules toute la chaine str et retourne la nouvelle chaine.

Exemple :

gsub : remplacer par un @ toutes les lettres a et A du fichier depts2012.txt

$ head depts2012.txt | awk '{gsub(/a|A/,"@") ; print}'
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       @IN     @in
22      02      02408   5       @ISNE   @isne
83      03      03190   5       @LLIER  @llier
93      04      04070   4       @LPES-DE-H@UTE-PROVENCE @lpes-de-H@ute-Provence
93      05      05061   4       H@UTES-@LPES    H@utes-@lpes
93      06      06088   4       @LPES-M@RITIMES @lpes-M@ritimes
82      07      07186   5       @RDECHE @rdèche
21      08      08105   4       @RDENNES        @rdennes
73      09      09122   5       @RIEGE  @riège
$

index : connaitre la position d'un caractère dans une chaine

$ head depts2012.txt | awk '{pos=index($5,"-") ; print "Position du tiret : " , pos , "\tdans la chaine : " , $5}'
Position du tiret :  0  dans la chaine :  NCC
Position du tiret :  0  dans la chaine :  AIN
Position du tiret :  0  dans la chaine :  AISNE
Position du tiret :  0  dans la chaine :  ALLIER
Position du tiret :  6  dans la chaine :  ALPES-DE-HAUTE-PROVENCE
Position du tiret :  7  dans la chaine :  HAUTES-ALPES
Position du tiret :  6  dans la chaine :  ALPES-MARITIMES
Position du tiret :  0  dans la chaine :  ARDECHE
Position du tiret :  0  dans la chaine :  ARDENNES
Position du tiret :  0  dans la chaine :  ARIEGE
$

length : connaitre le nombre de caractères dans une chaine

$ tail depts2012.txt | awk '{lg=length($5) ; printf ("Il y a %3d caracteres dans la chaine %30s\n" , lg , $5)}'
Il y a   7 caracteres dans la chaine                        ESSONNE
Il y a  14 caracteres dans la chaine                 HAUTS-DE-SEINE
Il y a  17 caracteres dans la chaine              SEINE-SAINT-DENIS
Il y a  12 caracteres dans la chaine                   VAL-DE-MARNE
Il y a  10 caracteres dans la chaine                     VAL-D'OISE
Il y a  10 caracteres dans la chaine                     GUADELOUPE
Il y a  10 caracteres dans la chaine                     MARTINIQUE
Il y a   6 caracteres dans la chaine                         GUYANE
Il y a  10 caracteres dans la chaine                     LA_REUNION
Il y a   7 caracteres dans la chaine                        MAYOTTE
$

match : connaitre la position d'une expression dans une chaine

$ tail depts2012.txt | awk '{pos=match($5,/-DE-/) ; printf("Position de l expression recherchee \"-DE-\" : %2d dans la chaine %20s\tRSTART = %2d\tRLENGTH = %2d\n" , pos , $5 , RSTART , RLENGTH)}'
Position de l expression recherchee "-DE-" :  0 dans la chaine              ESSONNE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  6 dans la chaine       HAUTS-DE-SEINE     RSTART =  6     RLENGTH =  4
Position de l expression recherchee "-DE-" :  0 dans la chaine    SEINE-SAINT-DENIS     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  4 dans la chaine         VAL-DE-MARNE     RSTART =  4     RLENGTH =  4
Position de l expression recherchee "-DE-" :  0 dans la chaine           VAL-D'OISE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           GUADELOUPE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           MARTINIQUE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine               GUYANE     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine           LA_REUNION     RSTART =  0     RLENGTH = -1
Position de l expression recherchee "-DE-" :  0 dans la chaine              MAYOTTE     RSTART =  0     RLENGTH = -1
$

split : séparer une chaine en éléments dans un tableau

$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; printf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s\n" , tab[1] , tab[2] , tab[3])}'
tab1 =      HAUTS       tab2 =         DE       tab3 =      SEINE
tab1 =      SEINE       tab2 =      SAINT       tab3 =      DENIS
tab1 =        VAL       tab2 =         DE       tab3 =      MARNE
tab1 =        VAL       tab2 =     D'OISE       tab3 =
$

sprintf (contrairement à la commande printf, avec sprintf le retour chariot \n n'est pas obligatoire)

$ tail depts2012.txt | awk '/-/{split($5,tab,"-") ; chaine=sprintf("tab1 = %10s\ttab2 = %10s\ttab3 = %10s" , tab[1] , tab[2] , tab[3]) ; print chaine}'
tab1 =      HAUTS       tab2 =         DE       tab3 =      SEINE
tab1 =      SEINE       tab2 =      SAINT       tab3 =      DENIS
tab1 =        VAL       tab2 =         DE       tab3 =      MARNE
tab1 =        VAL       tab2 =     D'OISE       tab3 =
$

sub : remplacer par un @ la première lettre a ou A de chaque ligne du fichier depts2012.txt

$ head depts2012.txt | awk '{sub(/a|A/,"@") ; print}'
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       @IN     Ain
22      02      02408   5       @ISNE   Aisne
83      03      03190   5       @LLIER  Allier
93      04      04070   4       @LPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       H@UTES-ALPES    Hautes-Alpes
93      06      06088   4       @LPES-MARITIMES Alpes-Maritimes
82      07      07186   5       @RDECHE Ardèche
21      08      08105   4       @RDENNES        Ardennes
73      09      09122   5       @RIEGE  Ariège
$

substr : extraire une partie d'une chaine

$ tail depts2012.txt | awk '{chaine=substr($5,1,3) ; printf("Les 3 premieres lettres de %20s sont : %3s\n" , $5 , chaine)}'
Les 3 premieres lettres de              ESSONNE sont : ESS
Les 3 premieres lettres de       HAUTS-DE-SEINE sont : HAU
Les 3 premieres lettres de    SEINE-SAINT-DENIS sont : SEI
Les 3 premieres lettres de         VAL-DE-MARNE sont : VAL
Les 3 premieres lettres de           VAL-D'OISE sont : VAL
Les 3 premieres lettres de           GUADELOUPE sont : GUA
Les 3 premieres lettres de           MARTINIQUE sont : MAR
Les 3 premieres lettres de               GUYANE sont : GUY
Les 3 premieres lettres de           LA_REUNION sont : LA_
Les 3 premieres lettres de              MAYOTTE sont : MAY
$

tolower : convertir une chaine majuscule en minuscule

$ tail depts2012.txt | awk '{chaine=tolower($5) ; printf("%20s en minuscule : %20s\n" , $5 , chaine)}'
             ESSONNE en minuscule :              essonne
      HAUTS-DE-SEINE en minuscule :       hauts-de-seine
   SEINE-SAINT-DENIS en minuscule :    seine-saint-denis
        VAL-DE-MARNE en minuscule :         val-de-marne
          VAL-D'OISE en minuscule :           val-d'oise
          GUADELOUPE en minuscule :           guadeloupe
          MARTINIQUE en minuscule :           martinique
              GUYANE en minuscule :               guyane
          LA_REUNION en minuscule :           la_reunion
             MAYOTTE en minuscule :              mayotte
$

toupper : convertir une chaine minuscule en majuscule

$ tail depts2012.txt | awk '{chaine=toupper($6) ; printf("%20s en MAJUSCULE : %20s\n" , $6 , chaine)}'
             Essonne en MAJUSCULE :              ESSONNE
      Hauts-de-Seine en MAJUSCULE :       HAUTS-DE-SEINE
   Seine-Saint-Denis en MAJUSCULE :    SEINE-SAINT-DENIS
        Val-de-Marne en MAJUSCULE :         VAL-DE-MARNE
          Val-d'Oise en MAJUSCULE :           VAL-D'OISE
          Guadeloupe en MAJUSCULE :           GUADELOUPE
          Martinique en MAJUSCULE :           MARTINIQUE
              Guyane en MAJUSCULE :               GUYANE
             Mayotte en MAJUSCULE :              MAYOTTE
$

Etiquettes: 

Les fonctions mathématiques

Les fonctions mathématiques

awk dispose également de fonctions dédiées aux traitements numériques. Celles-ci sont les suivantes :

Fonction mathématique Description
cos(r) Cosinus de l'angle r (r en radians)
exp(x) Exponentiel de x
int(x) Valeur entière de x
log(x) Logarithme de x
sin(r) Sinus de l'angle r (r en radians)
sqrt(x) Racine carrée de x
atan2(y,x) Arc tangente de y/x
rand() Nombre pseudo-aléatoire compris entre 0 et 1
srand(n) Réinitialise la fonction rand()

Exemple :

cos

$ echo 50 | awk '{print cos($1)}'
0.964966
$

exp

$ echo 5 | awk '{print exp($1)}'
148.413
$

int

$ echo 5.4 | awk '{print $1 , " ==> " , int($1)}'
5.4  ==>  5
$

log

$ echo 5 | awk '{print log($1)}'
1.60944
$

sin

$ echo 50 | awk '{print sin($1)}'
-0.262375
$

sqrt

$ echo 81 | awk '{print sqrt($1)}'
9
$

atan2

$ echo 50 25 | awk '{print atan2($1,$2)}'
1.10715
$

rand

$ echo | awk '{print rand()}'
0.795735
$ echo | awk '{print rand()}'
0.321886
$ echo | awk '{print int(rand()*1000)}'
792
$

Etiquettes: 

Autres fonctions

Etiquettes: 

La fonction getline

La fonction getline permet de lire la ligne suivante du flux sans remonter au début du traitement (contrairement à next) et de lire une ligne à partir d'un fichier, de l'entrée standard ou d'un tube.

Valeur de retour :
- 1 en cas de succès
- 0 en fin de fichier
- -1 en cas d'erreur

Il ne faut pas mettre de parenthèse lors de l'appel de la fonction getline.

Syntaxe

getline [variable]
Lecture de la ligne suivante du flux.
getline [variable] < "fichier"
Lecture d'une ligne à partir d'un fichier
"commande" | getline [variable]
Lecture d'une ligne provenant du résultat d'une commande du système.

La ligne lue par getline est stockée dans la variable variable ou $0 si aucun nom de variable n'est spécifié. Le nom de fichier "-" représente l'entrée standard.

Premier exemple :

Reconstituer des phrases écrites sur plusieurs lignes et délimitées par un caractère spécifique.

Le fichier suivant contient des phrases scindées en plusieurs lignes. Le scindement est caractérisé par un anti-slash en fin de ligne.

$ cat text.txt
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \
Maecenas porttitor congue massa. \
Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, \
sit amet commodo magna eros quis urna.
Nunc viverra imperdiet enim. \
Fusce est. \
Vivamus a tellus.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. \
Proin pharetra nonummy pede. Mauris et orci.
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. \
Mauris eget neque at sem venenatis eleifend. \
Ut nonummy.
$.

Le script suivant va analyser chaque ligne du fichier et reconstituer l'intégralité des phrases grâce à la fonction getline.

$ nl script10.awk
     1  {
     2          ligne=$0
     3          # Tant que la ligne se termine par \
     4          while(ligne ~ /\\$/){
     5                  # Suppression de \
     6                  sub(/\\$/,"",ligne)
     7                  # Lecture de la ligne suivante
     8                  getline nextline
     9                  # Concatenation de ligne et nextline
    10                  ligne=ligne nextline
    11          }
    12          # Affichage de la ligne globale
    13          printf("------- Contenu de la phrase %d -------\n%s\n",++num,ligne)
    14  }
$

Exécution du script

$ awk -f script10.awk text.txt
------- Contenu de la phrase 1 -------
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies,purus lectus malesuada libero, sit amet commodo magna eros quis urna.
------- Contenu de la phrase 2 -------
Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.
------- Contenu de la phrase 3 -------
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.
------- Contenu de la phrase 4 -------
Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.
------- Contenu de la phrase 5 -------
Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.
$

Deuxième exemple :

Lire une entrée clavier et le contenu d'un fichier extérieur au flux courant.

Rechercher le nom d'un département dans le fichier depts2012.txt en saisissant son nom au clavier.

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
...
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Le script suivant demande une saisie au clavier et recherche dans le fichier précédent le nom saisi.

$ nl script11.awk
     1  BEGIN{
     2          # Boucle infinie
     3          while(1){
     4                  printf("Rechercher le nom (ctrl+d pour quitter): ")
     5                  # Lecture clavier sur l'entree standard "-"
     6                  # Arret du programme si le code retour de getline est different de 1
     7                  if((getline nom < "-") != 1) break
     8                  # Si aucun nom saisi, on recommence
     9                  if(length(nom)==0) continue
    10                  trouve="false"
    11                  # Boucle de lecture du fichier depts2012.txt
    12                  # Tant que le code retour de getline est egal a 1
    13                  while((getline < "depts2012.txt")==1){
    14                          # Comparaison faite sans tenir compte de la casse
    15                          if(tolower($5)==tolower(nom)){
    16                                  trouve="true"
    17                                  # Affichage du resultat trouve
    18                                  print $0
    19                                  # On sort de la boucle while
    20                                  break
    21                          }
    22                  }
    23                  # Fermeture du fichier
    24                  close("depts2012.txt")
    25                  # Affichage du message si le nom n'a pas ete trouve
    26                  if(trouve=="false") print nom , "n'est pas dans le fichier"
    27          }
    28          # Affichage du message avant l'arret du programme
    29          print "\nA bientot"
    30  }
$

Exécution du script

$ awk -f script11.awk
Rechercher le nom (ctrl+d pour quitter): ain
82      01      01053   5       AIN     Ain
Rechercher le nom (ctrl+d pour quitter): somme
22      80      80021   3       SOMME   Somme
Rechercher le nom (ctrl+d pour quitter):
Rechercher le nom (ctrl+d pour quitter): var
93      83      83137   2       VAR     Var
Rechercher le nom (ctrl+d pour quitter):
A bientot
$

Troisième exemple :

Utiliser getline pour lire le résulat d'une commande système.

$ nl script12.awk
     1  BEGIN{
     2          "date" | getline
     3          print "$0 = ",$0
     4          for(i=1;i<=NF;i++){
     5                  printf("$%d = %s\n",i,$i)
     6          }
     7  }
$ awk -f script12.awk
$0 =  Tue May 15 19:35:25 CEST 2012
$1 = Tue
$2 = May
$3 = 15
$4 = 19:35:25
$5 = CEST
$6 = 2012
$

Etiquettes: 

La fonction close

La fonction close permet de fermer un fichier ou un tube de communication. Si l'appel à cette fonction est omis, les ressources sont libérées à la terminaison du script.

L'ordre de fermeture est intéressant dans les cas suivants :
- pouvoir se repositionner en début de fichier au sein du même processus (fermeture puis réouverture)
- fermer un tube de communication pour s'en servir à nouveau
- libérer les ressources au fur et à mesure des besoins (le système limite les processus, en ce qui concerne le nombre de fichiers/tubes ouverts simultanément).

Syntaxe

close("fichier")
close("commande")

Il est indispensable de fermer un fichier pour pouvoir le relire à partir du début.

Exemple :

$ nl script13.awk
     1  BEGIN {
     2          fichier = "/root/fichier1.txt"
     3          i=1
     4          while(i<=10){
     5                  print rand() > fichier
     6                  i++
     7          }
     8          # Fermeture du fichier
     9          close(fichier)
    10          while((getline<fichier)==1){
    11                  print $0
    12          }
    13  }
$ awk -f script13.awk
0.692303
0.532958
0.423364
0.484409
0.455168
0.0133607
0.554077
0.375875
0.325945
0.164046
$

Etiquettes: 

La fonction system

La fonction system permet d'exécuter une commande du système.

Syntaxe

system("commande")

La fonction retourne le statut renvoyé par la commande.

Exemple :

$ awk 'BEGIN{system("uptime")}'
 21:06:15 up 5 days,  2:22,  0 users,  load average: 0.00, 0.00, 0.00
$

Idem mais en se servant d'une variable

$ awk 'BEGIN{fic="depts2012.txt";system("ls -l " fic)}'
-rw-r--r-- 1 root root 3504 May 10 14:05 depts2012.txt
$

Etiquettes: 

Fonctions utilisateur

Les fonctions personnelles peuvent être définies dans le script awk, en dehors des blocs d'instructions, ou dans un autre script qui sera également appelé par l'option -f <script>.

Une fonction se définit par le mot clé function suivi du nom de la fonction et de ses paramètres. Une fonction peut recevoir de 0 à n arguments et retourner une valeur explicite.

Syntaxe

function nom_fonction (param1, param2, ..., paramn) {
     instructions
     return valeur
}

Tous les paramètres de la fonction sont des variables locales. Toute autre variable définie dans la fonction est globale.

Appel d'une fonction

valeur=nom_fonction(val1, val2, ..., valn)

Il ne doit pas y avoir d'espace entre le nom de la fonction et la parenthèse ouvrante.

Exemple

$ nl script14.awk
     1  function modulo(nb){
     2          mod=nb%2
     3          if(mod==0){
     4                  chaine=sprintf("%d est un nombre pair" , nb)
     5          }
     6          else{
     7                  chaine=sprintf("%d est un nombre impair" , nb)
     8          }
     9          return chaine
    10  }
    11  {
    12          for(i=1;i<=NF;i++){
    13                  print modulo($i)
    14          }
    15  }
$ echo "20 21 23 56 43 2.4 rr" | awk -f script14.awk
20 est un nombre pair
21 est un nombre impair
23 est un nombre impair
56 est un nombre pair
43 est un nombre impair
2 est un nombre impair
0 est un nombre pair
$

Etiquettes: 

Exemples de scripts

Etiquettes: 

Analyse des logs postfix

Le script suivant analyse les logs postfix et retourne le nombre de messages par code erreur SMTP.
3 niveaux de visualisation sont proposés dont le jour et le mois en cours.

Pour fonctionner, le script à besoin d'un fichier additionnel répertoriant la liste des codes erreurs et leurs descriptions.

Téléchargement des fichiers :

  1. codeErrorSmtp
  2. mail.awk

Détail du fichier codeErrorSmtp :

$ cat codeErrorSmtp
X.1.0|Other address status
X.1.1|Bad destination mailbox address
X.1.2|Bad destination system address
X.1.3|Bad destination mailbox address syntax
X.1.4|Destination mailbox address ambiguous
X.1.5|Destination mailbox address valid
X.1.6|Mailbox has moved
X.1.7|Bad sender's mailbox address syntax
X.1.8|Bad sender's system address
X.2.0|Other or undefined mailbox status
X.2.1|Mailbox disabled, not accepting messages
X.2.2|Mailbox full
X.2.3|Message length exceeds administrative limit.
X.2.4|Mailing list expansion problem
X.3.0|Other or undefined mail system status
X.3.1|Mail system full
X.3.2|System not accepting network messages
X.3.3|System not capable of selected features
X.3.4|Message too big for system
X.4.0|Other or undefined network or routing status
X.4.1|No answer from host
X.4.2|Bad connection
X.4.3|Routing server failure
X.4.4|Unable to route
X.4.5|Network congestion
X.4.6|Routing loop detected
X.4.7|Delivery time expired
X.5.0|Other or undefined protocol status
X.5.1|Invalid command
X.5.2|Syntax error
X.5.3|Too many recipients
X.5.4|Invalid command arguments
X.5.5|Wrong protocol version
X.6.0|Other or undefined media error
X.6.1|Media not supported
X.6.2|Conversion required and prohibited
X.6.3|Conversion required but not supported
X.6.4|Conversion with loss performed
X.6.5|Conversion failed
X.7.0|Other or undefined security status
X.7.1|Delivery not authorized, message refused
X.7.2|Mailing list expansion prohibited
X.7.3|Security conversion required but not possible
X.7.4|Security features not supported
X.7.5|Cryptographic failure
X.7.6|Cryptographic algorithm not supported
X.7.7|Message integrity failure
$

Détail du script commenté :

$ nl mail.awk
     1  BEGIN{
     2          # Si le nombre d'arguments est egal a 1
     3          # Arret du programme
     4          # Il faut au moins 2 arguments (1 nom de fichier de log)
     5          # Le script peut accepter plusieurs fichiers de log
     6          if(ARGC==1){
     7                  print "Nombre d'arguments incorrect"
     8                  print "Syntaxe : awk -f mail.awk fichierLogMail1 [fichierLogMailn...]"
     9                  exit 1
    10          }
    11          # Initialisation de la variable contenant le nom du fichier
    12          # des codes erreurs SMTP
    13          errorSmtp="codeErrorSmtp"
    14          # Execution d'une commande systeme pour tester si le fichier existe
    15          # Arret du programme si le fichier n'existe pas
    16          if((system("test -f " errorSmtp))==1){
    17                  print "Fichier codeErrorSmtp manquant"
    18                  exit 1
    19          }
    20          # Sauvegarde des donnees du fichier codeErrorSmtp
    21          # dans un tableau associatif avec le code en cle
    22          while((getline < errorSmtp) == 1){
    23                  ligne=$0
    24                  split(ligne,tab,"|")
    25                  tabErr[tab[1]]=tab[2]
    26          }
    27          # Fermeture du fichier codeErrorSmtp
    28          close(errorSmtp)
    29          # Affichage du menu
    30          while(1){
    31                  # Si le choix est passe en argument
    32                  if(ARGV[1] ~ /^choix=/){
    33                          split(ARGV[1],tab,"=")
    34                          choix=tab[2]
    35                          if(choix>=1 && choix<=3) break
    36                  }
    37                  print "Choix date"
    38                  print " 1 - all"
    39                  print " 2 - day"
    40                  print " 3 - month"
    41                  printf("Choix : ")
    42                  # Lecture de l'entree clavier
    43                  # Si erreur, on quitte la boucle while
    44                  if((getline choix < "-")!=1) break
    45                  # Si aucune saisie, reaffichage du menu
    46                  if(length(choix)==0) continue
    47                  # Si le choix est compris entre 1 et 3 on quitte la boucle while
    48                  if(choix>=1 && choix<=3) break
    49          }
    50          deb=systime()
    51          # Si le choix est egal a rien on quitte le programme
    52          if(choix==""){
    53                  print "Bye"
    54                  exit 2
    55          }
    56          print "-------------------------------------------"
    57          print "------- Analyse des fichiers de log -------"
    58          print "-------------------------------------------"
    59  }
    60  # Lecture des fichiers de log
    61  # Si la ligne courante contient l'expression recherchee
    62  $0 ~ /[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/ && (choix==1 || (choix==2 && $1==getMonth() && $2==getDay()) || (choix==3 && $1==getMonth())) {
    63          # Recherche de la position du premier caractere de l'expression xxx x.x.x
    64          pos=match($0,/[0-9][0-9][0-9] [0-9]\.[0-9]\.[0-9]/)
    65          # Extraction de l'expression xxx x.x.x
    66          code=substr($0,pos,9)
    67          # Sauvegarde de l'expression comme cle du tableau associatif
    68          # et incrementation du compteur
    69          codeSmtp[code]+=1
    70  }
    71  END{
    72          # Parcourt du tableau associatif codeSmtp
    73          totalMessages=0
    74          for(i in codeSmtp){
    75                  # Affichage des lignes du tableau
    76                  # et de la description du code avec la fonction descCode
    77                  printf("Code %s (%-45s) ==> %5d message(s)\n",i,descCode(i),codeSmtp[i])
    78                  totalMessages+=codeSmtp[i]
    79          }
    80          # Fin du programme
    81          fin=systime()
    82          duree=fin-deb
    83          printf("Total : %d messages\n%d lignes traitees en %d sec\n",totalMessages,NR,duree)
    84          exit 0
    85  }
    86  # Fonction permettant la recherche de la description du code
    87  function descCode(code){
    88          # Extraction des 4 derniers caracteres du code à partir du 6eme caractere
    89          codex=substr(code,6,4)
    90          # Concatenation de la lettre "X" aux 4 derniers caracteres du code
    91          codex="X" codex
    92          # Extraction de la description en fonction du code
    93          desc=tabErr[codex]
    94          # Si le code erreur n'existe pas alors OK
    95          desc=(desc != "" ? desc : "OK")
    96          # Retour de la description au programme appelant
    97          return desc
    98  }
    99  # Fonction permettant d'obtenir le numero du jour en cours
   100  function getDay(){
   101          day=int(strftime("%d"))
   102          return day
   103  }
   104  # Fonction permettant d'obtenir le nom du mois en cours
   105  function getMonth(){
   106          month=strftime("%b")
   107          return month
   108  }
$

Exécution du script :

$ awk -f mail.awk /var/log/mail.log
Choix date
 1 - all
 2 - day
 3 - month
Choix : 3
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused     ) ==>  1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address              ) ==>    23 message(s)
Code 250 2.0.0 (OK                                           ) ==>  1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$

En passant le choix de la date en argument :

$ awk -f mail.awk choix=3 /var/log/mail.log
-------------------------------------------
------- Analyse des fichiers de log -------
-------------------------------------------
Code 554 5.7.1 (Delivery not authorized, message refused     ) ==>  1992 message(s)
Code 550 5.1.1 (Bad destination mailbox address              ) ==>    23 message(s)
Code 250 2.0.0 (OK                                           ) ==>  1620 message(s)
Total : 3635 messages
42719 lignes traitees en 0 sec
$

Etiquettes: 

Afficher les infos du processeur et de la mémoire dans une page web

Exemple extrait du Linux/Magazine N° 131 d'octobre 2010 page 52.

Le script infoproc.awk utilise les fichiers virtuels /proc/cpuinfo et /proc/meminfo pour extraire les informations à afficher dans une page HTML infoproc.html. Les fichiers sont à placer en derniers paramètres et peuvent être traités différemment par les variables FILENAME et FNR. La sortie du script est redirigée vers le fichier infoproc.html.

L'en-tête et la fin de page HTML sont codés dans le bloc BEGIN et END.

Le fichier virtuel /proc/cpuinfo contient des enregistrements séparés par une ligne vide. Chaque champs des enregistrements est défini sur une ligne. Pour ce fichier, la variable de séparation d'enregistrement RS est donc égale à un saut de ligne "\n\n" et la variable de séparation des champs FS est égale à un retour-chariot "\n". Le fichier /proc/meminfo contient un seul enregistrement et les champs sont séparés par un retour-chariot, aussi les variables RS et FS n'ont pas à être modifiées entre ces fichiers.

Télécharger le script

infoproc.awk

Détail du script infoproc.awk

$ nl infoproc.awk
     1  BEGIN{
     2          RS="\n\n"       # Separateur d'enregistrement
     3          FS="\n"         # Separateur de ligne
     4          # En-tete et debut de page HTML
     5          print "<html>"
     6          print "<head>"
     7          print "<title>Informations CPU et m&eacute;moire</title>"
     8          print "<style type=\"text/css\">"
     9          print "table {border: solid thin; padding 10px; margin 5px}"
    10          print "table.proc {color: DarkSlateBlue; border-color: DarkSlateBlue}"
    11          print "table.proc caption {color: white; background: DarkSlateBlue; text-align: center}"
    12          print "table.mem {color: DarkGreen; border-color: DarkGreen}"
    13          print "table.mem caption {color: white; background: DarkGreen; text-align: center}"
    14          print "</style>"
    15          print "</head>"
    16          print "<body>"
    17          print "<table><tr>"
    18  }
    19  FILENAME ~ /cpuinfo$/ { print "<td valign=\"top\"><table class=\"proc\">"}
    20  FILENAME ~ /meminfo$/ { print "<td valign=\"top\"><table class=\"mem\">"}
    21  {
    22          for(i=1; i<=NF; i++){
    23                  split($i, cpu, ":")
    24                  if(i==1) print "<caption>", cpu[1], cpu[2], "</caption>"
    25                  else print "<tr><td>", cpu[1], "</td><td>", cpu[2], "</td></tr>"
    26          }
    27          print "</table></td>"
    28  }
    29  END{
    30          # Fin de page HTML
    31          print "</tr></table>"
    32          print "</body>"
    33          print "</html>"
    34  }
$

Exécution du script

$ awk -f /root/infoproc.awk /proc/cpuinfo /proc/meminfo > /var/www/infoproc.html

Résultat obtenu

Fichier attachéTaille
HTML icon infoproc.html5.96 Ko
Etiquettes: 

Les commandes filtres

Les commandes filtres traitent un flux de données issues de l'entrée standard ou contenues dans un fichier. Elles peuvent être utilisées de manière autonome ou placées derrière un tube de communication. Les filtres s'utilisent pour la plupart de la même manière.

Syntaxe

commande_filtre -options fic1 fic2 ... ficn
commande_filtre -options -
commande_filtre -options < fichier
commande | commande_filtre -options

Visualisation de données

Consultation de données, création de fichier : cat

Outre la consultation de données, voici quelques exemples d'utilisation de la commande cat.

Syntaxe

cat [options] [fichiers]
Principales options
-e     Affiche le symbole "$" à la fin de chaque ligne
-t     Affiche le symbole "^I" (i majuscule) à chaque tabulation
-v     Matérialise les caractères non affichables

Exemples

Quand la commande cat ne reçoit pas de fichier en argument, elle affiche sur la sortie standard les données saisies sur l'entrée standard.

$ cat
ceci est un test     # Texte saisi au clavier sur l'entrée standard
ceci est un test     # Texte afficher automatiquement sur la sortie standard
^d
$

Ce qui est saisi sur l'entrée standard peut être enregistré dans un fichier

$ cat > fic1
ceci est un test
^d
$ cat fic1
ceci est un test
$

cat peut également concaténer des fichiers

$ cat fic1
ceci est un test
$ cat fic2
ceci est un autre test
$ cat fic1 fic2 > fic3
$ cat fic3
ceci est un test
ceci est un autre test
$

L'option -t permet de visualiser les tabulations. Celles-ci sont matérialisées par les caractères "^I" (i majuscule)

$ cat fic4
ceci    est     un      test
$ cat -t fic4
ceci^Iest^Iun^Itest
$

L'option -e permet de visualiser les fins de ligne. Celles-ci sont matérialisées par le symbole "$"

$ cat -e fic4
ceci    est     un      test$
$

L'option -A permet de visualiser tous les caractères non affichables

$ cat -A fic4
ceci^Iest^Iun^Itest$
$

Etiquettes: 

Valeur des octets d'un flux de données : od

La commande od permet de visualiser la valeur de chaque octet d'un fichier texte ou binaire. L'option -c permet une interprétation octet par octet. Il est ainsi possible de voir de manière exacte le contenu du fichier, de détecter l'emplacement des espaces, tabulations et caractères de sauts de ligne (CR, LF ou CR LF).

Avec od, les tabulations sont représentées par \t, les espaces par un espace, le caractère LF par \n

Exemple

$ cat fic5
ceci est un     test
et un   autre test
$ od -c fic5
0000000   c   e   c   i       e   s   t       u   n  \t   t   e   s   t
0000020  \n   e   t       u   n      \t   a   u   t   r   e       t   e
0000040   s   t  \t  \n
0000044
$

La colonne de gauche représente la position du premier caractère de la ligne dans le fichier, exprimée en octal.

Etiquettes: 

Filtrage de lignes : grep

La commande grep recherche une chaine de caractères dans un ou plusieurs fichiers textes et affiche à l'écran les lignes contenant cette chaine. La chaine recherchée est matérialisée par une expression régulière basique ERb (défaut) ou étendue ERe grâce à l'option -E.

Syntaxe

grep [options] expreg [fichiers...]
grep [options] -e expreg1 -e expreg2 [fichiers...]
grep [options] -f fichier_expreg [fichiers...]
Principales options :
-c affiche le nombre de lignes trouvées
-e permet de spécifier plusieurs expressions régulières
-E permet d'utiliser les ERe
-f permet de lire les expressions régulières à partir d'un fichier
-F permet de ne pas interpréter le motif de recherche comme une expression régulière
-i permet une recherche insensible à la casse
-l permet de seulement afficher les noms des fichiers contenant l'expression régulière recherchée
-n permet de numéroter les lignes trouvées
-q permet d'effectuer une recherche sans afficher le résultat à l'écran
-v permet une recherche ne contenant pas l'expression régulière
-w l'expression recherchée doit correspondre à un mot entier
-x l'expression recherchée doit correspondre à une ligne entière
-B n Affiche n lignes avant l'expression recherchée (-B pour Before)
-A n Affiche n lignes après l'expression recherchée (-A pour After)

-C n Affiche n lignes avant et après l'expression recherchée
-P permet d'utiliser des regex compatible PERL

Exemple

Utilisation du fichier depts2012.txt pour les exemples

$ cat depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
93      04      04070   4       ALPES-DE-HAUTE-PROVENCE Alpes-de-Haute-Provence
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
21      10      10387   5       AUBE    Aube
91      11      11069   5       AUDE    Aude
73      12      12202   5       AVEYRON Aveyron
93      13      13055   4       BOUCHES-DU-RHONE        Bouches-du-Rhône
25      14      14118   2       CALVADOS        Calvados
83      15      15014   2       CANTAL  Cantal
54      16      16015   3       CHARENTE        Charente
54      17      17300   3       CHARENTE-MARITIME       Charente-Maritime
24      18      18033   2       CHER    Cher
74      19      19272   3       CORREZE Corrèze
94      2A      2A004   3       CORSE-DU-SUD    Corse-du-Sud
94      2B      2B033   3       HAUTE-CORSE     Haute-Corse
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
74      23      23096   3       CREUSE  Creuse
72      24      24322   3       DORDOGNE        Dordogne
43      25      25056   2       DOUBS   Doubs
82      26      26362   3       DROME   Drôme
23      27      27229   5       EURE    Eure
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      29      29232   2       FINISTERE       Finistère
91      30      30189   2       GARD    Gard
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
91      34      34172   5       HERAULT Hérault
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
72      40      40192   4       LANDES  Landes
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
73      46      46042   2       LOT     Lot
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
91      48      48095   3       LOZERE  Lozère
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
25      50      50502   3       MANCHE  Manche
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
52      53      53130   3       MAYENNE Mayenne
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
41      55      55029   3       MEUSE   Meuse
53      56      56260   2       MORBIHAN        Morbihan
41      57      57463   3       MOSELLE Moselle
26      58      58194   3       NIEVRE  Nièvre
31      59      59350   2       NORD    Nord
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
43      70      70550   3       HAUTE-SAONE     Haute-Saône
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
52      72      72181   3       SARTHE  Sarthe
82      73      73065   3       SAVOIE  Savoie
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
23      76      76540   3       SEINE-MARITIME  Seine-Maritime
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
22      80      80021   3       SOMME   Somme
73      81      81004   2       TARN    Tarn
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
93      83      83137   2       VAR     Var
93      84      84007   0       VAUCLUSE        Vaucluse
52      85      85191   3       VENDEE  Vendée
54      86      86194   3       VIENNE  Vienne
74      87      87085   3       HAUTE-VIENNE    Haute-Vienne
41      88      88160   4       VOSGES  Vosges
26      89      89024   5       YONNE   Yonne
43      90      90010   0       TERRITOIRE_DE_BELFORT   Territoire de Belfort
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher la ligne contenant 85191
 

$ grep 85191 depts2012.txt
52      85      85191   3       VENDEE  Vendée
$

Utilisation d'une ERe et d'une recherche insensible à la casse
 

$ grep -E -i '(paris|vosges)' depts2012.txt
11      75      75056   0       PARIS   Paris
41      88      88160   4       VOSGES  Vosges
$

Afficher les lignes ne commençant pas par le chiffre 9
 

$ grep -v '^9' depts2012.txt
REGION  DEP     CHEFLIEU        TNCC    NCC     NCCENR
82      01      01053   5       AIN     Ain
22      02      02408   5       AISNE   Aisne
83      03      03190   5       ALLIER  Allier
82      07      07186   5       ARDECHE Ardèche
21      08      08105   4       ARDENNES        Ardennes
73      09      09122   5       ARIEGE  Ariège
21      10      10387   5       AUBE    Aube
73      12      12202   5       AVEYRON Aveyron
25      14      14118   2       CALVADOS        Calvados
83      15      15014   2       CANTAL  Cantal
54      16      16015   3       CHARENTE        Charente
54      17      17300   3       CHARENTE-MARITIME       Charente-Maritime
24      18      18033   2       CHER    Cher
74      19      19272   3       CORREZE Corrèze
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
74      23      23096   3       CREUSE  Creuse
72      24      24322   3       DORDOGNE        Dordogne
43      25      25056   2       DOUBS   Doubs
82      26      26362   3       DROME   Drôme
23      27      27229   5       EURE    Eure
24      28      28085   1       EURE-ET-LOIR    Eure-et-Loir
53      29      29232   2       FINISTERE       Finistère
73      31      31555   3       HAUTE-GARONNE   Haute-Garonne
73      32      32013   2       GERS    Gers
72      33      33063   3       GIRONDE Gironde
53      35      35238   1       ILLE-ET-VILAINE Ille-et-Vilaine
24      36      36044   5       INDRE   Indre
24      37      37261   1       INDRE-ET-LOIRE  Indre-et-Loire
82      38      38185   5       ISERE   Isère
43      39      39300   2       JURA    Jura
72      40      40192   4       LANDES  Landes
24      41      41018   0       LOIR-ET-CHER    Loir-et-Cher
82      42      42218   3       LOIRE   Loire
83      43      43157   3       HAUTE-LOIRE     Haute-Loire
52      44      44109   3       LOIRE-ATLANTIQUE        Loire-Atlantique
24      45      45234   2       LOIRET  Loiret
73      46      46042   2       LOT     Lot
72      47      47001   0       LOT-ET-GARONNE  Lot-et-Garonne
52      49      49007   0       MAINE-ET-LOIRE  Maine-et-Loire
25      50      50502   3       MANCHE  Manche
21      51      51108   3       MARNE   Marne
21      52      52121   3       HAUTE-MARNE     Haute-Marne
52      53      53130   3       MAYENNE Mayenne
41      54      54395   0       MEURTHE-ET-MOSELLE      Meurthe-et-Moselle
41      55      55029   3       MEUSE   Meuse
53      56      56260   2       MORBIHAN        Morbihan
41      57      57463   3       MOSELLE Moselle
26      58      58194   3       NIEVRE  Nièvre
31      59      59350   2       NORD    Nord
22      60      60057   5       OISE    Oise
25      61      61001   5       ORNE    Orne
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
83      63      63113   2       PUY-DE-DOME     Puy-de-Dôme
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
42      67      67482   2       BAS-RHIN        Bas-Rhin
42      68      68066   2       HAUT-RHIN       Haut-Rhin
82      69      69123   2       RHONE   Rhône
43      70      70550   3       HAUTE-SAONE     Haute-Saône
26      71      71270   0       SAONE-ET-LOIRE  Saône-et-Loire
52      72      72181   3       SARTHE  Sarthe
82      73      73065   3       SAVOIE  Savoie
82      74      74010   3       HAUTE-SAVOIE    Haute-Savoie
11      75      75056   0       PARIS   Paris
23      76      76540   3       SEINE-MARITIME  Seine-Maritime
11      77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
22      80      80021   3       SOMME   Somme
73      81      81004   2       TARN    Tarn
73      82      82121   0       TARN-ET-GARONNE Tarn-et-Garonne
52      85      85191   3       VENDEE  Vendée
54      86      86194   3       VIENNE  Vienne
74      87      87085   3       HAUTE-VIENNE    Haute-Vienne
41      88      88160   4       VOSGES  Vosges
26      89      89024   5       YONNE   Yonne
43      90      90010   0       TERRITOIRE_DE_BELFORT   Territoire de Belfort
11      91      91228   5       ESSONNE Essonne
11      92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
11      94      94028   2       VAL-DE-MARNE    Val-de-Marne
11      95      95500   2       VAL-D'OISE      Val-d'Oise
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher les lignes commençant par un 0 ou se terminant par un s
 

$ grep -e '^0' -e 's$' depts2012.txt
93      05      05061   4       HAUTES-ALPES    Hautes-Alpes
93      06      06088   4       ALPES-MARITIMES Alpes-Maritimes
21      08      08105   4       ARDENNES        Ardennes
25      14      14118   2       CALVADOS        Calvados
43      25      25056   2       DOUBS   Doubs
73      32      32013   2       GERS    Gers
72      40      40192   4       LANDES  Landes
31      62      62041   2       PAS-DE-CALAIS   Pas-de-Calais
72      64      64445   4       PYRENEES-ATLANTIQUES    Pyrénées-Atlantiques
73      65      65440   4       HAUTES-PYRENEES Hautes-Pyrénées
91      66      66136   4       PYRENEES-ORIENTALES     Pyrénées-Orientales
11      75      75056   0       PARIS   Paris
11      78      78646   4       YVELINES        Yvelines
54      79      79191   4       DEUX-SEVRES     Deux-Sèvres
41      88      88160   4       VOSGES  Vosges
11      93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
01      971     97105   3       GUADELOUPE      Guadeloupe
02      972     97209   3       MARTINIQUE      Martinique
03      973     97302   3       GUYANE  Guyane
04      974     97411   0       LA_REUNION      La Réunion
06      976     97608   0       MAYOTTE Mayotte
$

Afficher et numéroter les lignes commençant par 11
 

$ grep -n '^11' depts2012.txt
77:11   75      75056   0       PARIS   Paris
79:11   77      77288   0       SEINE-ET-MARNE  Seine-et-Marne
80:11   78      78646   4       YVELINES        Yvelines
93:11   91      91228   5       ESSONNE Essonne
94:11   92      92050   4       HAUTS-DE-SEINE  Hauts-de-Seine
95:11   93      93008   3       SEINE-SAINT-DENIS       Seine-Saint-Denis
96:11   94      94028   2       VAL-DE-MARNE    Val-de-Marne
97:11   95      95500   2       VAL-D'OISE      Val-d'Oise
$

Effectuer une recherche sans afficher le résultat. Afficher le code de retour de la commande pour vérifier l'état de la recherche.
 

$ grep -q '^11' depts2012.txt
$ echo $?
0
$

Afficher seulement le nombre de lignes trouvées
 

$ grep -c -e '^0' -e 's$' depts2012.txt
21
$

Rechercher un mot exact
 

$ grep COTE depts2012.txt # Sans l'option -w 2 lignes sont affichées
26      21      21231   3       COTE-D'OR       Côte-d'Or
53      22      22278   4       COTES-D'ARMOR   Côtes-d'Armor
$ grep -w COTE depts2012.txt # Avec l'option -w seule la ligne contenant le mot COTE s'affiche
26      21      21231   3       COTE-D'OR       Côte-d'Or
$

Afficher les lignes contenant au moins 2 caractères. Utilisation du fichier fic7 en exemple.
 

$ cat fic7
erytert
e
er
..
uhy
2
e_
34
546
$ grep '..' fic7
erytert
er
..
uhy
e_
34
546
$

Ne pas interpréter le motif comme une expression régulière.
 

$ grep -F '..' fic7
..
$

Rechercher les lignes ne contenant que des chiffres. Utilisation d'une ERe.
 

$ grep -E '^[0-9]+$' << EOF        # Sans l'option -x
5
455788
456 657588
elrerioyuerioy
sdgjhg4547457
56456456dgldkgdlfg
EOF
5
455788

$ grep -x -E '[0-9]+' << EOF        # Avec l'option -x
5
455788
456 657588
elrerioyuerioy
sdgjhg4547457
56456456dgldkgdlfg
EOF
5
455788

Utiliser un fichier pour rechercher les expressions régulières
 

$ cat rech.er
# Rechercher les lignes se terminant par la lettre t
t$
# Rechercher les lignes contenant uniquement des chiffres
^[0-9]+$
$ grep -E -f rech.er fic7
erytert
2
34
546
$

Utiliser grep derrière un tube pour rechercher un processus particulier.
 

$ ps -ef | grep apache2
root       302     1  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   351   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   352   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
root      3783  3105  0 14:10 pts/0    00:00:00 grep --color=always apache2
$

Dans ce cas, la commande grep apparait également. L'utilisation de 2 tubes est donc nécessaire.

$ ps -ef | grep apache2 | grep -v grep # On demande à grep de ne pas afficher "grep"
root       302     1  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   351   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
www-data   352   302  0 06:18 ?        00:00:00 /usr/sbin/apache2 -k start
$

Afficher les 3 lignes précédant le mot "rose"
 

$ grep -B 3 rose << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
$

Afficher les 3 lignes suivant le mot "rose"
 

$ grep -A 3 rose << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
$

Afficher 1 ligne avant et après le mot "rose"
 

$ grep -C 1 rose << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
$

Exemple d'utilisation avec l'option -P (regex compatible PERL)
 

Dans le texte suivant, le mot "nécessaire" existe 2 fois

$ grep nécessaire << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Angoisse soulever aventure nécessaire sans installer plante.
Inutile rose nombre nécessaire président en robe.

Recherche du mot "nécessaire" uniquement s'il est précédé du mot "nombre"
 

$ grep -P '(?<=nombre) nécessaire' << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Inutile rose nombre nécessaire président en robe.

Recherche du mot "nécessaire" uniquement s'il est suivi du mot "sans"
 

$ grep -P 'nécessaire (?=sans)' << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Angoisse soulever aventure nécessaire sans installer plante.

Recherche du mot "nécessaire" uniquement s'il n'est pas suivi du mot "sans"
 

$ grep -P 'nécessaire (?!sans)' << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Inutile rose nombre nécessaire président en robe.

Recherche du mot "nécessaire" uniquement s'il n'est pas précédé du mot "nombre"
 

$ grep -P '(?<!nombre) nécessaire' << EOF
Pont entier étroit composer détacher politique terrain.
Angoisse soulever aventure nécessaire sans installer plante.
Plante protéger prochain sentir paysage.
Pierre fort façon loin relation.
Saint accent hasard franchir.
Inutile rose nombre nécessaire président en robe.
Entre mener semaine déposer atteindre professeur rentrer.
Terrible choisir loi subir oser.
Sonner expliquer conduire soldat.
Étendue profond table beau chose.
EOF
Angoisse soulever aventure nécessaire sans installer plante.

 

 

Etiquettes: 

Dernières lignes d'un flux de données : tail

La commande tail permet d'afficher, par défaut, les 10 dernières lignes d'un flux de données. Cette commande est souvent utilisée pour afficher les dernières lignes d'un fichier de log.

Syntaxe

tail [options] [fichiers ...]
Principales fonctions :
-f Affiche en temps réel les ajouts en fin de fichier
-n Affiche les n dernières lignes indiquées
-n +x Affiche à partir de la ligne x jusqu'à la fin du fichier

Exemples

Afficher les 5 dernières lignes du fichier /var/log/messages

$ tail -n 5 /var/log/messages # Utilisation de -n 5
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$ tail -5 /var/log/messages # Identique mais en indiquant seulement -5
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$

Afficher à partir de la ligne 9

$ tail -n +9 /var/log/messages
May 18 12:21:34 kernel: Kernel logging (proc) stopped.
May 18 12:21:34 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] exiting on signal 15.
May 18 14:09:12 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
$

Afficher les dernières lignes d'un fichier en temps réel

$ tail -f /var/log/messages
May 18 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 18 12:21:15 shutdown[10971]: shutting down for system halt
May 18 12:21:34 kernel: Kernel logging (proc) stopped.
May 18 12:21:34 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] exiting on signal 15.
May 18 14:09:12 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 14:09:12 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="287" x-info="http://www.rsyslog.com"] (re)start
May 18 15:26:36 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 18 15:26:36 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
May 19 06:18:39 kernel: imklog 4.6.4, log source = /proc/kmsg started.
May 19 06:18:39 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="285" x-info="http://www.rsyslog.com"] (re)start
^C # Pour interrompre la commande
$

Etiquettes: 

Premières lignes d'un flux de données : head

La commande head permet de visualiser, par défaut, les 10 premières lignes d'un flux de données.

Syntaxe

head [-n] [fichiers ...]
Principales options :
-n Affiche les n premières lignes

Exemple

Afficher les 5 premières lignes du fichier /var/log/messages

$ head -5 /var/log/messages
May 13 04:54:06 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 14 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 15 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 15 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
May 16 04:54:03 rsyslogd: [origin software="rsyslogd" swVersion="4.6.4" x-pid="288" x-info="http://www.rsyslog.com"] rsyslogd was HUPed, type 'lightweight'.
$

Etiquettes: 

Duplication de la sortie standard : tee

La commande tee récupère un flux de données sur son entrée standard, l'envoie dans le fichier passé en argument et sur la sortie standard. Cela permet d'avoir à la fois le résultat à l'écran et dans un fichier.

Syntaxe

commande | tee [options] fichier
Principales options :
-a Ajoute à la fin de "fichier" si celui-ci existe déjà

Exemple

A partir du fichier /var/log/apache2/access.log, récupérer les lignes datant du 19/05/2012 en les affichant à l'écran et en les enregistrant dans un fichier

$ grep '19/May/2012' /var/log/apache2/access.log | tee 2012_05_19_log_apache
x.x.x.x - - [19/May/2012:14:49:03 +0200] "GET / HTTP/1.1" 200 7226 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /node.css?7 HTTP/1.1" 200 664 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /defaults.css?7 HTTP/1.1" 200 718 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
...
$

$ head -5 2012_05_19_log_apache
x.x.x.x - - [19/May/2012:14:49:03 +0200] "GET / HTTP/1.1" 200 7226 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /node.css?7 HTTP/1.1" 200 664 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:04 +0200] "GET /defaults.css?7 HTTP/1.1" 200 718 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:05 +0200] "GET /layout.css?7 HTTP/1.1" 200 1444 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
x.x.x.x - - [19/May/2012:14:49:05 +0200] "GET /style.css?7 HTTP/1.1" 200 7648 "http://quennec.fr/" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"
$

Etiquettes: 

Numérotation de lignes : nl

La commande nl permet d'afficher un fichier texte en numérotant les lignes. Par défaut, les lignes vides ne sont pas numérotées.

Syntaxe

nl [options] [fichiers ...]
Principales options :
-b a Numéroter toutes les lignes
-b t Ne pas numéroter les lignes vides

Exemple

$ nl fic7
     1  erytert
 
     2  e
     3  er
     4  ..
     5  uhy
 
     6  2
     7  e_
     8  34
     9  546
 
$

Numéroter toutes les lignes, mêmes vides

$ nl -ba fic7
     1  erytert
     2
     3  e
     4  er
     5  ..
     6  uhy
     7
     8  2
     9  e_
    10  34
    11  546
    12
$

Etiquettes: 

Présentation d'un flux de données : pr

La commande pr affiche un texte de manière formatée. Par défaut, chaque page fait 66 lignes, dont 5 lignes d'en-tête et 5 ligne de pied de page.

Syntaxe

pr [options] [fichiers ...]
Quelques options :
-c nb Disposition en nb colonnes
-h texte Ecrire un  titre en en-tête
-l nb Nombre de lignes d'une page

Exemples

$ cat depts2012
CP : 01053      DEPT : AIN
CP : 02408      DEPT : AISNE
CP : 03190      DEPT : ALLIER
CP : 04070      DEPT : ALPES-DE-HAUTE-PROVENCE
CP : 05061      DEPT : HAUTES-ALPES
CP : 06088      DEPT : ALPES-MARITIMES
CP : 07186      DEPT : ARDECHE
CP : 08105      DEPT : ARDENNES
CP : 09122      DEPT : ARIEGE
CP : 10387      DEPT : AUBE
CP : 11069      DEPT : AUDE
CP : 12202      DEPT : AVEYRON
CP : 13055      DEPT : BOUCHES-DU-RHONE
CP : 14118      DEPT : CALVADOS
CP : 15014      DEPT : CANTAL
CP : 16015      DEPT : CHARENTE
CP : 17300      DEPT : CHARENTE-MARITIME
CP : 18033      DEPT : CHER
CP : 19272      DEPT : CORREZE
CP : 21231      DEPT : COTE-D'OR
CP : 22278      DEPT : COTES-D'ARMOR
CP : 23096      DEPT : CREUSE
CP : 24322      DEPT : DORDOGNE
CP : 25056      DEPT : DOUBS
CP : 26362      DEPT : DROME
CP : 27229      DEPT : EURE
CP : 28085      DEPT : EURE-ET-LOIR
CP : 29232      DEPT : FINISTERE
CP : 30189      DEPT : GARD
CP : 31555      DEPT : HAUTE-GARONNE
CP : 32013      DEPT : GERS
CP : 33063      DEPT : GIRONDE
CP : 34172      DEPT : HERAULT
CP : 35238      DEPT : ILLE-ET-VILAINE
CP : 36044      DEPT : INDRE
CP : 37261      DEPT : INDRE-ET-LOIRE
CP : 38185      DEPT : ISERE
CP : 39300      DEPT : JURA
CP : 40192      DEPT : LANDES
CP : 41018      DEPT : LOIR-ET-CHER
CP : 42218      DEPT : LOIRE
CP : 43157      DEPT : HAUTE-LOIRE
CP : 44109      DEPT : LOIRE-ATLANTIQUE
CP : 45234      DEPT : LOIRET
CP : 46042      DEPT : LOT
CP : 47001      DEPT : LOT-ET-GARONNE
CP : 48095      DEPT : LOZERE
CP : 49007      DEPT : MAINE-ET-LOIRE
CP : 50502      DEPT : MANCHE
CP : 51108      DEPT : MARNE
CP : 52121      DEPT : HAUTE-MARNE
CP : 53130      DEPT : MAYENNE
CP : 54395      DEPT : MEURTHE-ET-MOSELLE
CP : 55029      DEPT : MEUSE
CP : 56260      DEPT : MORBIHAN
CP : 57463      DEPT : MOSELLE
CP : 58194      DEPT : NIEVRE
CP : 59350      DEPT : NORD
CP : 60057      DEPT : OISE
CP : 61001      DEPT : ORNE
CP : 62041      DEPT : PAS-DE-CALAIS
CP : 63113      DEPT : PUY-DE-DOME
CP : 64445      DEPT : PYRENEES-ATLANTIQUES
CP : 65440      DEPT : HAUTES-PYRENEES
CP : 66136      DEPT : PYRENEES-ORIENTALES
CP : 67482      DEPT : BAS-RHIN
CP : 68066      DEPT : HAUT-RHIN
CP : 69123      DEPT : RHONE
CP : 70550      DEPT : HAUTE-SAONE
CP : 71270      DEPT : SAONE-ET-LOIRE
CP : 72181      DEPT : SARTHE
CP : 73065      DEPT : SAVOIE
CP : 74010      DEPT : HAUTE-SAVOIE
CP : 75056      DEPT : PARIS
CP : 76540      DEPT : SEINE-MARITIME
CP : 77288      DEPT : SEINE-ET-MARNE
CP : 78646      DEPT : YVELINES
CP : 79191      DEPT : DEUX-SEVRES
CP : 80021      DEPT : SOMME
CP : 81004      DEPT : TARN
CP : 82121      DEPT : TARN-ET-GARONNE
CP : 83137      DEPT : VAR
CP : 84007      DEPT : VAUCLUSE
CP : 85191      DEPT : VENDEE
CP : 86194      DEPT : VIENNE
CP : 87085      DEPT : HAUTE-VIENNE
CP : 88160      DEPT : VOSGES
CP : 89024      DEPT : YONNE
CP : 90010      DEPT : TERRITOIRE_DE_BELFORT
CP : 91228      DEPT : ESSONNE
CP : 92050      DEPT : HAUTS-DE-SEINE
CP : 93008      DEPT : SEINE-SAINT-DENIS
CP : 94028      DEPT : VAL-DE-MARNE
CP : 95500      DEPT : VAL-D'OISE
CP : 97105      DEPT : GUADELOUPE
CP : 97209      DEPT : MARTINIQUE
CP : 97302      DEPT : GUYANE
CP : 97411      DEPT : LA_REUNION
CP : 97608      DEPT : MAYOTTE
$

$ pr depts2012 | more
 
 
2012-05-19 17:19                    depts2012                     Page 1
 
 
CP : 01053      DEPT : AIN
CP : 02408      DEPT : AISNE
CP : 03190      DEPT : ALLIER
CP : 04070      DEPT : ALPES-DE-HAUTE-PROVENCE
CP : 05061      DEPT : HAUTES-ALPES
CP : 06088      DEPT : ALPES-MARITIMES
CP : 07186      DEPT : ARDECHE
CP : 08105      DEPT : ARDENNES
CP : 09122      DEPT : ARIEGE
CP : 10387      DEPT : AUBE
CP : 11069      DEPT : AUDE
CP : 12202      DEPT : AVEYRON
CP : 13055      DEPT : BOUCHES-DU-RHONE
CP : 14118      DEPT : CALVADOS
CP : 15014      DEPT : CANTAL
CP : 16015      DEPT : CHARENTE
CP : 17300      DEPT : CHARENTE-MARITIME
CP : 18033      DEPT : CHER
CP : 19272      DEPT : CORREZE
...
$

Présentation du fichier en 2 colonnes de 15 lignes par page avec un titre sur chaque page

$ pr -l 15 -c2 -h "Liste des departements" depts2012 | more
 
 
2012-05-19 17:19              Liste des departements              Page 1
 
 
CP : 01053      DEPT : AIN          CP : 06088  DEPT : ALPES-MARITI
CP : 02408      DEPT : AISNE        CP : 07186  DEPT : ARDECHE
CP : 03190      DEPT : ALLIER       CP : 08105  DEPT : ARDENNES
CP : 04070      DEPT : ALPES-DE-HAU CP : 09122  DEPT : ARIEGE
CP : 05061      DEPT : HAUTES-ALPES CP : 10387  DEPT : AUBE
 
 
 
 
 
 
 
2012-05-19 17:19              Liste des departements              Page 2
 
 
CP : 11069      DEPT : AUDE         CP : 16015  DEPT : CHARENTE
CP : 12202      DEPT : AVEYRON      CP : 17300  DEPT : CHARENTE-MAR
CP : 13055      DEPT : BOUCHES-DU-R CP : 18033  DEPT : CHER
CP : 14118      DEPT : CALVADOS     CP : 19272  DEPT : CORREZE
CP : 15014      DEPT : CANTAL       CP : 21231  DEPT : COTE-D'OR
...
$

Etiquettes: 

Traitement de données

Comptage de lignes, de mots et de caractères : wc

La commande wc compte le nombre de lignes, de mots et de caractères.

Syntaxe

wc [OPTION]... [FILE]...
Principales options :
-l Compter le nombre de lignes
-w Compter le nombre de mots
-c Compter le nombre d'octets
-m Compter le nombre de caractères

Exemples

Nombre de lignes, mots et caractères du fichier depts2012

$ wc depts2012
  99  594 2769 depts2012
$

Nombre de lignes uniquement

$ wc -l depts2012
99 depts2012
$

Nombre de mots uniquement

$ wc -w depts2012
594 depts2012
$

Nombre de caractères uniquement

$ wc -m depts2012
2769 depts2012
$

Nombre de caractères contenus dans un texte saisi au clavier (attention au saut de ligne ajouté par la commande echo)

$ read texte
ceci est un texte
$ echo "$texte" | wc -m
18     # Le saut de ligne est comptabilisé
$ echo -e "$texte\c" | wc -m
17     # Avec l'option -e et \c le saut de ligne n'est pas comptabilisé
$ echo -n "$texte" | wc -m
17     # Idem mais avec l'option -n
$

Etiquettes: 

Extraction de caractères : cut

La commande cut sert à récupérer des caractères ou des champs d'une ligne.

Syntaxe

Couper par caractères

cut -c3 [fichiers ...] Le 3ème caractère
cut -c3-5 [fichiers ...] Du 3ème au 5ème caractère
cut -c-3 [fichiers ...] Jusqu'au 3ème caractère
cut -c3- [fichiers ...] A partir du 3ème caractère
cut -c3,10 [fichiers ...] Le 3ème et le 10ème caractère

Couper par champs

cut -dsep -f3 [fichiers ...] Le 3ème champ
cut -dsep -f3-5 [fichiers ...] Du 3ème au 5ème champ
cut -dsep -f-3 [fichiers ...] Jusqu'au 3ème champ
cut -dsep -f3- [fichiers ...] A partir du 3ème champ
cut -dsep -f3,10 [fichiers ...] Le 3ème et le 10ème champ

L'option -d permet d'exprimer le caractère séparateur de champ. Le caractère séparateur par défaut est la tabulation.

Exemples

Couper les 2 Premiers chiffres d'un code postal

$ echo 44150 | cut -c1-2
44
$

Afficher le 1er, 6ème et 7ème champs des 5 dernières lignes du fichier /etc/passwd

$ tail -5 /etc/passwd | cut -d: -f1,6,7
nobody:/nonexistent:/bin/sh
libuuid:/var/lib/libuuid:/bin/sh
postfix:/var/spool/postfix:/bin/false
sshd:/var/run/sshd:/usr/sbin/nologin
mysql:/var/lib/mysql:/bin/false
$

Si le caractère séparateur est un caractère spécial du shell, il faut le protéger.

$ ligne="val1|val2|val3"
$ echo $ligne | cut -d'|' -f1
val1
$

Etiquettes: 

Tri de données : sort

La commande sort permet de trier les lignes d'un flux de données textes.

Syntaxe

sort [options] -k champs[.car] [options], champ[.car] [options] [fichiers ...]
Principales options :
-b : Option à utiliser lorsque le critère de tri est alphanumérique et que les valeurs dans les champs sont calés à droite et complétés avec des espaces à gauche
-k : champ [.car] [options] , champ [.car] [options] Spécifier le ou les champs à prendre en compte en tant que critère de tri. Le critère de tri peut commencer ou finir à une certaine position de caractère du champ.
-n : Préciser que le critère de tri doit être traité comme valeur numérique et non comme chaine de caractères.
-r : Demander un tri décroissant
-t sep : Préciser le caractère séparateur de champs (espace par défaut)
-u : Suppression des doublons

 

Exemples

Utilisation du fichier /etc/passwd

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
$

Trier le fichier sans aucune option.
Le fichier est trié en fonction de la première lettre de chaque ligne.

$ sort /etc/passwd
backup:x:34:34:backup:/var/backups:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
news:x:9:9:news:/var/spool/news:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
root:x:0:0:root:/root:/bin/bash
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
sys:x:3:3:sys:/dev:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
$

Trier le fichier sur le 3ème champ (valeurs numériques).
Le tri est alphanumérique.

$ sort -t: -k 3,3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
proxy:x:13:13:proxy:/bin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
$

Pour que le tri soit numérique il faut ajouter l'option -n

$ sort -t: -k 3,3 -n /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
postfix:x:101:104::/var/spool/postfix:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:103:108:MySQL Server,,,:/var/lib/mysql:/bin/false
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
$

Exemple de tri sur un fichier dont le champ servant de critère de tri est alphanumérique et complété avec des espaces à gauche.

$ cat listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
$

Tri sur le champ 9. Valeurs alphanumériques complétées avec des espaces à gauche. Sans l'option -b, le tri est mal réalisé.

$ sort -k 9,9 listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
$

Idem mais avec l'option -b

$ sort -b -k 9,9 listeFic
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 74 May 17 22:01   fic6.txt
-rw-r--r-- 1 root root 35 May 19 15:02       fic7
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
$

Utilisation de 2 critères de tri: Tri sur le champ 7 (numérique) et sur le champ 9 (alphanumérique)

$ sort -k 7n,7 -k 9b,9 listeFic
-rw-r--r-- 1 root root 74 May 15 22:01   fic6.txt
-rw-r--r-- 1 root root 35 May 15 15:02       fic7
-rw-r--r-- 1 root root 17 May 17 21:31       fic1
-rw-r--r-- 1 root root 23 May 17 21:33       fic2
-rw-r--r-- 1 root root 40 May 17 21:33       fic3
-rw-r--r-- 1 root root 17 May 17 21:36       fic4
-rw-r--r-- 1 root root 36 May 17 21:52       fic5
-rw-r--r-- 1 root root 48 May 19 17:59       fic8
$

Trier un fichier suivant les mois de l'année

$ cat fic91
April
August
December
February
January
July
June
March
May
November
October
September
$

$ sort -M fic91
January
February
March
April
May
June
July
August
September
October
November
December
$

Etiquettes: 

paste

La commande paste concatène les lignes de même niveau des fichiers passés en argument. Avec l'option -s, chaque fichier est traité de manière indépendante et verra toutes ses lignes concaténées en une seule. Les éléments concaténés sont séparés par une tabulation.

Syntaxe

paste [options] [fichiers ...]
Principales options :
-s : Concaténer toutes les lignes en une seule
-d listesep : Les caractères cités dans listesep seront utilisés pour séparer les champs en sortie

Exemple

Exemple avec 2 fichiers contenant les dépenses d'un ménage pour le mois de janvier 2012 et février 2012. Les catégories sont mentionnées dans le même ordre.

$ cat janvier2012
Alimentation    50.00
Eau             25.00
Electricite     123.50
Loyer           456.90
Assurances      234.00
 
$ cat fevrier2012
Alimentation    67.00
Eau             34.00
Electricite     156.00
Loyer           456.90
Assurances      225.00
$

Mettre sur la même ligne les montants de chaque catégorie.

$ paste janvier2012 fevrier2012
Alimentation    50.00   Alimentation    67.00
Eau             25.00   Eau             34.00
Electricite     123.50  Electricite     156.00
Loyer           456.90  Loyer           456.90
Assurances      234.00  Assurances      225.00
$

Retirer la 3ème colonne et ajouter des en-têtes de colonnes et le total pour chaque mois.

$ paste janvier2012 fevrier2012 | awk 'BEGIN{printf("%-15s\t%7s\t%7s\n","POSTE","JANVIER","FEVRIER")}{printf("%-15s\t%7.2f\t%7.2f\n",$1,$2,$4);jan+=$2;fev+=$4}END{printf("%-15s\t%7.2f\t%7.2f\n","TOTAL",jan,fev)}'
POSTE           JANVIER FEVRIER
Alimentation      50.00   67.00
Eau               25.00   34.00
Electricite      123.50  156.00
Loyer            456.90  456.90
Assurances       234.00  225.00
TOTAL            889.40  938.90
$

Concaténer toutes les lignes en une seule.

$ paste -s janvier2012 fevrier2012
Alimentation    50.00   Eau             25.00   Electricite     123.50  Loyer           456.90  Assurances      234.00
Alimentation    67.00   Eau             34.00   Electricite     156.00  Loyer           456.90  Assurances      225.00
$

Etiquettes: 

split

La commande split permet d'éclater un fichier en fragments. Chaque fragment étant stocké dans des fichiers nommés PREFIXEaa, PREFIXEab, ... PREFIXE ayant pour valeur par défaut "x". Si aucun nom de fichier n'est spécifié, l'entrée standard est utilisée.

Syntaxe

split [options] [fichiers ... [prefixe]]
Principales options :
-b nb : Le fichier est éclaté tous les "nb" octets
-l nb : Le fichier est éclaté toutes les "nb" lignes

Exemples

Exemple avec un fichier contenant 9 lignes

$ cat fic10
ligne1
ligne2
ligne3
ligne4
ligne5
ligne6
ligne7
ligne8
ligne9
$

Eclater le fichier en différents fichiers de 3 lignes chacuns. Par défaut, chaque fichier généré sera préfixé par la lettre "x".

$ split -l3 fic10
$ ls -l x*
-rw-r--r-- 1 root root 21 May 23 08:56 xaa
-rw-r--r-- 1 root root 21 May 23 08:56 xab
-rw-r--r-- 1 root root 21 May 23 08:56 xac
$ cat xaa
ligne1
ligne2
ligne3
$ cat xab
ligne4
ligne5
ligne6
$ cat xac
ligne7
ligne8
ligne9
$

Modifier le préfixe des fichiers générés.

$ split -l3 fic10 fichier
$ ls -l fichier*
-rw-r--r-- 1 root root 21 May 23 08:58 fichieraa
-rw-r--r-- 1 root root 21 May 23 08:58 fichierab
-rw-r--r-- 1 root root 21 May 23 08:58 fichierac
$

Etiquettes: 

Transformation de caractères : tr

La commande tr permet d'appliquer un traitement sur certains caractères d'un flux de données : suppression, substitutions ... Cette commande exploite uniquement les données arrivant sur l'entrée standard.

Syntaxe

tr [options] ensemble1 [ensemble2]
Principale option :
[-d caractères] : La liste des caractères qui doivent être supprimés du flux de données.

Substitution de caractères

Deux ensembles de caractères doivent être spécifiés. Chaque ensemble comporte le même nombre de caractères. Tout caractère de ensemble1 trouvé dans le flux de données est substitué par le caractère de même position situé dans ensemble2.

Exemples

La chaine abcd est traitée : chaque caractère b est remplacé par 2 et chaque caractère d est remplacé par 4.

$ echo abcd | tr bd 24
a2c4
$

Il est possible d'utiliser la notion d'intervalles de caractères en utilisant des crochets. Ici chaque minuscule est remplacée par une majuscule correspondante :

$ echo abcd | tr "[a-z]" "[A-Z]"
ABCD
$

Pour traiter un fichier, il faut utiliser la redirection :

$ cat fic1
ceci est un test
$ tr "[a-z]" "[A-Z]" < fic1
CECI EST UN TEST
$

Suppression de caractères

L'option -d permet de supprimer certains caractères du flux de données.

Exemples

Transformation d'un fichier au format DOS (fin de lignes "\r\n") en un format UNIX ("\n"). Retrait du caractère "\r" :

$ od -c fic100
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \r  \n
0000022
$ tr -d '\r' < fic100 > fic101
$ od -c fic101
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \n
0000021
$

En ajoutant l'option -c à l'option -d, la commande supprime tous les caractères non présents dans la liste passée en argument :

$ echo "Linux Pratique HS N°26 – Février/Mars 2013 – En kiosque" | tr -dc a-zA-Z; echo
LinuxPratiqueHSNFvrierMarsEnkiosque

La dernière commande echo permet uniquement d'afficher un retour à la ligne.

Etiquettes: 

Dédoublonnage : uniq

La commande uniq permet de dédoublonner les lignes d'un fichier. Seules les lignes identiques consécutives sont traitées. Elle s'utilise le plus souvent à la suite de la commande sort. Le résultat est stocké dans un fichier de sortie, si celui-ci est spécifié, sur la sortie standard dans le cas contraire.

Syntaxe

uniq [options] [fichier_entree [fichier_sortie]]
Principales options :
-d : Affichage des doublons
-c : Comptage des doublons

Exemples

Le fichier fic20 contient des lignes en double

$ cat fic20
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un autre test
ceci est un test
ceci est un autre test
ceci    est     un      test
ceci est un     test
et un   autre test
ceci est un fichier
$

Le fichier doit être trié pour que les lignes identiques soient consécutives :

$ sort fic20
ceci est un autre test
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Et enfin, suppression des doublons :

$ sort fic20 | uniq
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Résultat identique avec la commande sort et l'option -u :

$ sort -u fic20
ceci est un autre test
ceci est un fichier
ceci est un test
ceci est un     test
ceci    est     un      test
et un   autre test
$

Afficher devant chaque ligne son nombre d'occurences dans le fichier :

$ sort fic20 | uniq -c
      2 ceci est un autre test
      1 ceci est un fichier
      5 ceci est un test
      1 ceci est un     test
      1 ceci    est     un      test
      1 et un   autre test
$

Afficher uniquement les lignes ayant des doublons :

$ sort fic20 | uniq -d
ceci est un autre test
ceci est un test
$

Combinées les options -c et -d :

$ sort fic20 | uniq -cd
      2 ceci est un autre test
      5 ceci est un test
$

Afficher les lignes uniques :

$ sort fic20 | uniq -u
ceci est un fichier
ceci est un     test
ceci    est     un      test
et un   autre test
$ sort fic20 | uniq -cu
      1 ceci est un fichier
      1 ceci est un     test
      1 ceci    est     un      test
      1 et un   autre test
$

Etiquettes: 

Compressions, archivages et conversions

Compressions : gzip, bzip2

Les commandes gzip/gunzip, bzip2/bunzip2 compressent/décompressent chaque fichier passé en argument sur la ligne de commande. La commande bzip2 possède un meilleur taux de compression. Ces commandes se trouvent en standard sur les systèmes Linux et peuvent s'installer sur les systèmes UNIX si elles ne sont pas déjà présentes. La commande gzip produit l'extension .gz et la commande bzip2 produit l'extension .bz2.

Syntaxe de gzip

Compression :
gzip [options] [fichiers ...]
Décompression :
gunzip [options] [fichiers ...]
zcat [options] [fichiers ...]
Principales options :
-c : Envoie le résultat sur la sortie standard. La commande gunzip -c est équivalente à la commande zcat.
-t : Tester la validité de l'archive.

Si le nom de fichier est omis, les données à traiter sont lues sur l'entrée standard.

Exemple

Compression du fichier mon_fichier4. L'original est remplacé par le fichier compressé.

$ ls -lh mon_fichier4
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
$ gzip mon_fichier4
$ ls -lh mon_fichier4.gz
-rw-r--r-- 1 root root 2,1M 25 mai   16:36 mon_fichier4.gz
$

Décompression. Le fichier d'origine est restitué et remplace le fichier compressé.

$ gunzip mon_fichier4.gz
$ ls -lh mon_fichier4*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
$

Compression du fichier avec envoi du flux sur la sortie standard. Cette méthode permet de conserver le fichier d'origine.

$ gzip -c mon_fichier4 > mon_fichier4.gz
$ ls -lh mon_fichier4*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
$

Décompressé en conservant le fichier compressé.

$ gunzip -c mon_fichier4.gz > mon_fichier5
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
$

Idem mais avec la commande zcat.

$ zcat mon_fichier4.gz > mon_fichier6
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$

Les commandes bzip2, bunzip2 et bzcat fonctionnent de manière similaire avec une légère différence au niveau des options.

Syntaxe de bzip2

Compression :
bzip2 [options] [fichiers ...]
Décompression :
bunzip2 [fichiers ...]
bzip2 -d
[options] [fichiers ...]
bzcat [options] [fichiers ...]
Principales options :
-c : Envoie le résultat sur la sortie standard. La commande bzip2 -dc est équivalente à la commande bzcat.
-d : Décompression
-t : Tester la validité de l'archive.

Exemple

Compression du fichier mon_fichier4. La compression avec bzip2 est meilleure qu'avec gzip.

$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$ bzip2 -c mon_fichier4 > mon_fichier4.bz2
$ ls -lh mon_fichier*
-rw-r--r-- 1 root root 6,6M 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 1,2M 25 mai   16:54 mon_fichier4.bz2
-rw-r--r-- 1 root root 2,1M 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6,6M 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6,6M 25 mai   16:46 mon_fichier6
$

Analyse des différentes compressions

$ wc -c mon_fichier4
6903921 mon_fichier4
$ wc -m mon_fichier4
6903921 mon_fichier4
$ ls -l mon_fichier*
-rw-r--r-- 1 root root 6903921 25 mai   16:36 mon_fichier4
-rw-r--r-- 1 root root 1207370 25 mai   16:54 mon_fichier4.bz2
-rw-r--r-- 1 root root 2129319 25 mai   16:42 mon_fichier4.gz
-rw-r--r-- 1 root root 6903921 25 mai   16:44 mon_fichier5
-rw-r--r-- 1 root root 6903921 25 mai   16:46 mon_fichier6
$

Test réalisé avec un fichier contenant uniquement des caractères.
Le fichier mon_fichier4 contient 6 903 921 caractères et par conséquent une taille de 6 903 921 octets.

Compression avec gzip : Le fichier a été réduit à une taille de 2 129 319 octets. Ce qui représente une diminution de 69,16 %

Compression avec bzip2 : Le fichier a été réduit à une taille de 1 207 370 octets. Ce qui représente une diminution de 82,51%

Etiquettes: 

Archives tar

La commande tar permet de créer une archive à partir d'un ou plusieurs fichiers. Si le fichier est un répertoire, il sera archivé avec sa sous-arborescence.

Syntaxe

Création d'une archive :
tar -c [-zv] -f archive fichiers_a_archiver ...
Vérification d'une archive :
tar -t [-zv] -f archive [fichiers_a_verifier ...]
Extraction d'une archive :
tar -x [-zv] -f archive [fichiers_a_extraire ...]
Principales options :
-c : Création d'une archive
-t : Vérification d'une archive
-x : Extraction d'une archive
-f archive.tar : Nom de l'archive à créer, vérifier ou extraire
-f - : Dans le cas d'une création, le contenu de l'archive est envoyé sur la sortie standard. En extraction et vérification, le contenu de l'archive est attendu sur l'entrée standard
-v : Mode verbeux
-z : Sous Linux uniquement : permet de gérer en plus la compression
avec gzip
-j : Compression avec bzip2

Exemples

Archivage et compression du dossier dossier_a_sauvegarder. La commande tar envoi le contenu de l'archive à la commande gzip via un pipe et qui compresse l'archive dans un fichier dossier_a_sauvegarder.tar.gz

$ ls -R dossier_a_sauvegarder
dossier_a_sauvegarder:
aZerty  Bonjour  Coucou  help  history  last  monscript  pays2011.txt.1  piwik1  piwik2  result  ronan  ronan2  ronan3  search  test
$ tar -cvf - dossier_a_sauvegarder | gzip -c > dossier_a_sauvegarder.tar.gz
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$ ls -l dossier_a_sauvegarder.tar.gz
-rw-r--r-- 1 root root 369562 25 mai   17:30 dossier_a_sauvegarder.tar.gz
$

Décompression et extraction. La commande gunzip décompresse le fichier et envoie le résultat à la commande tar qui extrait tout le contenu de l'archive.

$ gunzip -c dossier_a_sauvegarder.tar.gz | tar -xvf -
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Lister le contenu d'une archive

$ gunzip -c dossier_a_sauvegarder.tar.gz | tar -tf -
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Archivage et compression en utilisant l'option -z

$ tar -cvzf monArchive.tar.gz dossier_a_sauvegarder
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Idem pour lister le contenu de l'archive. En fait, l'option -z n'est pas obligatoire. Que l'archive soit compressée ou pas les options -tf suffisent.

$ tar -tf monArchive.tar.gz
dossier_a_sauvegarder/
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
$

Ajouter un fichier à une archive existante en 3 étapes.

1 - Décompression (un fichier ne peut être ajouté à une archive compressée

$ gunzip monArchive.tar.gz

2 - Ajout du fichier xam à l'archive

$ tar -rf monArchive.tar xam

3 - Compression de l'archive

$ gzip monArchive.tar

Vérifier les différences entre le contenu de l'archive tar et les fichiers sources. Mettre à jour si nécessaire.

1 - Décompression de l'archive

$ gunzip monArchive.tar.gz

2 - Comparaison de l'archive avec les fichiers sources et affichage des différences existantes

$ tar -df monArchive.tar
dossier_a_sauvegarder/aZerty: La date de modification est différente.
dossier_a_sauvegarder/aZerty: La taille est différente.
$

3 - Suppresion du fichier ou des fichiers ayant des différences

$ tar --delete -f monArchive.tar dossier_a_sauvegarder/aZerty

4 - Mise à jour des fichiers en cas de différences trouvées.

$ tar -uf monArchive.tar dossier_a_sauvegarder

5 - Compression de l'archive

$ gzip monArchive.tar

Etiquettes: 

Archives cpio

La commande cpio permet également de faire des archives. Contrairement à la commande tar, elle n'est pas récursive. Tous les noms de fichiers entrant dans la composition de l'archive doivent être lus sur l'entrée standard. L'archive est envoyée sur la sortie standard. La commande est presque toujours utilisée derrière un tube lui-même précédé de la commande find qui permet de générer les noms de fichiers souhaités.

En vérification et restauration, l'archive à traiter est attendue sur l'entrée standard.

Syntaxe

Création d'une archive :
find . | cpio -o [-cv] > archive
Vérification d'une archive :
cpio -it [-cv] < archive [fichiers_a_verifier ...]
Extraction d'une archive :
cpio -i [-cdv] < archive [fichiers_a_extraire ...]
Principales options :
-c : Création/vérification/extraction d'une archive ayant un format d'en-tête d'archive portable entre les systèmes UNIX
-d : Extraction avec création des répertoires s'ils n'existent pas
-i : Extraction d'une archive
-it : Vérification d'une archive
-o : Création d'une archive
-v : Mode verbeux

Exemples

Archivage du dossier dossier_a_sauvegarder

$ find dossier_a_sauvegarder
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
 
$ find dossier_a_sauvegarder | cpio -ocv > monArchive.cpio
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$

Vérification de l'archive

$ cpio -icvt < monArchive.cpio
drwxr-xr-x   2 root     root            0 Apr 17 13:15 dossier_a_sauvegarder
-rw-r--r--   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan2
-rw-r--r--   1 root     root      3171822 Apr 17 08:46 dossier_a_sauvegarder/last
-rw-r--r--   1 root     root       195732 Apr 17 08:46 dossier_a_sauvegarder/search
-rw-r--r--   1 root     root          622 Apr 17 08:46 dossier_a_sauvegarder/piwik2
-rw-r--r--   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan3
-rw-r--r--   1 root     root          116 Apr 17 08:46 dossier_a_sauvegarder/result
-rw-r--r--   1 root     root         3083 Apr 17 08:46 dossier_a_sauvegarder/help
-rw-r--r--   1 root     root        26000 Apr 17 08:46 dossier_a_sauvegarder/history
-rw-r--r--   1 root     root            7 Apr 17 08:46 dossier_a_sauvegarder/Coucou
-rw-r--r--   1 root     root        15667 Apr 17 08:46 dossier_a_sauvegarder/pays2011.txt.1
-rw-r--r--   1 root     root           10 Apr 17 08:46 dossier_a_sauvegarder/monscript
-rw-------   1 root     root          281 Apr 17 08:46 dossier_a_sauvegarder/ronan
-rw-r--r--   1 root     root          387 Apr 17 08:46 dossier_a_sauvegarder/test
-rw-r--r--   1 root     root            0 Apr 17 08:46 dossier_a_sauvegarder/Bonjour
-rw-r--r--   1 root     root          622 Apr 17 08:46 dossier_a_sauvegarder/piwik1
-rw-r--r--   1 root     root           36 May 25 18:11 dossier_a_sauvegarder/aZerty
6674 blocs
$

Création d'une archive cpio compressée avec bzip2

$ find dossier_a_sauvegarder | cpio -ocv | bzip2 -c > monArchive.cpio.bz2
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$

Décompression et extraction de l'archive

$ mkdir tmp
$ cp monArchive.cpio.bz2 tmp
$ cd tmp
$ ll
total 276
drwxr-xr-x  2 root root   4096 26 mai   08:59 .
drwx------ 12 root root   4096 26 mai   08:58 ..
-rw-r--r--  1 root root 266276 26 mai   08:59 monArchive.cpio.bz2
$ bzip2 -dc monArchive.cpio.bz2 | cpio -icvd
dossier_a_sauvegarder
dossier_a_sauvegarder/ronan2
dossier_a_sauvegarder/last
dossier_a_sauvegarder/search
dossier_a_sauvegarder/piwik2
dossier_a_sauvegarder/ronan3
dossier_a_sauvegarder/result
dossier_a_sauvegarder/help
dossier_a_sauvegarder/history
dossier_a_sauvegarder/Coucou
dossier_a_sauvegarder/pays2011.txt.1
dossier_a_sauvegarder/monscript
dossier_a_sauvegarder/ronan
dossier_a_sauvegarder/test
dossier_a_sauvegarder/Bonjour
dossier_a_sauvegarder/piwik1
dossier_a_sauvegarder/aZerty
6674 blocs
$ ll
total 280
drwxr-xr-x  3 root root   4096 26 mai   08:59 .
drwx------ 12 root root   4096 26 mai   08:58 ..
drwxr-xr-x  2 root root   4096 26 mai   08:59 dossier_a_sauvegarder
-rw-r--r--  1 root root 266276 26 mai   08:59 monArchive.cpio.bz2
$ ll dossier_a_sauvegarder/
total 3396
drwxr-xr-x 2 root root    4096 26 mai   08:59 .
drwxr-xr-x 3 root root    4096 26 mai   08:59 ..
-rw-r--r-- 1 root root      36 26 mai   08:59 aZerty
-rw-r--r-- 1 root root       0 26 mai   08:59 Bonjour
-rw-r--r-- 1 root root       7 26 mai   08:59 Coucou
-rw-r--r-- 1 root root    3083 26 mai   08:59 help
-rw-r--r-- 1 root root   26000 26 mai   08:59 history
-rw-r--r-- 1 root root 3171822 26 mai   08:59 last
-rw-r--r-- 1 root root      10 26 mai   08:59 monscript
-rw-r--r-- 1 root root   15667 26 mai   08:59 pays2011.txt.1
-rw-r--r-- 1 root root     622 26 mai   08:59 piwik1
-rw-r--r-- 1 root root     622 26 mai   08:59 piwik2
-rw-r--r-- 1 root root     116 26 mai   08:59 result
-rw------- 1 root root     281 26 mai   08:59 ronan
-rw-r--r-- 1 root root     281 26 mai   08:59 ronan2
-rw-r--r-- 1 root root     281 26 mai   08:59 ronan3
-rw-r--r-- 1 root root  195732 26 mai   08:59 search
-rw-r--r-- 1 root root     387 26 mai   08:59 test
$

Etiquettes: 

Copie physique, transformations : dd

La commande dd réalise une copie physique d'un flux d'entrée vers un flux de sortie. Chaque octet souhaité du flux d'entrée est copié vers le flux de sortie, indépendamment de ce que représente la donnée.

Syntaxe

dd [if=fichier_entree] [of=fichier_sortie] [ibs=nboctets] [obs=nboctets] [bs=nboctets] [skip=nbblocs] [seek=nbblocs] [count=nbblocs]
Principales options :
if=fichier_entree : Fichier à traiter. Si aucun fichier n'est spécifié, l'entrée standard est traité.
of=fichier_sortie : Fichier de sortie. Si aucun fichier n'est spécifié, la sortie standard est utilisée.
ibs=nboctets : Traiter les données en entrée par blocs de nboctets octets (défaut : 512 octets).
obs=nboctets : Ecrire les données en sortie par blocs de nboctets octets (défaut : 512 octets).
bs=nboctets : Traiter les données d'entrée et de sortie par bloc de nboctets.
skip=nbblocs : Sauter nbblocs en entrée avant de commencer le traitement.
seek=nbblocs : En sortie, écrire à partir du bloc nbblocs.
count=nbblocs : Ecrire uniquement nbblocs de l'entrée vers la sortie.

Exemples

Copie du disque /dev/sda sur le disque /dev/sdb par blocs de 8ko

$ fdisk -l
 
Disque /dev/sda: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sda1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sda2             994        1045      407553    5  Etendue
/dev/sda5             994        1045      407552   82  Linux swap / Solaris
 
Disque /dev/sdb: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00000000
 
Le disque /dev/sdb ne contient pas une table de partition valide

$ dd if=/dev/sda of=/dev/sdb bs=8k
1048576+0 enregistrements lus
1048576+0 enregistrements écrits
8589934592 octets (8,6 GB) copiés, 859,097 s, 10,0 MB/s
$

$ fdisk -l
 
Disque /dev/sda: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sda1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sda2             994        1045      407553    5  Etendue
/dev/sda5             994        1045      407552   82  Linux swap / Solaris
 
Disque /dev/sdb: 8589 Mo, 8589934592 octets
255 têtes, 63 secteurs/piste, 1044 cylindres
Unités = cylindres de 16065 * 512 = 8225280 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0x00040afc
 
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sdb1   *           1         994     7977984   83  Linux
La partition 1 ne se termine pas sur une frontière de cylindre.
/dev/sdb2             994        1045      407553    5  Etendue
/dev/sdb5             994        1045      407552   82  Linux swap / Solaris

Idem mais sur un disque sur une machine distante

$ dd if=/dev/sda | ssh 192.168.1.19 dd of=/dev/sdb bs=8k

Supprimer les 5 premiers octets du fichier fic1

$ cat fic1
ceci est un test
$ od -c fic1
0000000   c   e   c   i       e   s   t       u   n       t   e   s   t
0000020  \n
0000021
$ dd bs=1 skip=5 if=fic1 of=fic11
12+0 enregistrements lus
12+0 enregistrements écrits
12 octets (12 B) copiés, 0,00010661 s, 113 kB/s
$ cat fic11
est un test
$ od -c fic11
0000000   e   s   t       u   n       t   e   s   t  \n
0000014
$

Retirer le dernier octet d'un fichier de 6 903 921 octets. La taille du bloc est fixée à 6 903 920 octets. Le nombre de blocs traités est de 1

$ ls -l mon_fichier4
-rw-r--r-- 1 root root 6903921 25 mai   16:36 mon_fichier4
$ dd if=mon_fichier4 of=mon_fichier41 bs=6903920 count=1
1+0 enregistrements lus
1+0 enregistrements écrits
6903920 octets (6,9 MB) copiés, 0,183292 s, 37,7 MB/s
$ ls -l mon_fichier41
-rw-r--r-- 1 root root 6903920 26 mai   11:21 mon_fichier41
$

Etiquettes: 

Changement d'encodage : iconv

La commande iconv permet de transformer l'encodage d'un flux de données, typiquement de l'encodage UTF8 vers ISO8859-15 et inversement.

Syntaxe

iconv options [fichiers_a_traiter]
Principales options :
-f encodage_entree : Encodage du fichier d'entrée
-t encodage_sortie : Encodage du fichier de sortie

Exemples

Conversion du fichier fic1 (UTF-8) vers fic2 (ISO8859-15)

$ cat fic1
systèmes
$ od -c fic1
0000000   s   y   s   t 303 250   m   e   s  \n
0000012
$ file fic1
fic1: UTF-8 Unicode text
$ iconv -f UTF8 -t ISO885915 fic1 > fic2
$ cat fic2
syst▒mes
$ od -c fic2
0000000   s   y   s   t 350   m   e   s  \n
0000011
$ file fic2
fic2: ISO-8859 text
$

Afficher la liste complète de tous les jeux des codes de caractères connus.

$ iconv -l
La liste suivante contient tous les jeux des codes de caractères connus. Ceci
ne signifie pas nécessairement que toutes les combinaisons de ces noms peuvent
être utilisées dans les paramètres « FROM » et « TO » des commandes. Un jeu de codes de
caractères peut être affiché avec différents noms (aliases).
 
 437, 500, 500V1, 850, 851, 852, 855, 856, 857, 860, 861, 862, 863, 864, 865,
  866, 866NAV, 869, 874, 904, 1026, 1046, 1047, 8859_1, 8859_2, 8859_3, 8859_4,
  8859_5, 8859_6, 8859_7, 8859_8, 8859_9, 10646-1:1993, 10646-1:1993/UCS4,
  ANSI_X3.4-1968, ANSI_X3.4-1986, ANSI_X3.4, ANSI_X3.110-1983, ANSI_X3.110,
  ARABIC, ARABIC7, ARMSCII-8, ASCII, ASMO-708, ASMO_449, BALTIC, BIG-5,
  BIG-FIVE, BIG5-HKSCS, BIG5, BIG5HKSCS, BIGFIVE, BRF, BS_4730, CA, CN-BIG5,
  CN-GB, CN, CP-AR, CP-GR, CP-HU, CP037, CP038, CP273, CP274, CP275, CP278,
  CP280, CP281, CP282, CP284, CP285, CP290, CP297, CP367, CP420, CP423, CP424,
  CP437, CP500, CP737, CP775, CP803, CP813, CP819, CP850, CP851, CP852, CP855,
  CP856, CP857, CP860, CP861, CP862, CP863, CP864, CP865, CP866, CP866NAV,
  CP868, CP869, CP870, CP871, CP874, CP875, CP880, CP891, CP901, CP902, CP903,
  CP904, CP905, CP912, CP915, CP916, CP918, CP920, CP921, CP922, CP930, CP932,
  CP933, CP935, CP936, CP937, CP939, CP949, CP950, CP1004, CP1008, CP1025,
  CP1026, CP1046, CP1047, CP1070, CP1079, CP1081, CP1084, CP1089, CP1097,
  CP1112, CP1122, CP1123, CP1124, CP1125, CP1129, CP1130, CP1132, CP1133,
  CP1137, CP1140, CP1141, CP1142, CP1143, CP1144, CP1145, CP1146, CP1147,
  CP1148, CP1149, CP1153, CP1154, CP1155, CP1156, CP1157, CP1158, CP1160,
  CP1161, CP1162, CP1163, CP1164, CP1166, CP1167, CP1250, CP1251, CP1252,
  CP1253, CP1254, CP1255, CP1256, CP1257, CP1258, CP1282, CP1361, CP1364,
  CP1371, CP1388, CP1390, CP1399, CP4517, CP4899, CP4909, CP4971, CP5347,
  CP9030, CP9066, CP9448, CP10007, CP12712, CP16804, CPIBM861, CSA7-1, CSA7-2,
  CSASCII, CSA_T500-1983, CSA_T500, CSA_Z243.4-1985-1, CSA_Z243.4-1985-2,
  CSA_Z243.419851, CSA_Z243.419852, CSDECMCS, CSEBCDICATDE, CSEBCDICATDEA,
  CSEBCDICCAFR, CSEBCDICDKNO, CSEBCDICDKNOA, CSEBCDICES, CSEBCDICESA,
  CSEBCDICESS, CSEBCDICFISE, CSEBCDICFISEA, CSEBCDICFR, CSEBCDICIT, CSEBCDICPT,
  CSEBCDICUK, CSEBCDICUS, CSEUCKR, CSEUCPKDFMTJAPANESE, CSGB2312, CSHPROMAN8,
  CSIBM037, CSIBM038, CSIBM273, CSIBM274, CSIBM275, CSIBM277, CSIBM278,
  CSIBM280, CSIBM281, CSIBM284, CSIBM285, CSIBM290, CSIBM297, CSIBM420,
  CSIBM423, CSIBM424, CSIBM500, CSIBM803, CSIBM851, CSIBM855, CSIBM856,
  CSIBM857, CSIBM860, CSIBM863, CSIBM864, CSIBM865, CSIBM866, CSIBM868,
  CSIBM869, CSIBM870, CSIBM871, CSIBM880, CSIBM891, CSIBM901, CSIBM902,
  CSIBM903, CSIBM904, CSIBM905, CSIBM918, CSIBM921, CSIBM922, CSIBM930,
  CSIBM932, CSIBM933, CSIBM935, CSIBM937, CSIBM939, CSIBM943, CSIBM1008,
  CSIBM1025, CSIBM1026, CSIBM1097, CSIBM1112, CSIBM1122, CSIBM1123, CSIBM1124,
  CSIBM1129, CSIBM1130, CSIBM1132, CSIBM1133, CSIBM1137, CSIBM1140, CSIBM1141,
  CSIBM1142, CSIBM1143, CSIBM1144, CSIBM1145, CSIBM1146, CSIBM1147, CSIBM1148,
  CSIBM1149, CSIBM1153, CSIBM1154, CSIBM1155, CSIBM1156, CSIBM1157, CSIBM1158,
  CSIBM1160, CSIBM1161, CSIBM1163, CSIBM1164, CSIBM1166, CSIBM1167, CSIBM1364,
  CSIBM1371, CSIBM1388, CSIBM1390, CSIBM1399, CSIBM4517, CSIBM4899, CSIBM4909,
  CSIBM4971, CSIBM5347, CSIBM9030, CSIBM9066, CSIBM9448, CSIBM12712,
  CSIBM16804, CSIBM11621162, CSISO4UNITEDKINGDOM, CSISO10SWEDISH,
  CSISO11SWEDISHFORNAMES, CSISO14JISC6220RO, CSISO15ITALIAN, CSISO16PORTUGESE,
  CSISO17SPANISH, CSISO18GREEK7OLD, CSISO19LATINGREEK, CSISO21GERMAN,
  CSISO25FRENCH, CSISO27LATINGREEK1, CSISO49INIS, CSISO50INIS8,
  CSISO51INISCYRILLIC, CSISO58GB1988, CSISO60DANISHNORWEGIAN,
  CSISO60NORWEGIAN1, CSISO61NORWEGIAN2, CSISO69FRENCH, CSISO84PORTUGUESE2,
  CSISO85SPANISH2, CSISO86HUNGARIAN, CSISO88GREEK7, CSISO89ASMO449, CSISO90,
  CSISO92JISC62991984B, CSISO99NAPLPS, CSISO103T618BIT, CSISO111ECMACYRILLIC,
  CSISO121CANADIAN1, CSISO122CANADIAN2, CSISO139CSN369103, CSISO141JUSIB1002,
  CSISO143IECP271, CSISO150, CSISO150GREEKCCITT, CSISO151CUBA,
  CSISO153GOST1976874, CSISO646DANISH, CSISO2022CN, CSISO2022JP, CSISO2022JP2,
  CSISO2022KR, CSISO2033, CSISO5427CYRILLIC, CSISO5427CYRILLIC1981,
  CSISO5428GREEK, CSISO10367BOX, CSISOLATIN1, CSISOLATIN2, CSISOLATIN3,
  CSISOLATIN4, CSISOLATIN5, CSISOLATIN6, CSISOLATINARABIC, CSISOLATINCYRILLIC,
  CSISOLATINGREEK, CSISOLATINHEBREW, CSKOI8R, CSKSC5636, CSMACINTOSH,
  CSNATSDANO, CSNATSSEFI, CSN_369103, CSPC8CODEPAGE437, CSPC775BALTIC,
  CSPC850MULTILINGUAL, CSPC862LATINHEBREW, CSPCP852, CSSHIFTJIS, CSUCS4,
  CSUNICODE, CSWINDOWS31J, CUBA, CWI-2, CWI, CYRILLIC, DE, DEC-MCS, DEC,
  DECMCS, DIN_66003, DK, DS2089, DS_2089, E13B, EBCDIC-AT-DE-A, EBCDIC-AT-DE,
  EBCDIC-BE, EBCDIC-BR, EBCDIC-CA-FR, EBCDIC-CP-AR1, EBCDIC-CP-AR2,
  EBCDIC-CP-BE, EBCDIC-CP-CA, EBCDIC-CP-CH, EBCDIC-CP-DK, EBCDIC-CP-ES,
  EBCDIC-CP-FI, EBCDIC-CP-FR, EBCDIC-CP-GB, EBCDIC-CP-GR, EBCDIC-CP-HE,
  EBCDIC-CP-IS, EBCDIC-CP-IT, EBCDIC-CP-NL, EBCDIC-CP-NO, EBCDIC-CP-ROECE,
  EBCDIC-CP-SE, EBCDIC-CP-TR, EBCDIC-CP-US, EBCDIC-CP-WT, EBCDIC-CP-YU,
  EBCDIC-CYRILLIC, EBCDIC-DK-NO-A, EBCDIC-DK-NO, EBCDIC-ES-A, EBCDIC-ES-S,
  EBCDIC-ES, EBCDIC-FI-SE-A, EBCDIC-FI-SE, EBCDIC-FR, EBCDIC-GREEK, EBCDIC-INT,
  EBCDIC-INT1, EBCDIC-IS-FRISS, EBCDIC-IT, EBCDIC-JP-E, EBCDIC-JP-KANA,
  EBCDIC-PT, EBCDIC-UK, EBCDIC-US, EBCDICATDE, EBCDICATDEA, EBCDICCAFR,
  EBCDICDKNO, EBCDICDKNOA, EBCDICES, EBCDICESA, EBCDICESS, EBCDICFISE,
  EBCDICFISEA, EBCDICFR, EBCDICISFRISS, EBCDICIT, EBCDICPT, EBCDICUK, EBCDICUS,
  ECMA-114, ECMA-118, ECMA-128, ECMA-CYRILLIC, ECMACYRILLIC, ELOT_928, ES, ES2,
  EUC-CN, EUC-JISX0213, EUC-JP-MS, EUC-JP, EUC-KR, EUC-TW, EUCCN, EUCJP-MS,
  EUCJP-OPEN, EUCJP-WIN, EUCJP, EUCKR, EUCTW, FI, FR, GB, GB2312, GB13000,
  GB18030, GBK, GB_1988-80, GB_198880, GEORGIAN-ACADEMY, GEORGIAN-PS,
  GOST_19768-74, GOST_19768, GOST_1976874, GREEK-CCITT, GREEK, GREEK7-OLD,
  GREEK7, GREEK7OLD, GREEK8, GREEKCCITT, HEBREW, HP-GREEK8, HP-ROMAN8,
  HP-ROMAN9, HP-THAI8, HP-TURKISH8, HPGREEK8, HPROMAN8, HPROMAN9, HPTHAI8,
  HPTURKISH8, HU, IBM-803, IBM-856, IBM-901, IBM-902, IBM-921, IBM-922,
  IBM-930, IBM-932, IBM-933, IBM-935, IBM-937, IBM-939, IBM-943, IBM-1008,
  IBM-1025, IBM-1046, IBM-1047, IBM-1097, IBM-1112, IBM-1122, IBM-1123,
  IBM-1124, IBM-1129, IBM-1130, IBM-1132, IBM-1133, IBM-1137, IBM-1140,
  IBM-1141, IBM-1142, IBM-1143, IBM-1144, IBM-1145, IBM-1146, IBM-1147,
  IBM-1148, IBM-1149, IBM-1153, IBM-1154, IBM-1155, IBM-1156, IBM-1157,
  IBM-1158, IBM-1160, IBM-1161, IBM-1162, IBM-1163, IBM-1164, IBM-1166,
  IBM-1167, IBM-1364, IBM-1371, IBM-1388, IBM-1390, IBM-1399, IBM-4517,
  IBM-4899, IBM-4909, IBM-4971, IBM-5347, IBM-9030, IBM-9066, IBM-9448,
  IBM-12712, IBM-16804, IBM037, IBM038, IBM256, IBM273, IBM274, IBM275, IBM277,
  IBM278, IBM280, IBM281, IBM284, IBM285, IBM290, IBM297, IBM367, IBM420,
  IBM423, IBM424, IBM437, IBM500, IBM775, IBM803, IBM813, IBM819, IBM848,
  IBM850, IBM851, IBM852, IBM855, IBM856, IBM857, IBM860, IBM861, IBM862,
  IBM863, IBM864, IBM865, IBM866, IBM866NAV, IBM868, IBM869, IBM870, IBM871,
  IBM874, IBM875, IBM880, IBM891, IBM901, IBM902, IBM903, IBM904, IBM905,
  IBM912, IBM915, IBM916, IBM918, IBM920, IBM921, IBM922, IBM930, IBM932,
  IBM933, IBM935, IBM937, IBM939, IBM943, IBM1004, IBM1008, IBM1025, IBM1026,
  IBM1046, IBM1047, IBM1089, IBM1097, IBM1112, IBM1122, IBM1123, IBM1124,
  IBM1129, IBM1130, IBM1132, IBM1133, IBM1137, IBM1140, IBM1141, IBM1142,
  IBM1143, IBM1144, IBM1145, IBM1146, IBM1147, IBM1148, IBM1149, IBM1153,
  IBM1154, IBM1155, IBM1156, IBM1157, IBM1158, IBM1160, IBM1161, IBM1162,
  IBM1163, IBM1164, IBM1166, IBM1167, IBM1364, IBM1371, IBM1388, IBM1390,
  IBM1399, IBM4517, IBM4899, IBM4909, IBM4971, IBM5347, IBM9030, IBM9066,
  IBM9448, IBM12712, IBM16804, IEC_P27-1, IEC_P271, INIS-8, INIS-CYRILLIC,
  INIS, INIS8, INISCYRILLIC, ISIRI-3342, ISIRI3342, ISO-2022-CN-EXT,
  ISO-2022-CN, ISO-2022-JP-2, ISO-2022-JP-3, ISO-2022-JP, ISO-2022-KR,
  ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
  ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-9E, ISO-8859-10, ISO-8859-11,
  ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, ISO-10646,
  ISO-10646/UCS2, ISO-10646/UCS4, ISO-10646/UTF-8, ISO-10646/UTF8, ISO-CELTIC,
  ISO-IR-4, ISO-IR-6, ISO-IR-8-1, ISO-IR-9-1, ISO-IR-10, ISO-IR-11, ISO-IR-14,
  ISO-IR-15, ISO-IR-16, ISO-IR-17, ISO-IR-18, ISO-IR-19, ISO-IR-21, ISO-IR-25,
  ISO-IR-27, ISO-IR-37, ISO-IR-49, ISO-IR-50, ISO-IR-51, ISO-IR-54, ISO-IR-55,
  ISO-IR-57, ISO-IR-60, ISO-IR-61, ISO-IR-69, ISO-IR-84, ISO-IR-85, ISO-IR-86,
  ISO-IR-88, ISO-IR-89, ISO-IR-90, ISO-IR-92, ISO-IR-98, ISO-IR-99, ISO-IR-100,
  ISO-IR-101, ISO-IR-103, ISO-IR-109, ISO-IR-110, ISO-IR-111, ISO-IR-121,
  ISO-IR-122, ISO-IR-126, ISO-IR-127, ISO-IR-138, ISO-IR-139, ISO-IR-141,
  ISO-IR-143, ISO-IR-144, ISO-IR-148, ISO-IR-150, ISO-IR-151, ISO-IR-153,
  ISO-IR-155, ISO-IR-156, ISO-IR-157, ISO-IR-166, ISO-IR-179, ISO-IR-193,
  ISO-IR-197, ISO-IR-199, ISO-IR-203, ISO-IR-209, ISO-IR-226, ISO/TR_11548-1,
  ISO646-CA, ISO646-CA2, ISO646-CN, ISO646-CU, ISO646-DE, ISO646-DK, ISO646-ES,
  ISO646-ES2, ISO646-FI, ISO646-FR, ISO646-FR1, ISO646-GB, ISO646-HU,
  ISO646-IT, ISO646-JP-OCR-B, ISO646-JP, ISO646-KR, ISO646-NO, ISO646-NO2,
  ISO646-PT, ISO646-PT2, ISO646-SE, ISO646-SE2, ISO646-US, ISO646-YU,
  ISO2022CN, ISO2022CNEXT, ISO2022JP, ISO2022JP2, ISO2022KR, ISO6937,
  ISO8859-1, ISO8859-2, ISO8859-3, ISO8859-4, ISO8859-5, ISO8859-6, ISO8859-7,
  ISO8859-8, ISO8859-9, ISO8859-9E, ISO8859-10, ISO8859-11, ISO8859-13,
  ISO8859-14, ISO8859-15, ISO8859-16, ISO11548-1, ISO88591, ISO88592, ISO88593,
  ISO88594, ISO88595, ISO88596, ISO88597, ISO88598, ISO88599, ISO88599E,
  ISO885910, ISO885911, ISO885913, ISO885914, ISO885915, ISO885916,
  ISO_646.IRV:1991, ISO_2033-1983, ISO_2033, ISO_5427-EXT, ISO_5427,
  ISO_5427:1981, ISO_5427EXT, ISO_5428, ISO_5428:1980, ISO_6937-2,
  ISO_6937-2:1983, ISO_6937, ISO_6937:1992, ISO_8859-1, ISO_8859-1:1987,
  ISO_8859-2, ISO_8859-2:1987, ISO_8859-3, ISO_8859-3:1988, ISO_8859-4,
  ISO_8859-4:1988, ISO_8859-5, ISO_8859-5:1988, ISO_8859-6, ISO_8859-6:1987,
  ISO_8859-7, ISO_8859-7:1987, ISO_8859-7:2003, ISO_8859-8, ISO_8859-8:1988,
  ISO_8859-9, ISO_8859-9:1989, ISO_8859-9E, ISO_8859-10, ISO_8859-10:1992,
  ISO_8859-14, ISO_8859-14:1998, ISO_8859-15, ISO_8859-15:1998, ISO_8859-16,
  ISO_8859-16:2001, ISO_9036, ISO_10367-BOX, ISO_10367BOX, ISO_11548-1,
  ISO_69372, IT, JIS_C6220-1969-RO, JIS_C6229-1984-B, JIS_C62201969RO,
  JIS_C62291984B, JOHAB, JP-OCR-B, JP, JS, JUS_I.B1.002, KOI-7, KOI-8, KOI8-R,
  KOI8-RU, KOI8-T, KOI8-U, KOI8, KOI8R, KOI8U, KSC5636, L1, L2, L3, L4, L5, L6,
  L7, L8, L10, LATIN-9, LATIN-GREEK-1, LATIN-GREEK, LATIN1, LATIN2, LATIN3,
  LATIN4, LATIN5, LATIN6, LATIN7, LATIN8, LATIN9, LATIN10, LATINGREEK,
  LATINGREEK1, MAC-CENTRALEUROPE, MAC-CYRILLIC, MAC-IS, MAC-SAMI, MAC-UK, MAC,
  MACCYRILLIC, MACINTOSH, MACIS, MACUK, MACUKRAINIAN, MIK, MS-ANSI, MS-ARAB,
  MS-CYRL, MS-EE, MS-GREEK, MS-HEBR, MS-MAC-CYRILLIC, MS-TURK, MS932, MS936,
  MSCP949, MSCP1361, MSMACCYRILLIC, MSZ_7795.3, MS_KANJI, NAPLPS, NATS-DANO,
  NATS-SEFI, NATSDANO, NATSSEFI, NC_NC0010, NC_NC00-10, NC_NC00-10:81,
  NF_Z_62-010, NF_Z_62-010_(1973), NF_Z_62-010_1973, NF_Z_62010,
  NF_Z_62010_1973, NO, NO2, NS_4551-1, NS_4551-2, NS_45511, NS_45512,
  OS2LATIN1, OSF00010001, OSF00010002, OSF00010003, OSF00010004, OSF00010005,
  OSF00010006, OSF00010007, OSF00010008, OSF00010009, OSF0001000A, OSF00010020,
  OSF00010100, OSF00010101, OSF00010102, OSF00010104, OSF00010105, OSF00010106,
  OSF00030010, OSF0004000A, OSF0005000A, OSF05010001, OSF100201A4, OSF100201A8,
  OSF100201B5, OSF100201F4, OSF100203B5, OSF1002011C, OSF1002011D, OSF1002035D,
  OSF1002035E, OSF1002035F, OSF1002036B, OSF1002037B, OSF10010001, OSF10010004,
  OSF10010006, OSF10020025, OSF10020111, OSF10020115, OSF10020116, OSF10020118,
  OSF10020122, OSF10020129, OSF10020352, OSF10020354, OSF10020357, OSF10020359,
  OSF10020360, OSF10020364, OSF10020365, OSF10020366, OSF10020367, OSF10020370,
  OSF10020387, OSF10020388, OSF10020396, OSF10020402, OSF10020417, PT, PT2,
  PT154, R8, R9, RK1048, ROMAN8, ROMAN9, RUSCII, SE, SE2, SEN_850200_B,
  SEN_850200_C, SHIFT-JIS, SHIFT_JIS, SHIFT_JISX0213, SJIS-OPEN, SJIS-WIN,
  SJIS, SS636127, STRK1048-2002, ST_SEV_358-88, T.61-8BIT, T.61, T.618BIT,
  TCVN-5712, TCVN, TCVN5712-1, TCVN5712-1:1993, THAI8, TIS-620, TIS620-0,
  TIS620.2529-1, TIS620.2533-0, TIS620, TS-5881, TSCII, TURKISH8, UCS-2,
  UCS-2BE, UCS-2LE, UCS-4, UCS-4BE, UCS-4LE, UCS2, UCS4, UHC, UJIS, UK,
  UNICODE, UNICODEBIG, UNICODELITTLE, US-ASCII, US, UTF-7, UTF-8, UTF-16,
  UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE, UTF7, UTF8, UTF16, UTF16BE,
  UTF16LE, UTF32, UTF32BE, UTF32LE, VISCII, WCHAR_T, WIN-SAMI-2, WINBALTRIM,
  WINDOWS-31J, WINDOWS-874, WINDOWS-936, WINDOWS-1250, WINDOWS-1251,
  WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255, WINDOWS-1256,
  WINDOWS-1257, WINDOWS-1258, WINSAMI2, WS2, YU
$

Etiquettes: 

Commandes

Quelques commandes pour bien utiliser Linux

Utilisateurs

Ajouter un utilisateur au système

Ajouter un utilisateur au système

$ sudo   adduser   nom_du_user

(répondre aux différentes questions posées)

Etiquettes: 

Ajouter un utilisateur Samba

Ajouter un utilisateur Samba

(après avoir créé le user au niveau du système avec la commande précédente)

$ sudo    smbpasswd   -a   nom_du_user

(saisir le mot de passe indiqué lors de la création du user au niveau du système)

Etiquettes: 

Afficher l'UID d'un utilisateur

Pour afficher l'UID d'un utilisateur, saisir la commande suivante dans une console :

$ echo `grep root /etc/passwd | cut -d: -f3`
0
$

La commande grep root /etc/passwd affiche la ligne du fichier /etc/passwd contenant l'expression root

$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
$

La commande cut -d: -f3 permet d'extraire uniquement la 3ème valeur -f3 après découpage de la ligne en indiquant le délimiteur -d:

Etiquettes: 

Fermer une session utilisateur via la console (Ubuntu)

2 sessions GNOME ouverte sur Ubuntu. La seconde session est verrouillée et protégée par un mot de passe.

Pour la fermer, saisir la commande suivante dans une console :

$ sudo skill -KILL -u utilisateur


Pour fermer un terminal dont la connexion a été perdue (ssh par exemple)

Identifier la console à fermer :

$ w
 14:32:16 up 12 days,  6:26,  2 users,  load average: 0,00, 0,00, 0,00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
toto    pts/0    vm-ssh.quennec. 14:28    4:12   0.29s  0.29s -bash
toto    pts/1    vm-ssh.quennec. 14:28    0.00s  0.24s  0.00s w
$

La commande w permet de visualiser les utilisateurs connectés et la commande en cours d'exécution.
Dans l'exemple ci-dessus, je suis donc actuellement connecté sur 2 terminaux pts/0 & pts/1.
On voit également que la commande w a été exécutée sur le terminal pts/1 et que le terminal pts/0 est inactif depuis 4 min et 12 sec.
Il faut donc fermer le terminal pts/0.

Identifier le processus lié au terminal à fermer :

$ ps -ef | grep toto@pts/0 | grep -v grep
toto    15236 15224  0 14:28 ?        00:00:00 sshd: toto@pts/0
$

Le terminal pts/0 est donc lié au processus 15236.

Fermer le terminal :

$ kill -s 9 15236

Etiquettes: 

Modifier l'interpréteur de commande d'un utilisateur

Pour changer l'interpréteur de commande d'un utilisateur, saisir dans une console :

Mettre par défaut l'interpréteur de commande BASH pour l'utilisateur toto

$ chsh -s /bin/bash toto

L'option -s permet d'indiquer l'interpréteur de son choix.
Si cette option n'est pas indiquée, le programme chsh s'appuie sur le fichier /etc/shells pour fournir la liste des interpréteurs à utiliser.

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/csh
/bin/sh
/usr/bin/es
/usr/bin/ksh
/bin/ksh
/usr/bin/rc
/usr/bin/tcsh
/bin/tcsh
/usr/bin/esh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/screen
/bin/ksh93
/bin/zsh
/usr/bin/zsh

Le nouvel interpréteur est renseigné dans le fichier /etc/passwd pour l'utilisateur indiqué.

$ cat /etc/passwd | grep "toto"
toto:x:1020:1020:toto:/home/toto:/bin/bash
$

 

Prendre le rôle ROOT en mode console

La commande suivante permet de se loguer en root.

Ouvrir une session avec son  user puis saisir :

$ sudo su -

(Saisir son mot de passe)

Etiquettes: 

Prendre le rôle d'un user en mode console

Dans une console, pour prendre le rôle d'un user spécifique, saisir la commande suivante :

$ sudo -i -u user

Par exemple, pour prendre le rôle du user www-data

$ sudo -i -u www-data

Via le user root

$ sudo su -
$ su - www-data

Etiquettes: 

Partage NFS

Partage NFS

Pré-requis

$ sudo apt-get install nfs-common

Créer un partage

$ sudo mkdir /opt/partage_nfs
$ sudo cat >> /etc/exports <<EOF
> $_ 192.168.1.56/32(rw)
> EOF
$ sudo /etc/init.d/nfs-kernel-server restart

Ce partage sera accessible uniquement pour l'adresse IP indiquée

Afficher les partages NFS du serveur

$ showmount -e   adresse_ip_du_serveur

Démarrer le serveur NFS

$ sudo /etc/init.d/nfs-kernel-server   start

Redémarrer le serveur NFS

$ sudo /etc/init.d/nfs-kernel-server   restart

Monter un partage NFS

à renseigner dans /etc/fstab

  • adresse_ip_serveur  :  /nom_du_partage_nfs  /point_de_montage_sur_client  nfs  defaults  0  0

Monter un partage NFS via SSH

Installer sshfs

$ sudo apt-get install sshfs

Créer le répertoire servant au montage

$ sudo mkdir /mnt/ssh

Monter le partage NFS distant via SSH

$ sudo sshfs $USER@adresse_de_la_machine_distante:/nom_du_partage_nfs /mnt/ssh

Pour démonter le partage NFS

$ sudo fusermount -u /mnt/ssh

 

Etiquettes: 

FSTAB

Documentation Ubuntu

Modifier le fichier FSTAB

$ sudo gedit /etc/fstab
$ sudo nano /etc/fstab

Activer les modifications du fichier FSTAB

$ sudo mount -a

Etiquettes: 

TAR

Archiver avec TAR

Archiver avec TAR

$ tar -cvf mon_archive.tar   nom_du_fichier_a_archiver
$ tar -cvf mon_archive.tar   nom_du_dossier_a_archiver

-c : Create

-v : Verbose

-f : File

Archiver des fichiers avec TAR puis les supprimer

Suppression des fichiers archivés avec l'option --remove-files

$ /bin/tar --create --file=/monDossier/monArchive.tar --remove-files /monDossier/*.jpg

Archive tous les fichiers jpg présents dans le dossier 'monDossier' puis les supprime du dossier

Etiquettes: 

Désarchiver avec TAR

Désarchiver avec TAR

$ tar -xvf mon_archive.tar

-x : eXtract

-v : Verbose

-f : File

Etiquettes: 

Archivage incrémentiel avec TAR

Archivage :

Création de la première sauvegarde (sauvegarde complète)

Sauvegarde du dossier /home dans le fichier /backup/archive.1.tar avec détail du contenu dans /backup/backup.list

$ tar --create --file=/backup/archive.1.tar --listed-incremental=/backup/backup.list /home

Création des sauvegardes suivantes (incrémentées uniquement avec les fichiers nouveaux et/ou modifiés)

Sauvegarde des fichiers du dossier /home dans le fichier /backup/archive.2.tar différents de ceux présents dans la liste /backup/backup.list

$ tar --create --file=/backup/archive.2.tar --listed-incremental=/backup/backup.list /home

Utilisation de la date dans le nom de l'archive générée

$ tar --create --file=/backup/archive.`date +%Y_%m_%d_%s`.tar --listed-incremental=/backup/backup.list /home
 

Restauration :

Restaurer la première archive complète

$ tar --extract --listed-incremental=/dev/null --file archive.1.tar

Puis restaurer les archives suivantes

$ tar --extract --listed-incremental=/dev/null --file archive.2.tar

Sur un historique de sauvegarde de 10 archives, pour restaurer l'archive 4, restaurer les archives 1, 2, 3 & 4 et ce dans le bon ordre. Ne pas restaurer directement l'archive 4, elle serait incomplète.

Etiquettes: 

Supprimer un fichier dans une archive TAR

Pour supprimer un fichier ou un dossier dans une archive TAR :

$ tar --delete --file=nom_de_l_archive.tar nom_du_fichier_a_supprimer

Etiquettes: 

TAR & GZIP

Compresser avec TAR & GZIP

Compresser avec TAR & GZIP

$ tar -cvzf mon _archive.tar.gz   nom_du_fichier_a_compresser
$ tar -cvzf mon _archive.tar.gz   nom_du_dossier_a_compresser

-c : Create

-v : Verbose

-z : gZip

-f : File

Décompresser avec TAR & GZIP

Décompresser avec TAR & GZIP

$ tar -xvzf mon_archive.tar.gz

-x : eXtract

-v : Verbose

-z : gZip

-f : File

Variables d'environnement

Afficher les variables d'environnements

Pour afficher les variables d'environnements, saisir dans la console

$ printenv

Etiquettes: 

Créer une variable d'environnement

Pour créer une variable d'environnement, éditer le fichier $HOME/.bashrc

Y inscrire :

export MA_VARIABLE=/mon_dossier

ou

export MA_VARIABLE=/mon_dossier/mon_fichier

Etiquettes: 

Date & heure

Conversion timestamp

Pour convertir un timestamp en format date :

$ date -d @1320822978 "+%Y-%m-%d %T"
2011-11-09 08:16:18

Pour convertir une date en timestamp :

Date au format AAAAMMJJ

$ date -d "20111109" +%s
1320793200

Effectuer une action à une date et un jour précis de la semaine

Voici un exemple qui permet d'exécuter une action en fonction d'un jour bien précis.

Comment recevoir automatiquement un mail tous les vendredi 13 de chaque mois :

$ if [[ `date +%u` == 5 && `date +%d` == 13 ]]; then echo "" | mail -s "Vendredi 13 : Jouer au Loto" moi@gmail.com; fi

Explications :

La fonction `date +%u` retourne le numéro du jour de la semaine. Lundi = 1... Dimanche = 7.

La fonction `date +%d` retourne le jour du mois

Etiquettes: 

Les différents formats de date sous Linux

Sous Linux, la fonction date permet d'afficher la date du jour ou une date définie sous différentes formes.

Le script suivant (afficheDate.sh), associé au fichier contenant les différentes fonctions utilisées (fonctions.inc.sh) affiche différentes informations sur la date du jour.

$ nl afficheDate.sh
     1  #!/usr/bin/bash
     2  SCRIPTDIR="."
     3  . $SCRIPTDIR/fonctions.inc.sh
     4  echo "Date au format aaaa_mm_jj : `getDate`"
     5  echo "Année : `getYear`"
     6  echo "Mois : `getMonth`"
     7  echo "Jour : `getDay`"
     8  echo "Nous sommes le `getJourDate` `getDay` `getMoisDate` `getYear`"
     9  echo "Nous sommes en semaine `getNumSemaine`"
    10  echo "Nous sommes le `getNumDay` jour de l'année"
    11  echo "Nous sommes le `getNumDayWeek` jour de la semaine"
    12  echo "Cette année il y a `nbJourAnnee` jours"
    13  echo "Il reste `nbResteJour` jour(s) avant la fin de l'année"
    14  echo "Cette année il y a `nbSemAnnee` semaines"
    15  echo "Il reste `nbResteSemaine` semaine(s) avant la fin de l'année"
$

$ nl fonctions.inc.sh
     1  function getDate {
     2          date '+%Y_%m_%d'
     3  }
     4  function getYear {
     5          date '+%Y'
     6  }
     7  function getMonth {
     8          date '+%m'
     9  }
    10  function getDay {
    11          date '+%d'
    12  }
    13  function getJourDate {
    14          date '+%A'
    15  }
    16  function getMoisDate {
    17          date '+%B'
    18  }
    19  function getNumSemaine {
    20          date '+%V'
    21  }
    22  function getNumDay {
    23          typeset jour
    24          jour=`date '+%j'`
    25          case "$jour" in
    26                  1)
    27                          echo "1 er"
    28                          ;;
    29                  *)
    30                          echo "$jour ème"
    31                          ;;
    32          esac
    33  }
    34  function getNumDayWeek {
    35          typeset jour
    36          jour=`date '+%u'`
    37          case "$jour" in
    38                  1)
    39                          echo "1 er"
    40                          ;;
    41                  *)
    42                          echo "$jour ème"
    43                          ;;
    44          esac
    45  }
    46  function nbJourAnnee {
    47          typeset jour
    48          typeset mois
    49          typeset annee
    50          jour=31
    51          mois=12
    52          annee=`getYear`
    53          date -d $mois'/'$jour'/'$annee '+%j'
    54  }
    55  function nbResteJour {
    56          typeset jour
    57          typeset nbJourAnnee
    58          typeset nbJourRestant
    59          jour=`date '+%j'`
    60          nbJourAnnee=`nbJourAnnee`
    61          ((nbJourRestant=nbJourAnnee-jour))
    62          echo $nbJourRestant
    63  }
    64  function nbSemAnnee {
    65          typeset jour
    66          typeset mois
    67          typeset annee
    68          jour=31
    69          mois=12
    70          annee=`getYear`
    71          date -d $mois'/'$jour'/'$annee '+%V'
    72  }
    73  function nbResteSemaine {
    74          typeset numSem
    75          typeset nbSem
    76          typeset nbSemReste
    77          numSem=`getNumSemaine`
    78          nbSem=`nbSemAnnee`
    79          ((nbSemReste=nbSem-numSem))
    80          echo $nbSemReste
    81  }
$

Exécution du script :

$ ./afficheDate.sh
Date au format aaaa_mm_jj : 2011_12_19
Année : 2011
Mois : 12
Jour : 19
Nous sommes le Monday 19 December 2011
Nous sommes en semaine 51
Nous sommes le 353 ème jour de l'année
Nous sommes le 1 er jour de la semaine
Cette année il y a 365 jours
Il reste 12 jour(s) avant la fin de l'année
Cette année il y a 52 semaines
Il reste 1 semaine(s) avant la fin de l'année
$

Etiquettes: 

Fichiers / Dossiers

Convertir un fichier Windows en Linux et inversement

Problème de lecture / exécution d'un fichier sous Linux créer sous Windows.

Sous Windows (DOS), les lignes de fichier se terminent par les caractères spéciaux "\r\n".
Sous UNIX, les lignes de fichier se terminent par les caractères spéciaux "\n".

Les caractères spéciaux permettant le retour chariot étant différents sous Windows (DOS) et sous Linux (UNIX), il faut penser à les convertir.

Différentes méthodes permettent de le faire.

Avec la commande sed :

$ sed -i 's/\r//g'  /nom_du_fichier

Sinon, le caractère "\r" est représenté par "^M" qui s'obtient par la séquence de touches suivantes : "CTRL-V" + "CTRL-M"

$ sed -i 's/^M//' /nom_du_fichier

Avec la commande tr :

$ tr -d '\r' < /mon_fichier_source > /mon_fichier_destination

A l'inverse, convertir un fichier UNIX vers DOS

$ sed 's/$/^M/' /nom_du_fichier

 

Source : http://www.commentcamarche.net/faq/5978-sed-conversion-retours-chariots-...

Rendre un fichier exécutable

Pour rendre un fichier exécutable

$ sudo chmod +x ./nom_du_fichier

Etiquettes: 

Afficher la somme MD5 d'un fichier

Pour afficher la somme MD5 d'un fichier afin d'en assurer son intégrité, il suffit d'utiliser la commande md5sum.

Exemple avec le fichier "test.txt" :

$ cat test.txt
ceci est une ligne
$

Calcul de la somme MD5 du fichier "test.txt" :

$ md5sum test.txt
2f378979d16de47b9d439149be5623db  test.txt
$

Modification du fichier "test.txt" :

$ echo "une nouvelle ligne" >> test.txt
$ cat test.txt
ceci est une ligne
une nouvelle ligne
$

Re-calcul de la somme MD5 du fichier "test.txt" :

$ md5sum test.txt
5c69442bc1b1084b60a81190a75c6a0e  test.txt
$

Après modification du fichier, la somme MD5 du fichier n'est plus la même.

Il est également possible d'enregistrer la somme MD5 d'un fichier dans un fichier :

$ md5sum test.txt > test.md5
$ cat test.md5
5c69442bc1b1084b60a81190a75c6a0e  test.txt
$

De cette manière, il est possible de controler la somme MD5 d'un fichier :

Utilisation de la commande md5sum avec l'option -c et le fichier contenant la somme MD5 en paramètre.

$ md5sum -c test.md5
test.txt: OK
$

Attention : Les 2 fichiers (celui à controler et celui contenant la somme MD5) doivent être au même endroit.

Si le fichier à controler a été altéré :

$ md5sum -c test.md5
test.txt: FAILED
md5sum: WARNING: 1 of 1 computed checksum did NOT match
$

Etiquettes: 

BASH: Gérer des fichiers dont les noms comportent des espaces

Sous Unix, les fichiers comportant des espaces dans le nom sont très compliqués à gérer sachant que l'espace est interprété comme étant un séparateur.

Voici un exemple qui permet d'afficher la somme MD5 d'une liste de fichiers dont les noms comportent des espaces.

 # ls -1
20160524 leclerc 1264.pdf             
20160528 boulanger 3290.pdf           
20160528 cofiroute 620.pdf            
20160529 la mie caline 790.pdf        
20160529 le marché aux fleurs 2050.pdf
20160601 cordonnerie leclerc 1000.pdf
20160601 leclerc 990.pdf              
20160601 pharmacie 3830.pdf          

Les 8 fichiers ci-dessus comportent tous des espaces dans leurs noms.

 # while read f; do md5sum "$f"; done <<< $(ls)

57709e696d14838ceeff6500728d1569  20160524 leclerc 1264.pdf
9f870fb12e598c7345cb74c6db15afee  20160528 boulanger 3290.pdf
5bb71357967db84baf6ed4a5fb56c552  20160528 cofiroute 620.pdf
690471607356b9ea798d3d44d69f478d  20160529 la mie caline 790.pdf
519a250059f5d8bc93af22d2ae02512f  20160529 le marché aux fleurs 2050.pdf
a08e6094bc01a34c508761ebbdae6418  20160601 cordonnerie leclerc 1000.pdf
c8f36624113710939365dd0a4daeb30c  20160601 leclerc 990.pdf
387ce36eaa18d383bd1950ee17a4fdff  20160601 pharmacie 3830.pdf

La commande read, dans la boucle while, interprète correctement les noms de fichiers provenant de la commande ls.

warningNe pas oublier les 3 chevrons vers la gauche "<<<" entre la commande ls et la boucle while

Etiquettes: 

Chiffrer / Déchiffrer des fichiers

mcrypt & mdecrypt sont des programmes qui permettent de chiffrer / déchiffer des fichiers avec un mot de passe.

Le programme mcrypt créé un nouveau fichier portant l'extension .nc avec des permissions 0600

Pour l'installer :

$ sudo apt-get install mcrypt

Pour chiffrer un fichier

$ mcrypt monFichier
Enter the passphrase (maximum of 512 characters)
Please use a combination of upper and lower case letters and numbers.
Enter passphrase:
Enter passphrase:
 
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 ronan ronan   0 2011-11-05 15:24 monFichier
-rw------- 1 ronan ronan 109 2011-11-05 15:24 monFichier.nc
$

Pour chiffrer un fichier et supprimer le fichier source

$ mcrypt -u monFichier
Enter the passphrase (maximum of 512 characters)
Please use a combination of upper and lower case letters and numbers.
Enter passphrase:
Enter passphrase:
 
File monFichier was encrypted.
$ ls -l monFichier*
-rw------- 1 ronan ronan 109 2011-11-05 15:24 monFichier.nc
$

Pour chiffrer un fichier avec une compression gzip

$ mcrypt -z monFichier
Enter the passphrase (maximum of 512 characters)
Please use a combination of upper and lower case letters and numbers.
Enter passphrase:
Enter passphrase:
 
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 ronan ronan   0 2011-11-05 15:31 monFichier
-rw------- 1 ronan ronan 141 2011-11-05 15:31 monFichier.gz.nc
$

Lister tous les algorithmes utilisable par mcrypt

$ mcrypt --list
cast-128 (16): cbc cfb ctr ecb ncfb nofb ofb
gost (32): cbc cfb ctr ecb ncfb nofb ofb
rijndael-128 (32): cbc cfb ctr ecb ncfb nofb ofb
twofish (32): cbc cfb ctr ecb ncfb nofb ofb
arcfour (256): stream
cast-256 (32): cbc cfb ctr ecb ncfb nofb ofb
loki97 (32): cbc cfb ctr ecb ncfb nofb ofb
rijndael-192 (32): cbc cfb ctr ecb ncfb nofb ofb
saferplus (32): cbc cfb ctr ecb ncfb nofb ofb
wake (32): stream
blowfish-compat (56): cbc cfb ctr ecb ncfb nofb ofb
des (8): cbc cfb ctr ecb ncfb nofb ofb
rijndael-256 (32): cbc cfb ctr ecb ncfb nofb ofb
serpent (32): cbc cfb ctr ecb ncfb nofb ofb
xtea (16): cbc cfb ctr ecb ncfb nofb ofb
blowfish (56): cbc cfb ctr ecb ncfb nofb ofb
enigma (13): stream
rc2 (128): cbc cfb ctr ecb ncfb nofb ofb
tripledes (24): cbc cfb ctr ecb ncfb nofb ofb
$

Chiffrer un fichier en utilisant l'algorithme "DES"

$ mcrypt -a des monFichier
Enter the passphrase (maximum of 512 characters)
Please use a combination of upper and lower case letters and numbers.
Enter passphrase:
Enter passphrase:
 
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 ronan ronan  0 2011-11-05 15:31 monFichier
-rw------- 1 ronan ronan 84 2011-11-05 15:31 monFichier.nc
$

Obtenir des infos sur le fichier chiffré

$ file monFichier.nc
monFichier.nc: mcrypt 2.5 encrypted data, algorithm: des, keysize: 8 bytes, mode: cbc,
$

Déchiffrer un fichier

$ ls -l monFichier*
-rw------- 1 ronan ronan 84 2011-11-05 15:31 monFichier.nc
$ mcrypt -d monFichier.nc
Enter passphrase:
File monFichier.nc was decrypted.
$ ls -l monFichier*
-rw------- 1 ronan ronan  0 2011-11-05 15:31 monFichier
-rw------- 1 ronan ronan 84 2011-11-05 15:31 monFichier.nc
$

Ou en utilisant le programme mdecrypt

$ ls -l monFichier*
-rw------- 1 ronan ronan 84 2011-11-05 15:31 monFichier.nc
$ mdecrypt monFichier.nc
Enter passphrase:
File monFichier.nc was decrypted.
$ ls -l monFichier*
-rw------- 1 ronan ronan  0 2011-11-05 15:31 monFichier
-rw------- 1 ronan ronan 84 2011-11-05 15:31 monFichier.nc
$

Etiquettes: 

Comparer les fichiers de deux dossiers distants

Comment savoir si les fichiers présents dans le dossier "DOSSIERX" du serveur "SERVEURA" sont exactement les mêmes que ceux présents dans le dossier "DOSSIERY" du serveur "SERVEURB" ?

Grâce à la commande sha256sum et une connexion ssh correctement configurée.

Exécuter le commande suivante sur le serveur "SERVEURA".

$ find /DOSSIERX -type f -exec sha256sum {} \; | ssh SERVEURB 'cd /DOSSIERY && sha256sum --quiet -c -'

Idem, mais en excluant un dossier de la recherche (.git par exemple):

$ find /DOSSIERX -type f -not -path */.git/* -exec sha256sum {} \; | ssh SERVEURB 'cd /DOSSIERY && sha256sum --quiet -c -'

 

Etiquettes: 

Connaitre la taille d'un dossier

Pour connaitre la taille d'un dossier :

$ sudo du -hs /nom_du_dossier

Etiquettes: 

Copier des fichiers résultant d'une recherche avec find

Rechercher tous les fichiers *.jpg présents dans le répertoire ~/images (sous-répertoires inclus) et les copier dans le répertoire imagesJPG.

$ find ~/images -name '*.jpg' -print0 | xargs -I '{}' -0 cp {} ~/imagesJPG/

L'option -print0 de la commande find et -0 de la commande xargs permet de prendre en compte les fichiers comportant des espaces dans leurs noms.

Etiquettes: 

Créer un fichier d'une taille précise

La commande dd permet de créer des fichiers d'une taille bien précise.

Créer un fichier vide de 1 Mo (1024 blocs de 1Ko):

$ dd if=/dev/zero of=zzz bs=1k count=1024
1024+0 enregistrements lus
1024+0 enregistrements écrits
1048576 octets (1,0 MB) copiés, 0,0108743 s, 96,4 MB/s
 
$ ls -l zzz
-rw-r--r-- 1 r.quennec mkpasswd 1,0M 23 mai   10:53 zzz
$

Créer un fichier avec des données aléatoires de 1 Mo (1024 blocs de 1Ko):

$ dd if=/dev/urandom of=zzz bs=1k count=1024
1024+0 enregistrements lus
1024+0 enregistrements écrits
1048576 octets (1,0 MB) copiés, 0,239581 s, 4,4 MB/s
 
$ wc zzz
   4074   23182 1048576 zzz
$

Etiquettes: 

Lire un fichier PDF en ligne de commande

Lire un fichier PDF en ligne commande est possible grâce à la commande less

$ less Verne-Le_tour_du_monde_en_80_jours.InLibroVeritas.net_oeuvre18616.pdf
Jules Verne
   
LE TOUR DU
MONDE EN
QUATRE-VINGTS
JOURS
   
- Collection Romans / Nouvelles -
   
Retrouvez cette oeuvre et beaucoup d'autres sur
http://www.inlibroveritas.net
   
^L^LTable des matières
LE TOUR DU MONDE EN QUATRE-VINGTS JOURS......................1
I - DANS LEQUEL PHILEAS FOGG ET PASSEPARTOUT
S'ACCEPTENT RÉCIPROQUEMENT L'UN COMME
MAÎTRE, L'AUTRE COMME DOMESTIQUE...............................2
II - OU PASSEPARTOUT EST CONVAINCU QU'IL A ENFIN
TROUVE SON IDEAL......................................................................8
III - OU S'ENGAGE UNE CONVERSATION QUI POURRA
COUTER CHER A PHILEAS FOGG.............................................12
IV - DANS LEQUEL PHILEAS FOGG STUPEFIE
PASSEPARTOUT, SON DOMESTIQUE.......................................21
V - DANS LEQUEL UNE NOUVELLE VALEUR APPARAÎT
SUR LA PLACE DE LONDRES
...................
$

Etiquettes: 

Modifier le propriétaire d'un fichier ou d'un dossier

Pour modifier le propriétaire d'un fichier ou d'un dossier

$ sudo chown root /u

change le propriétaire de /u en « root ».

$ sudo chown root:staff /u

même chose en changeant également le groupe en « staff ».

$ sudo chown -hR root /u

change le propriétaire de /u et ses sous-fichiers en « root ».

OPTIONS

Changer le propriétaire et/ou le groupe de chaque FICHIER en PROPRIÉTAIRE et/ou GROUPE. Avec l’option --reference, modifier le propriétaire et le groupe de chaque fichier en celui du FICHIER-R.

       -c, --changes
              utiliser le mode bavard en ne signalant que les modifications

       --dereference
              affecter la cible de chaque lien  symbolique  (comportement  par
              défaut) plutôt que le lien symbolique lui-même

       -h, --no-dereference
              modifier  les  liens symboliques au lieu des fichiers référencés
              (utile seulement sur  les  systèmes  permettant  de  changer  le
              propriétaire d’un lien symbolique)

       --from=PROPRIETAIRE_COURANT:GROUPE_COURANT
              changer  le  propriétaire  et/ou  le  groupe  de  chaque fichier
              seulement s’il y a concordance avec  le  propriétaire  et/ou  le
              groupe  courant spécifié. Les deux peuvent être omis, auquel cas
              la concordance n’est pas requise pour le paramètre non spécifié

       --no-preserve-root
              ne pas traiter « / » de manière spéciale (par défaut)

       --preserve-root
              ne pas opérer récursivement sur « / »

       -f, --silent, --quiet
              supprimer la plupart des messages d’erreur

       --reference=FICHIER-R
              utiliser le propriétaire et le groupe du FICHIER-R  au  lieu  de
              valeurs explicites PROPRIÉTAIRE:GROUPE

       -R, --recursive
              modifier récursivement fichiers et répertoires

       -v, --verbose
              afficher un diagnostic pour chaque fichier traité

       Les  options  suivantes  modifient  la  façon  dont  est  parcourue  la
       hiérarchie lorsque l’option -R est également spécifiée. Si  plus  d’une
       option est spécifiée, seule la dernière est prise en compte.

       -H     si  un  paramètre de la ligne de commande est un lien symbolique
              vers un répertoire, le parcourir

       -L     parcourir  chaque  lien  symbolique  rencontré   menant   à   un
              répertoire

       -P     ne parcourir aucun lien symbolique (par défaut)

       --help afficher l’aide-mémoire et quitter

       --version
              afficher le nom et la version du logiciel et quitter

       Le  propriétaire  n’est  pas modifié s’il n’est pas spécifié. Le groupe
       reste inchangé s’il n’est pas spécifié, sauf si cela est  implicitement
       demandé avec un « : » suivant un PROPRIÉTAIRE symbolique, auquel cas il
       sera modifié en ce nom de propriétaire. Le PROPRIÉTAIRE  et  le  GROUPE
       peuvent être numériques ou symboliques.
Etiquettes: 

Modifier les permissions d'un fichier ou d'un dossier

Pour modifier les permissions d'un fichier ou d'un dossier

$ sudo chmod 777 ./nom_du_fichier

modifie les permissions pour le fichier

$ sudo chmod 777 ./nom_du_dossier

modifie les permissions pour le dossier

$ sudo chmod -R 777 ./nom_du_dossier

modifie les permissions pour le dossier et tous les fichiers du dossier

Les permission sont données dans l'ordre user - groupe - autre

La lecture = 4

L'écriture = 2

L'exécution = 1

Tous les droits = 7

Etiquettes: 

Quelques commandes pour les dossiers

Pour connaitre le nom du dossier courant, saisir dans une console la commande :

$ pwd

Pour se rendre dans son répertoire personnel saisir dans une console l'une des 2 commandes suivantes:

$ cd

$ cd ~

Pour retourner au dossier précédent :

$ cd -

Rechercher les dossiers modifiés dans les dernières 24 heures et lister leurs contenus :

$ find /dossier_origine/* -mtime 0 -type d -exec ls -lht {} \;

l'option -mtime permet de spécifier le nombre de période de 24 heures.
l'option -type d permet d'indiquer que la recherche s'effectue uniquement sur les dossiers.
l'option -exec ls -lht {} \; permet d'exécuter la commande ls pour chaque dossier trouvé.

Etiquettes: 

Remplacer les espaces dans les noms de fichiers par des underscores

Le principe est simple. La commande find recherche tous les fichiers comportant un espace puis exécute pour chaque fichier trouvé la commande rename qui remplace tous les espaces par des underscores.

$ find ./ -depth -name "* *"  -exec rename 's/ /_/g' "{}" \;

Etiquettes: 

Renommer un fichier

Pour renommer un fichier, il suffit d'utiliser la commande mv.

Exemple :

$ mv ./foo ./bar

Cette commande va renommer le fichier foo en bar.

Voici une petite astuce bien sympa qui consiste à renommer un fichier en y ajoutant une extension quelconque.

$ mv ./foo{,.txt}

Cette commande va donc renommer le fichier foo en foo.txt

Supprimer le résultat d'une recherche (console)

En utilisant l'option -delete

$ sudo find /mon-repertoire -name mon-document -delete

ou en utilisant l'option -exec et la fonction rm

$ sudo find /mon-repertoire -name mon-document -exec rm {} \;

la chaine de caractère "{}" est remplacée par le résultat de la recherche

ou en combinant les commandes find, xargs et rm

$ find /mon-repertoire -name '*.txt' -print0 | xargs -0 rm -f
$ find /mon-repertoire -name '*.txt' -print0 | xargs -I '{}' -0 rm -f {}

L'option -print0 de la commande find et -0 de la commande xargs permet de prendre en compte les fichiers comportant des espaces dans leurs noms.

Synchroniser des dossiers et des fichiers

Documentation Ubuntu

La synchronisation de dossiers et/ou de fichiers se fait via le programme RSYNC

$ sudo rsync -av --del /source_a_sauvegarder/    /destination_de_la_sauvegarde/


-a : préserve l'intégralité des dossiers et fichiers

-v : verbose (affiche le détail à l'écran)

--del : supprime les fichiers et dossiers de destination qui ne sont plus dans source


rsync via SSH

$ rsync -a /source/ login@serveur.org:/destination/

Transfert de fichiers via SCP

SCP (Secure Copy Protocol) est un protocole permettant le transfert de fichiers en toute sécurité d'une machine locale vers une machine distante et inversement. Tous les transferts de fichiers sont effectués via le protocol SSH.


Transférer le fichier local file1.txt vers le répertoire courant de la machine distante 192.168.1.100

$ scp ./file1.txt user@192.168.1.100:./

Transférer le fichier file1.txt de la machine distante vers le répertoire courant de la machine locale

$ scp user@192.168.1.100:./file1.txt ./

Transférer tout le répertoire local /var/test vers le répertoire /tmp de la machine distante 192.168.1.100

$ scp -r /var/test user@192.168.1.100:/tmp/

Transférer un fichier via SSH

SSH permet d'utiliser des pipelines de commandes, et d'utiliser des tubes d'entrées/sorties comme toute autre commande, a ceci prêt que la redirection se fait vers, ou depuis la machine distante.

Celà peut, entre autre, servir à transférer des fichiers:

$ ssh serveur "cat fichier_distant" > fichier_local

L'utilité d'une telle commande est discutable, la commande scp faisant exactement la même chose.

Maintenant, imaginons un fichier de plusieurs mégas, et une bande passante assez limitée:

$ ssh serveur "gzip -c fichier_distant" > fichier_local.gz

ici, le serveur compresse le fichier, la commande écrit le fichier compressé sur le client.

On peut pousser encore plus loin, si l'on ne veut pas récupérer un fichier gzipé, mais diminuer quand même l'utilisation de la bande passante:

$ ssh serveur "gzip -c fichier_distant" |gunzip > fichier_local

Etiquettes: 

VIM : Supprimer les caractères BOM dans un fichier encodé en UTF-8

Pour supprimer les caractères BOM d'un fichier encodé en UTF-8, il suffit de l'ouvrir avec l'éditeur vim et d'exécuter la commande :set nobomb

Pour finir, enregistrer les modifications avec la commande :wq.

Etiquettes: 

Imprimer via la console

Pour imprimer un document via la console, il faut utiliser le commande LPR

Impression avec l'imprimante par défaut

$ lpr /mon-document

Impression en choisissant une imprimante

$ lpr -P nom-de-l-imprimante /mon-document

Impression avec un autre USER

$ lpr -U $USER /mon-document

Impression en mode mirroir

$ lpr -o mirror /mon-document

Etiquettes: 

Internet

Télécharger un fichier d'internet

Télécharger un fichier d'internet

$ wget   adresse_du_fichier_a_telecharger
 
$ fetch   adresse_du_fichier_a_telecharger

Etiquettes: 

Aspirer un site internet

Pour aspirer un site internet

$ wget -r -l5 -k -E "http://www.l_adresse_du_site.a_recuperer.com"

-r : récursif sur le site
-l5 : cinq niveaux de récursion au maximum
-k : convertir les destinations des liens pour une lecture locale
-E : convertir les types de fichier au format HTML (pour éviter que la lecture de sites en PHP ne foire en lecture sous Firefox).

A l'aide de l'outil httrack

$ sudo apt-get install httrack

Pour aspirer le site :

$ httrack --mirror http://mon-site.com

Pour mettre à jour le site aspirer (se placer dans le dossier du site):

$ httrack --update

Etiquettes: 

CURL: Afficher les informations SSL d'un domaine

La commande curl est très utile pour afficher, tester etc... un site internet.
On peut également utiliser la commande curl pour afficher les informations SSL des sites internet.

Par exemple, avec mon site internet https://quennec.fr

# curl -I --verbose https://quennec.fr*   Trying 51.159.70.99:443...
* TCP_NODELAY set
* Connected to quennec.fr (51.159.70.99) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=quennec.fr
*  start date: Mar 11 07:02:14 2024 GMT
*  expire date: Jun  9 07:02:13 2024 GMT
*  subjectAltName: host "quennec.fr" matched cert's "quennec.fr"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
...
<
* Connection #0 to host quennec.fr left intact

On peut voir qui a émis le certificat, ses dates de validité etc etc ...

Et sinon, la commande openssl permet de récupérer le certificat complet, voir openssl : Récupérer un certificat distant

CURL: Poster des données et des fichiers via un formulaire

La commande CURL peut être utilisée pour poster des données via un formulaire WEB.

Exemple avec la commande suivante:

$ curl \
--insecure \
--request POST \
--header 'Content-Type: multipart/form-data' \

'https://www.monsite.com/monformulaire' \
-F 'username=toto' \
-F 'password=pass4toto' \
-F 'date-de-naissance=11-02-82' \
-F "file=@monfichier.txt" \
--include

 

Détail des options:

--insecure permet d'utiliser une connexion sécurisée (https) avec un certificat auto-signé (non reconnu)
--request POST permet d'indiquer le type de la requête
--header 'Content-Type: multipart/form-data' permet d'indiquer l'en-tête de la requête
-F permet d'indiquer les valeurs pour les différents champs du formulaire
--include permet de récupérer l'en-tête de la réponse

infoPour poster un fichier, il est nécessaire de préfixer le nom du fichier avec l'arobase (@)

Etiquettes: 

CURL: appeler un webservice SOAP

Pour appeler un webservice SOAP avec la commande CURL, il suffit d'indiquer le bon content-type dans le header ainsi que le nom du fichier XML à envoyer.

# curl \
> --header "content-type: application/soap+xml; charset=utf-8" \
> --data @request.xml \
> http://$SERVER:$PORT/$ENDPOINT

infoNe pas oublier le symbole '@' devant le nom du fichier XML pour indiquer à la commande CURL qu'il s'agit d'un fichier.

Etiquettes: 

CURL: utiliser un serveur proxy

Pour utiliser un serveur proxy avec la commande curl, il suffit tout simplement de créer dans son home un fichier .curlrc

Il est possible d'y ajouter différents paramètres liés à la commande curl

$ cat .curlrc
--proxy-user "user_toto:pass_toto"
--proxy "http://proxy.mondomaine.local:3128/"
--noproxy "localhost,127.0.0.1,mondomaine.local"

Ne pas oublier de paramétrer les accès au fichier

$ chmod 0600 .curlrc

 

Etiquettes: 

Créer un sprite CSS sans se prendre la tête

Qu'est ce qu'un sprite CSS ?

En CSS, un sprite est une image unique regroupant plusieurs images différentes.
Cela permet de réduire les requêtes http pour les images insérées dans une page html.
Les positions absolues de chaque images sont indiquées dans un fichier css.
Avec ce procédé, l'insertion d'une image dans une page html ne se fait plus avec la balise <img> mais avec une balise <div>  ayant comme nom de "class" celui correspondant à l'image souhaitée et indiqué dans le fichier css.

Il existe sous Debian/Ubuntu, mais également pour Windows et OSX, une commande qui permet de générer un sprite CSS à partir d'un dossier contenant des images.

Cette commande se nomme Glue.

Pour l'installer sous Debian/Ubuntu :

$ sudo apt-get install libjpeg62 libjpeg62-dev zlib1g-dev python-dev python-pip
$ sudo pip install glue

ou

$ sudo apt-get install glue-sprite

Créer un sprite CSS tout simplement

$ glue source_dir output_dir

Par exemple :

$ glue famfamfam_flag_icons/png sprite

Le dossier famfamfam_flag_icons/png contient exactement 247 images PNG et la commande glue va donc créer un unique fichier PNG dans le dossier sprite regroupant toutes les images et un fichier css indiquant les positions de chaque image.

$ ls -1 famfamfam_flag_icons/png
ad.png
ae.png
af.png
ag.png
ai.png
al.png
am.png
an.png
ao.png
...
$ ls -1 sprite/
png.css
png.png
 

Il est possible de générer également la page html permettant de visualiser le résultat en ajoutant l'option --html.

$ glue famfamfam_flag_icons/png sprite --html
$ ls -1 sprite/
png.css
png.html
png.png

La commande glue génère les fichiers css, html et png en fonction du nom du dossier indiqué en source.
Par défaut, le nom des "class" commence par sprite-leNomDuDossierSource-leNomDuFichier.

$ head sprite/png.css 
/* glue: 0.4 hash: 1c2ef5e4ec */
.sprite-png-zw,
.sprite-png-zm,
.sprite-png-za,
.sprite-png-yt,
.sprite-png-ye,
.sprite-png-ws,
.sprite-png-wf,
.sprite-png-wales,
.sprite-png-vu,
 
Il est possible de modifier le nommage des "class" avec l'option --namespace=NAMESPACE.
 

$ glue famfamfam_flag_icons/png sprite --namespace=monSprite

$ head sprite/png.css 
/* glue: 0.4 hash: d6adc62ab7 */
.monSprite-png-zw,
.monSprite-png-zm,
.monSprite-png-za,
.monSprite-png-yt,
.monSprite-png-ye,
.monSprite-png-ws,
.monSprite-png-wf,
.monSprite-png-wales,
.monSprite-png-vu,

La liste complète des options est disponible avec l'option -h.

$ glue -h

Voici un exemple de l'image globale générée à l'aide de la commande glue.

sprite

Navigateur en ligne de commande

$ w3m http://adresse_du_site

Etiquettes: 

La commande mount

Monter une clé USB ou un disque dur externe en ligne de commande avec la commande mount.

  • Brancher la clé USB ou le disque dur externe
  • Avec la commande fdisk et l'option -l, afficher à l'écran la liste des périphériques connectés afin d'obtenir les informations nécessaires au montage.
    Rechercher dans la liste le périphérique concerné.
    Dans mon cas, il s'agit d'une clé USB de 4 Go et correspondant au dernier périphérique de la liste (/dev/sdd)

$ sudo fdisk -l
...
Disque /dev/sdd: 4009 Mo, 4009754624 octets
23 têtes, 23 secteurs/piste, 14804 cylindres
Unités = cylindres de 529 * 512 = 270848 octets
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Identifiant de disque : 0xc3072e18
     
Périphérique Amorce  Début        Fin      Blocs     Id  Système
/dev/sdd1   *          16       14805     3911744    b  W95 FAT32
$

  • Les infos nécessaires au montage sont celles indiquées en rouge.
    C'est à dire, le fichier correspondant à la partition de la clé ou du disque externe (/dev/sdd1).
    Dans le cas d'un périphérique ayant plusieurs partitions, la liste des périphériques contiendrait plusieurs lignes (/dev/sddx).
    Et enfin, le système de fichiers (FAT32)

La syntaxe de la commande mount est la suivante :

mount -t type device dir

type correspond au système de fichiers du périphérique à monter.
device correspond au fichier de la partition à monter.
dir correspond au répertoire dans lequel sera monté le périphérique.

Pour le paramètre type, il existe différents formats pris en charge :

adfs,  affs,  autofs, cifs, coda, coherent, cramfs, debugfs, devpts, efs, ext, ext2, ext3, ext4, hfs, hfsplus, hpfs,iso9660, jfs, minix, msdos,  ncpfs,  nfs,  nfs4,  ntfs,  proc,  qnx4,ramfs,  reiserfs,  romfs,  squashfs,smbfs, sysv, tmpfs, ubifs, udf, ufs, umsdos, usbfs, vfat, xenix, xfs,xiafs

  • Créer, s'il n'existe pas déjà, le répertoire destiné à recevoir le montage.

$ sudo mkdir /mnt/ma_cle_usb
$

  • Pour terminer, procéder au montage de la partition /dev/sdd1 dans le répertoire /mnt/ma_cle_usb avec le système de fichiers vfat.
    vfat correspondant au système de fichiers FAT32.

$ sudo mount -t vfat /dev/sdd1 /mnt/ma_cle_usb/
$

Pour vérifier la bonne exécution du montage

$ mount
...
/dev/sdd1 on /mnt/ma_cle_usb type vfat (rw)
$

Avec cette commande, on s'aperçoit que le prériphérique est monté en RW (lecture/écriture).
Pour monter le périphérique en lecture seule, il suffit d'utiliser l'option -r.

$ sudo mount -r -t vfat /dev/sdd1 /mnt/ma_cle_usb/
$ mount
...
/dev/sdd1 on /mnt/ma_cle_usb type vfat (ro)
$

Pour démonter le périphérique

$ sudo umount -f /mnt/ma_cle_usb/
$

L'option -f permet de forcer le démontage

Une erreur souvent rencontrée

Un périphérique est monté et l'erreur suivante apparait lors du démontage

$ sudo umount /mnt/ma_cle_usb/
démontage : /mnt/ma_cle_usb: périphérique occupé.
       (Dans certains cas, des infos sur les processus l'utilisant
        sont récupérables par lsof(8) ou fuser(1))
$

Vérifier le répertoire dans lequel on se trouve

$ pwd
/mnt/ma_cle_usb
$

La commande pwd indique que je me trouve dans le répertoire que je veux démonter.
Le démontage est donc impossible car le répertoire concerné est en cours d'utilisation.
Il suffit donc de se placer dans un autre répertoire et d'exécuter à nouveau la commande umount.

$ cd
$ sudo umount /mnt/ma_cle_usb/
$


La commande mount utilisée avec l'option -a permet d'exécuter tous les points de montage inscrits dans le fichier /etc/fstab

$ sudo mount -a

Très pratique pour vérifier toutes les modifications effectuées dans le fichier /etc/fstab


Si une partition n'a plus d'espace libre disponible, elle est montée en lecture seule.

# mount
...
/dev/sdb1 on /mnt/datas type ext4 (ro)
...

Pour pouvoir y faire le ménage, il est nécessaire de pouvoir y accéder en écriture.

Pour ce faire,  la partition doit être remontée en écriture de cette manière:

# mount -o remount,rw /mnt/datas
# mount
...
/dev/sdb1 on /mnt/datas type ext4 (rw)
...

Etiquettes: 

Liens symboliques

Etiquettes: 

Créer un lien symbolique

Pour créer un lien symbolique

Avant toute chose, se mettre dans le répertoire dans lequel on souhaite créer le lien :

$ ln  -s  /nom_du_dossier_source  nom_du_lien
 
$ ln  -s  /emplacement/nom_du_fichier_source  nom_du_lien

Ou en utlisant les chemins absolus :

$ ln -s /emplacement/nom_du_fichier_source /emplacement/nom_du_lien

Etiquettes: 

Supprimer un lien symbolique

Pour supprimer un lien symbolique

Se positionner au préalable dans le répertoire contenant le lien symbolique à supprimer :

$ cd mon_repertoire
$ sudo unlink nom_du_lien

Ou avec son chemin absolu :

$ unlink /mon_repertoire/nom_du_lien

Les nombres

Générer une séquence de nombre

Sous GNU/Linux, il existe une commande permettant de générer une séquence de nombre.

Il s'agit de la commande seq.

Cette commande s'utilise avec ou sans options et avec un nombre d'arguments allant de 1 à 3.

Un seul argument indique la fin de la séquence.
Deux arguments indiquent le début et la fin de la séquence.
Trois arguments indiquent le début, le pas et la fin de la séquence.

Générer une séquence simple jusqu'à 10 :

$ seq 10
1
2
3
4
5
6
7
8
9
10

Générer une séquence allant de 5 à 10 :

$ seq 5 10
5
6
7
8
9
10

Générer une séquence allant de 1 à 10 avec un pas de 2 :

$ seq 1 2 10
1
3
5
7
9

Par défaut, le séparateur est le retour chariot. L'option -s permet de modifier ce séparateur :

$ seq -s "-" 1 2 10
1-3-5-7-9

L'option -w permet d'avoir une séquence dont les nombres ont le même nombre de caractères :

$ seq -w 0 100
000
001
002
003
004
005
006
007
008
009
...
098
099
100

Parcourir une séquence avec une boucle for :

$ for i in $(seq 0 2 10); do echo $i; done
0
2
4
6
8
10

Il existe une autre méthode permettant de générer une séquence de nombres, ou de lettres :

$ echo {1..5}
1 2 3 4 5

$ echo {5..-4}
5 4 3 2 1 0 -1 -2 -3 -4

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

$ echo "Nombre #"{1..7},
Nombre #1, Nombre #2, Nombre #3, Nombre #4, Nombre #5, Nombre #6, Nombre #7,

$ echo {1..5}{x..z}" +" "..."
1x + 1y + 1z + 2x + 2y + 2z + 3x + 3y + 3z + 4x + 4y + 4z + 5x + 5y + 5z + ...

$ echo {001..090}
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090

$ for j in {1..10}; do echo $j; done
1
2
3
4
5
6
7
8
9
10

Générer toutes les combinaisons possibles pour les immatriculations automobiles françaises :

$ echo  {A..Z}{A..Z}"-"{001..999}"-"{A..Z}{A..Z}

Attention. Cette commande génère un peu moins de 460 millions de données.

Etiquettes: 

Trier un tableau de valeurs numériques

Initialisation du tableau A :

$ TAB_A=(25 46 98 3 9 10 21 15 6)

$ echo ${TAB_A[*]}
25 46 98 3 9 10 21 15 6

Initialisation du tableau B contenant les valeurs triées du tableau A :

$ TAB_B=($(echo ${TAB_A[*]} | sed 's/ /\n/g' | sort -n))

$ echo ${TAB_B[*]}
3 6 9 10 15 21 25 46 98

L'astuce consiste à afficher les valeurs du tableau A, puis substituer les espaces entre les différentes valeurs par des retours à la ligne et enfin utiliser la commande sort pour trier les valeurs.

 

Une calculatrice en ligne de commande

Sous Debian/Ubuntu, il existe une commande qui permet de transformer la console en calculatrice.

Cette commande s'appelle bc.

Pour l'installer :

$ apt-get install bc

Utilisation (en rouge les résultats affichés) :

$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
5*8
40
15+9
24
45/8
5

Sans option, la commande bc exécute les calculs sans les décimales.

Pour afficher les décimales, il suffit d'utiliser l'option -l

$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
5*8
40
9-5
4
6+8
14
40/5
8.00000000000000000000
45/8
5.62500000000000000000

Par défaut, l'option -l affiche 20 chiffres après la virgule.
Pour modifier cette valeur, il suffit d'utiliser la fonction scale.

$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=5
45/8
5.62500

Il est également possible d'utiliser des variables.

$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=5
a=10
b=3
c=a+b
d=a-b
e=a*b
f=a/b
c
13
d
7
e
30
f
3.33333
 
Les différentes fonctions mathématiques ne sont pas oubliées.
 
$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=5
5^3         #PUISSANCE
125
sqrt(9)     #RACINE CARREE
3.00000
45%8        #MODULO
0
scale=0
45%8        #MODULO
5
 
ATTENTION, pour le modulo, initialiser le scale à 0 avant l'opération.
 

Calculer la valeur de pi :

pi est égal à 4 fois l'arc tangente de 1 radian.

$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
pi=4*a(1)     # On calcul la valeur de pi
pi
3.14159265358979323844
 
Convertir des degrés en radian et inversement :
 
$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
pi=4*a(1)     # On calcul la valeur de pi
pi
3.14159265358979323844
45*(pi/180)     # 45 degrés en radian
.78539816339744830920
.78539816339744830920*(180/pi)     # 0.78.... radian en degrés
44.99999999999999997650
 
et les sinus, cosinus et arc tangente
 
$ bc -l
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
pi=4*a(1)     # On calcul la valeur de pi
r=45*(pi/180)     # On convertit les degrés en radian (45°)
s(r)    # On calcul le sinus
.70710678118654752410
c(r)     # On calcul le cosinus
.70710678118654752469
a(r)     # On calcul l'arc tangente
.66577375002835386333
 
Toute l'aide disponible avec : 
 
$ man bc
 
Il est également possible d'utiliser la commande bc pour initialiser une variable bash :
 
$ PI=$(echo "scale=10; 4*a(1)" | bc -l)
$ echo $PI
3.1415926532

 

Etiquettes: 

test - integer expression expected

J'ai récemment été confronté à l'erreur "integer expression expected" lors d'un test de deux entiers dans un de mes scripts.

Après plusieurs tests, j'ai finalement compris d'où venait mon erreur.
La variable que je testais ne contenait pas que des chiffres.
Elle n'était donc pas reconnue comme un entier.

Un simple "echo" de ma variable ne faisait pas apparaître tous les caractères de celle-ci.
Pour m'en apercevoir, j'ai dû utiliser la commande "od".

Exemple :

Je vais générer volontairement une erreur au niveau du contenu de la variable à tester en y insérant un caractère spécifique.

$ a=$(echo -e "20\a")
$ echo $a
20

La commande "echo" ne fait pas apparaitre le caractère spécifique contenu dans ma variable.

Je test si ma variable est égale à 20 (par exemple).

$ test $a -eq 20
-bash: test: 20: integer expression expected

J'ai bien une erreur car effectivement ma variable ne peut pas être considérée comme un entier à cause du caractère spécifique qu'elle contient.

La preuve avec la commande "od -c" ...

$ echo -n $a | od -c
0000000   2   0  \a

... le caractère spécifique "\a" apparait bien.

En conclusion, penser à bien vérifier le contenu des variables avec la commande "od" pour voir si des caractères spécifiques ne s'y sont pas insérés.

Etiquettes: 

Mail

Etiquettes: 

Envoyer un mail

Pour envoyer un mail via une console Ubuntu :

$ mail -s "sujet du mail" adresse@destinataire.fr

Appuyer sur Entrée, renseigner si besoin une adresse CC, appuyer à nouveau sur Entrée, saisir le texte du message, appuyer une dernière fois sur Entrée puis appuyer sur les touches ctrl+d

Si cette commande est enregistrée dans un script, et si ce script est exécuté automatiquement via une tâche Cron, il n'y a pas de demande concernant l'adresse CC et le texte du message.

Pour envoyer un mail avec une pièce jointe via une console Ubuntu :

Installer le paquet mutt :

$ sudo apt-get install mutt

Envoyer un mail via cette commande :

$ mutt -s "sujet du mail" adresse@destinataire.fr -a /nom_du_fichier
$ mutt -s "Sujet du mail" -a /mon_dossier/mon_fichier -- destinataire@domaine.com

Valider les différentes étapes puis appuyer sur y pour envoyer le mail.

Si cette commande est utilisée dans un script et exécuté automatiquement via une tâche cron, le message est envoyé sans aucune étape supplémentaire à valider

Attention, dans la commande mutt, l'ajout d'une pièce jointe se fait exclusivement en dernier paramètre sans compter l'adresse du destinataire.

Pour envoyer un mail via Telnet :

$ telnet adresse_ip_du_serveur 25
helo votre_nom
mail from:<votreadresse@votredomaine.fr>
rcpt to:<adressedestinataire@domaine.fr>
data
votre message
. (le point est très important, il indique la fin du message)
quit

Etiquettes: 

Envoyer un mail en HTML

Il est quand même plus agréable de recevoir un mail en HTML qu'en texte brut.

La commande mail propose l'option -a qui permet de modifier l'en-tête du message.

Comme des exemples valent mieux qu'une grande explication :

$ echo "<html><head></head><body><p>Mon premier mail en <b>HTML</b></p></body></html>" | mail -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "Message" moi@gmail.lui

C'est quand même vachement simple.

Ecrire du HTML avec la commande echo, c'est sympa, mais un peu chiant si le message est long et pleins de balises.

Qu'à cela ne tienne, on écrit le HTML dans un fichier et on envoie le contenu du fichier par mail.

$ cat testMail.html
<html>
        <head>
        </head>
        <body>
        <h1>Mon Premier Mail En HTML</h1>
        <p>
                Qu'est ce que c'est chouette de pouvoir écrire des mails en HTML
        </p>
        <P>
                On peut y insérer des liens ... <br />
                <a href="http://www.quennec.fr">Mon Site</a> <br />
                <a href="mailto:moi@gmail.lui">Mon mail</a>
        </p>
        </body>
</html>

$ cat testMail.html | mail -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "Message" moi@gmail.lui

De mieux en mieux.

Bonus :

Je m'envoie très souvent par mail des données issues de requêtes SQL.
Qu'est ce que c'est moche la représentation des tableaux en texte brut.
L'option -H utilisée avec la commande mysql permet de formater le résultat en tableau HTML.

Le top du top.

$ ( echo -n "<html><head></head><body>" ; echo "select col1, col2, col3 from maTable" | mysql -H -h localhost -u user -ppass maBase ; echo "</body></html>" ) | mail -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "Le Résultat de ma requête" moi@gmail.lui

Petite explication :

On affiche, avec la commande echo, la balise <html><head> </head> et <body> sans retour à la ligne avec l'option -n.
On enchaine avec la requête SQL (echo "select ...") qu'on transmet à mysql avec un "|" et qu'on exécute avec l'option -H pour afficher le résultat au format HTML.
On enchaine avec la commande echo pour afficher la balise fermante </body> et </html>.
On regroupe toutes ces commandes entre parenthèses afin de les envoyer à la commande mail avec un "|".
On indique à la commande mail d'envoyer le contenu du mail au fromat HTML grâce aux paramètres de l'option -a.

La classe, non ?

Etiquettes: 

OpenSSL

OpenSSL est un programme utilisant les protocoles de chiffrement SSL (v2/v3)  et TLS (v1).

Il est utilisé principalement pour :

  • Créer des clés RSA, DH et DSA.
  • Créer des certificats X509, CSR et CRL.
  • Calculer des sommes de fichiers.
  • Chiffrer et déchiffrer des chaines de caractères/des fichiers.
  • Effectuer des tests SSL/TLS entre un client et un serveur.

Quelques liens consernant SSL

Autorités de certification:

cacert.org
let's encrypt

Tests de sécurité SSL:

SSL Labs

Générateur de configuration SSL pour apache, nginx, haproxy, aws elb:

Mozilla SSL Configuration Generator

 

Etiquettes: 

openssl : Afficher la version installée

$ openssl version
OpenSSL 0.9.8o 01 Jun 2010

$ openssl version -a
OpenSSL 0.9.8o 01 Jun 2010
built on: Mon Feb 11 21:27:58 UTC 2013
platform: debian-i386-i686/cmov
options:  bn(64,32) md2(int) rc4(idx,int) des(ptr,risc1,16,long) blowfish(idx)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DTERMIO -O3 -march=i686 -Wa,--noexecstack -g -Wall -DOPENSSL_BN_ASM_PART_WORDS -DOPENSSL_IA32_SSE2 -DSHA1_ASM -DMD5_ASM -DRMD160_ASM -DAES_ASM
OPENSSLDIR: "/usr/lib/ssl"
$
Etiquettes: 

openssl : Afficher les sous-commandes disponibles

$ openssl help
openssl:Error: 'help' is an invalid command.
 
Standard commands
asn1parse      ca             ciphers        crl            crl2pkcs7
dgst           dh             dhparam        dsa            dsaparam
ec             ecparam        enc            engine         errstr
gendh          gendsa         genrsa         nseq           ocsp
passwd         pkcs12         pkcs7          pkcs8          prime
rand           req            rsa            rsautl         s_client
s_server       s_time         sess_id        smime          speed
spkac          verify         version        x509
 
Message Digest commands (see the `dgst' command for more details)
md2            md4            md5            rmd160         sha
sha1
 
Cipher commands (see the `enc' command for more details)
aes-128-cbc    aes-128-ecb    aes-192-cbc    aes-192-ecb    aes-256-cbc
aes-256-ecb    base64         bf             bf-cbc         bf-cfb
bf-ecb         bf-ofb         cast           cast-cbc       cast5-cbc
cast5-cfb      cast5-ecb      cast5-ofb      des            des-cbc
des-cfb        des-ecb        des-ede        des-ede-cbc    des-ede-cfb
des-ede-ofb    des-ede3       des-ede3-cbc   des-ede3-cfb   des-ede3-ofb
des-ofb        des3           desx           rc2            rc2-40-cbc
rc2-64-cbc     rc2-cbc        rc2-cfb        rc2-ecb        rc2-ofb

Comme indiqué, il est possible d'afficher les détails des sous-commandes dgst et enc.

$ openssl dgst -h
unknown option '-h'
options are
-c              to output the digest with separating colons
-d              to output debug info
-hex            output as hex dump
-binary         output in binary form
-sign   file    sign digest using private key in file
-verify file    verify a signature using public key in file
-prverify file  verify a signature using private key in file
-keyform arg    key file format (PEM or ENGINE)
-signature file signature to verify
-binary         output in binary form
-hmac key       create hashed MAC with key
-engine e       use engine e, possibly a hardware device.
-md5            to use the md5 message digest algorithm (default)
-md4            to use the md4 message digest algorithm
-md2            to use the md2 message digest algorithm
-sha1           to use the sha1 message digest algorithm
-sha            to use the sha message digest algorithm
-sha224         to use the sha224 message digest algorithm
-sha256         to use the sha256 message digest algorithm
-sha384         to use the sha384 message digest algorithm
-sha512         to use the sha512 message digest algorithm
-mdc2           to use the mdc2 message digest algorithm
-ripemd160      to use the ripemd160 message digest algorithm

$ openssl enc -h
unknown option '-h'
options are
-in <file>     input file
-out <file>    output file
-pass <arg>    pass phrase source
-e             encrypt
-d             decrypt
-a/-base64     base64 encode/decode, depending on encryption flag
-k             passphrase is the next argument
-kfile         passphrase is the first line of the file argument
-md            the next argument is the md to use to create a key
                 from a passphrase.  One of md2, md5, sha or sha1
-K/-iv         key/iv in hex is the next argument
-[pP]          print the iv/key (then exit if -P)
-bufsize <n>   buffer size
-engine e      use engine e, possibly a hardware device.
Cipher Types
-aes-128-cbc               -aes-128-cfb               -aes-128-cfb1
-aes-128-cfb8              -aes-128-ecb               -aes-128-ofb
-aes-192-cbc               -aes-192-cfb               -aes-192-cfb1
-aes-192-cfb8              -aes-192-ecb               -aes-192-ofb
-aes-256-cbc               -aes-256-cfb               -aes-256-cfb1
-aes-256-cfb8              -aes-256-ecb               -aes-256-ofb
-aes128                    -aes192                    -aes256
-bf                        -bf-cbc                    -bf-cfb
-bf-ecb                    -bf-ofb                    -blowfish
-cast                      -cast-cbc                  -cast5-cbc
-cast5-cfb                 -cast5-ecb                 -cast5-ofb
-des                       -des-cbc                   -des-cfb
-des-cfb1                  -des-cfb8                  -des-ecb
-des-ede                   -des-ede-cbc               -des-ede-cfb
-des-ede-ofb               -des-ede3                  -des-ede3-cbc
-des-ede3-cfb              -des-ede3-cfb1             -des-ede3-cfb8
-des-ede3-ofb              -des-ofb                   -des3
-desx                      -desx-cbc                  -rc2
-rc2-40-cbc                -rc2-64-cbc                -rc2-cbc
-rc2-cfb                   -rc2-ecb                   -rc2-ofb
-rc4                       -rc4-40
Etiquettes: 

openssl : Tester les performances de son système

L'option speed permet de tester les capacités du sytème pour encrypter des données avec les différents algorithmes de cryptage pendant une période donnée.

$ openssl speed
Doing md2 for 3s on 16 size blocks: 149573 md2's in 2.54s
Doing md2 for 3s on 64 size blocks: 82254 md2's in 2.74s
Doing md2 for 3s on 256 size blocks: 26039 md2's in 2.58s
Doing md2 for 3s on 1024 size blocks: 8149 md2's in 2.93s
Doing md2 for 3s on 8192 size blocks: 1072 md2's in 2.99s
Doing md4 for 3s on 16 size blocks: 3651107 md4's in 2.87s
Doing md4 for 3s on 64 size blocks: 3393515 md4's in 3.00s
Doing md4 for 3s on 256 size blocks: 2340602 md4's in 2.99s
Doing md4 for 3s on 1024 size blocks: 1045777 md4's in 3.00s
Doing md4 for 3s on 8192 size blocks: 167900 md4's in 2.96s
Doing md5 for 3s on 16 size blocks: 3080791 md5's in 2.99s
Doing md5 for 3s on 64 size blocks: 2599131 md5's in 3.00s
Doing md5 for 3s on 256 size blocks: 1577068 md5's in 2.87s
Doing md5 for 3s on 1024 size blocks: 666677 md5's in 2.99s
Doing md5 for 3s on 8192 size blocks: 102011 md5's in 2.99s
...

Pour l'exemple, mon système est capable d'encrypter 666 677 fichiers de 1024 octets en 3s maximum.

Il est possible d'effectuer le test sur un algorithme précis :

$ openssl speed sha256
Doing sha256 for 3s on 16 size blocks: 1626266 sha256's in 2.94s
Doing sha256 for 3s on 64 size blocks: 958515 sha256's in 3.00s
Doing sha256 for 3s on 256 size blocks: 415732 sha256's in 2.99s
Doing sha256 for 3s on 1024 size blocks: 127673 sha256's in 2.99s
Doing sha256 for 3s on 8192 size blocks: 16527 sha256's in 2.87s
OpenSSL 0.9.8o 01 Jun 2010
built on: Mon Feb 11 21:27:58 UTC 2013
options:bn(64,32) md2(int) rc4(idx,int) des(ptr,risc1,16,long) aes(partial) blowfish(idx)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DTERMIO -O3 -march=i686 -Wa,--noexecstack -g -Wall -DOPENSSL_BN_ASM_PART_WORDS -DOPENSSL_IA32_SSE2 -DSHA1_ASM -DMD5_ASM -DRMD160_ASM -DAES_ASM
available timing options: TIMES TIMEB HZ=100 [sysconf value]
timing function used: times
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
sha256            8850.43k    20448.32k    35594.45k    43724.80k    47173.93k

Il est possible également de tester les performances SSL d'un serveur distant :

$ openssl s_time -connect google.fr:443 -www / -new -ssl3
No CIPHER specified
Collecting connection statistics for 30 seconds
333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
 
186 connections in 0.20s; 930.00 connections/user sec, bytes read 183024
186 connections in 31 real seconds, 984 bytes read per connection

Il est possible également de simuler un serveur SSL pour effectuer le test de performance :

1 - Générer un certificat pour le serveur simulé (répondre aux questions posées)

$ openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout cert.pem -out cert.pem

2 - Démarrage du serveur simulé

$ openssl s_server -cert cert.pem -www

3 - Dans un autre terminal, tester les performances du serveur simulé

$ openssl s_time -connect localhost:4433 -www / -new -ssl3
No CIPHER specified
Collecting connection statistics for 30 seconds
333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333...
33333333333333333333333333
 
3301 connections in 6.07s; 543.82 connections/user sec, bytes read 8506677
3301 connections in 31 real seconds, 2577 bytes read per connection
Etiquettes: 

openssl : Encoder une chaîne de caractères en base64/md5

Pour encoder une chaîne de caractères en base64, utiliser la commande suivante :

$ echo -n "ceci est mon mot de passe" | openssl base64
Y2VjaSBlc3QgbW9uIG1vdCBkZSBwYXNzZQ==

Pour encoder une chaîne de caractères en md5, utiliser la commande suivante :

$ echo -n 'ceci est un mot de passe' | openssl md5
7c1cc4a1a08c9352b50151dfa5d6edf3
Etiquettes: 

openssl : Chiffrer-Déchiffrer une chaine de caractères ou un fichier

Encodage en BASE64

Encoder le contenu d'un fichier sur la sortie standard :

$ cat fic1
systèmes
$ openssl enc -base64 -in fic1
c3lzdMOobWVzCg==

Encoder le contenu d'un fichier et écrire le résultat dans un autre fichier :

$ openssl enc -base64 -in fic1 -out fic1.enc
$ cat fic1.enc
c3lzdMOobWVzCg==

Encoder une chaine de caractères :

$ echo -n "secret" | openssl enc -base64
c2VjcmV0

Ne pas oublier d'ajouter l'option -n à la commande echo sinon un retour chariot sera ajouté à la chaine encodée.

Décoder une chaine de caractères :

$ echo "c2VjcmV0" | openssl enc -base64 -d
secret

Décoder le contenu d'un fichier sur la sortie standard :

$ openssl enc -base64 -in fic1.enc -d
systèmes

Décoder le contenu d'un fichier et écrire le résultat dans un autre fichier :

$ openssl enc -base64 -d -in fic1.enc -out fic1.dec
$ cat fic1.dec
systèmes

Chiffrer un fichier avec un algorithme de chiffrement et un mot de passe

1 - Choisir un algorithme de chiffrement :

$ openssl list-cipher-commands

Voir également la page de manuel de la commande enc.

2 - Chiffrer un fichier avec l'algorithme DES3 et écrire le contenu dans un autre fichier (génération d'un fichier binaire) :

$ openssl enc -des3 -salt -in fic1 -out fic1.des3
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:
$ cat fic1.des3
Salted__d▒j▒G(▒▒r▒d}▒i▒7▒▒▒▒M▒#

Pour le déchiffrage (indication du mot de passe avec l'option -pass) :

$ openssl enc -des3 -d -salt -in fic1.des3 -out file.txt -pass pass:aaaa
$ cat file.txt
systèmes

Chiffrage DES3 mais avec un encodage en BASE64 (pour un envoi par mail par exemple) :

$ openssl enc -des3 -a -salt -in fic1 -out fic1.des3
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:
$ cat fic1.des3
U2FsdGVkX1/Cl8Jr0Aw/3eDLegGPc7meTjWbUQZcNkw=

Pour le déchiffrage :

$ openssl enc -des3 -d -a -salt -in fic1.des3 -out file.txt
enter des-ede3-cbc decryption password:
$ cat file.txt
systèmes

Pour chiffrer un fichier en indiquant le mot de passe dans la ligne de commande (avec l'option -pass) :

$ openssl enc -des3 -salt -in fic1 -out fic1.des3 -pass pass:aaaa

Pour déchiffrer un fichier en indiquant le mot de passe dans un fichier :

$ openssl enc -des3 -salt -in fic1 -out fic1.des3 -pass file:/root/pass

Dans ce cas, seule la première ligne du fichier est utilisée comme mot de passe ou phrase de passe.

Etiquettes: 

openssl : Clé de contrôle MD5 ou SHA1 d'un fichier

Pour afficher la clé de contrôle d'un fichier, il suffit d'utiliser la sous-commande dgst suivi de l'algorithme voulu.

$ openssl dgst -md5 /var/log/syslog
MD5(/var/log/syslog)= 16973fc98773afeea8b0c9c3be3ab677
$ openssl dgst -sha1 /var/log/syslog
SHA1(/var/log/syslog)= 3b0634f6793d5ed1a6260a585ea92d22badc2070

Il existe également une commande propre à chaque algorithme et qui retourne le même résultat :

$ md5sum /var/log/syslog
16973fc98773afeea8b0c9c3be3ab677  /var/log/syslog
$ sha1sum /var/log/syslog
3b0634f6793d5ed1a6260a585ea92d22badc2070  /var/log/syslog

Il est possible également de signer la clé de contrôle d'un fichier à l'aide d'une clé privée afin d'éviter toute modification sans permission :

Génération du fichier syslog.sha1 contenant la clé de contrôle et sécurisé par une clé privée (mykey2.pem).

Cliquez ici pour la génération des clés.

$ openssl dgst -sha1 -sign mykey2.pem -out syslog.sha1 /var/log/syslog

Il ne faut pas oublier de distribuer avec le fichier contenant la clé de contrôle (syslog.sha1) le fichier contenant la clé publique correspondant à la clé privée utilisée (pubkey.pem).

Par contre, ce qu'il ne faut surtout pas faire : distribuer sa clé privée (mykey2.pem).
Il faut la garder bien au chaud dans un endroit bien secret.

Pour la vérification de la clé de contrôle sécurisée, il suffit d'utiliser la commande suivante en y indiquant la clé publique correspondante à la clé privée utilisée lors de la génération de la clé de contrôle sécurisée.

$ openssl dgst -sha1 -verify pubkey.pem -signature syslog.sha1 /var/log/syslog
Verified OK

 

openssl : Exporter ou importer un certificat au format PKCS#12

Pour exporter un certificat PEM au format PKCS#12 :

1 - Générer le certificat au format PEM :

$ openssl req -x509 -nodes -days 365 -subj '/C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com' -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
Generating a 1024 bit RSA private key
...........++++++
............++++++
writing new private key to 'mycert.pem'
-----

2 - Exporter le certificat au format PKCS#12 :

$ openssl pkcs12 -export -out mycert.pfx -in mycert.pem -name "My Certificate"

Renseigner un phrase de passe si nécessaire.

Convertir un certificat PKCS#12 au format PEM :

$ openssl pkcs12 -in mycert.pfx -out mycert2.pem -nodes

Saisir la phrase de passe si nécessaire.

Pour protéger la clé privée à l'aide d'une phrase de passe :

$ openssl pkcs12 -in mycert.pfx -out mycert2.pem

La saisie d'une phrase de passe pour la protection de la clé privée est obligatoire.

 

Etiquettes: 

openssl : Extraire les informations d'un certificat

La commande suivante affiche toutes les informations du certificat indiqué :

$ openssl x509 -text -in mycert.pem

Qui a émis le certificat ?

$ openssl x509 -noout -in mycert.pem -issuer
issuer= /C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com

Pour qui a-t-il été émis ?

$ openssl x509 -noout -in mycert.pem -subject
subject= /C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com

Quelle est sa période de validité ?

$ openssl x509 -noout -in mycert.pem -dates
notBefore=Apr  9 15:28:28 2013 GMT
notAfter=Apr  9 15:28:28 2014 GMT

Toutes les infos précédentes :

$ openssl x509 -noout -in mycert.pem -issuer -subject -dates
issuer= /C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com
subject= /C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com
notBefore=Apr  9 15:28:28 2013 GMT
notAfter=Apr  9 15:28:28 2014 GMT

Quelle est sa valeur de hachage ?

$ openssl x509 -noout -in mycert.pem -hash
bf163efd

Quelle est son empreinte MD5 ?

$ openssl x509 -noout -in mycert.pem -fingerprint
SHA1 Fingerprint=C1:CD:DD:29:D1:8D:23:63:6D:3F:71:AD:7E:29:DE:26:FF:D4:11:17

Et à partir d'un certificat d'un site internet (plus de détail openssl : Récupérer un certificat distant)

$ echo | openssl s_client -connect quennec.fr:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text -noout -in -

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            04:f8:64:c9:8c:bb:f8:c9:6a:fd:3f:b9:b2:42:a0:c5:9c:ab
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = R3
        Validity
            Not Before: Mar 11 07:02:14 2024 GMT
            Not After : Jun  9 07:02:13 2024 GMT
        Subject: CN = quennec.fr
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:b3:d3:a3:d3:f3:2c:f8:75:d0:71:5f:8f:8d:c4:
                    ...
                    83:f0:69:50:7a:fe:ce:a5:48:64:e1:5a:41:a0:a3:
                    8b:25
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                F0:5D:1D:A5:87:7D:61:1D:AB:74:A4:DE:14:34:F4:D7:3A:52:56:F5
            X509v3 Authority Key Identifier:
                keyid:14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6

            Authority Information Access:
                OCSP - URI:http://r3.o.lencr.org
                CA Issuers - URI:http://r3.i.lencr.org/

            X509v3 Subject Alternative Name:
                DNS:*.quennec.fr, DNS:quennec.fr
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1

            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 3B:53:77:75:3E:2D:B9:80:4E:8B:30:5B:06:FE:40:3B:
                                67:D8:4F:C3:F4:C7:BD:00:0D:2D:72:6F:E1:FA:D4:17
                    Timestamp : Mar 11 08:02:14.835 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:46:02:21:00:C6:D5:B3:6D:A4:45:49:25:9B:47:C9:
                                21:20:61:57:67:E9:EB:5A:EB:3F:80:87:3E:EF:41:F6:
                                7C:CD:2C:46:61:02:21:00:D8:27:12:05:AF:5E:9A:0D:
                                91:E9:DA:C5:5A:27:27:25:1D:31:56:A3:6D:4D:26:21:
                                5D:E9:08:B0:5F:1E:5F:88
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 76:FF:88:3F:0A:B6:FB:95:51:C2:61:CC:F5:87:BA:34:
                                B4:A4:CD:BB:29:DC:68:42:0A:9F:E6:67:4C:5A:3A:74
                    Timestamp : Mar 11 08:02:14.890 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:44:02:20:41:9E:16:0F:BF:95:4F:FA:23:4F:A4:06:
                                ...
                                0F:CB:99:8B:E9:87
    Signature Algorithm: sha256WithRSAEncryption
         0c:dd:53:bc:97:e0:18:39:94:19:fa:28:7c:67:b9:35:a4:ca:
         ...
         04:ee:d9:9

 

Etiquettes: 

openssl : Générer des clés

De nos jours, il est conseillé de générer des clés d'une longueur de 2048 bits pour une meilleure sécurité.

Pour un serveur Apache (configuration SSL), il est conseillé de générer des clés sans phrase de passe. Dans le cas contraire, à chaque démarrage du service Apache, la phrase de passe sera demandée.

Avec OpenSSL, la clé privée contient également les informations de la clé publique. Il n'est donc pas nécessaire de générer la clé publique séparément.

Génération d'une clé RSA

Générer une clé simple :

$ openssl genrsa

Générer une clé de 2048 bits et la sauvegarder dans un fichier :

$ openssl genrsa -out mykey.pem 2048

Idem mais avec un cryptage DES3 et une phrase de passe :

$ openssl genrsa -des3 -out mykey.pem 2048

Génération d'une clé publique RSA

$ openssl rsa -in mykey.pem -pubout

Génération d'une clé DSA

Les clés DSA sont utilisées pour la signature d'objets divers.

Le processus de génération se déroule en deux étapes.

Premièrement, générer les paramètres permettant la génération de la clé :

$ openssl dsaparam -out dsaparam.pem 2048

Puis, générer la clé en fonction des paramètres générés ci-dessus :

$ openssl gendsa -des3 -out privkey.pem dsaparam.pem

Une phrase de passe est demandée lors de la génération de la clé DSA.

Pour ne pas avoir de phrase de passe, il suffit de supprimer le paramètre "-des3".

Supprimer la phrase de passe d'une clé privée RSA

Cette commande permet de générer une nouvelle clé privée (newkey.pem) à partir de celle protégée par une phrase de passe (mykey.pem).

$ openssl rsa -in mykey.pem -out newkey.pem

Supprimer la phrase de passe d'un certificat

Pour l'exemple, nous allons créer un certificat crypté (mycert.pem).
Ce fichier (mycert.pem) contiendra la clé privée et le certificat public et sera protégé par une phrase de passe.

$ openssl req -x509 -days 365 -subj '/C=FR/ST=Loire Atlantique/L=Ancenis/CN=www.monsite.fr/emailAddress=toto@gmail.com' -newkey rsa:2048-keyout mycert.pem -out mycert.pem

Le décryptage du certificat se déroule en deux étapes :

Génération de la clé privée à partir du certificat crypté (mycert.pem) vers le nouveau certificat (newcert.pem) :

$ openssl rsa -in mycert.pem -out newcert.pem

Ajout du certificat public dans le nouveau certificat (newcert.pem) :

$ openssl x509 -in mycert.pem >>newcert.pem

 

Etiquettes: 

openssl : Générer des données aléatoires

Générer une chaine aléatoire de 128 octets encodée en BASE64 :

$ openssl rand -base64 128
2/X1yDvXHdAsDYPBmToCNYFI9Vjhtt4ynVMFCcMfV0jJm+EytH22MEyMs7XV4rbB
6CdddwCD0T3sYu7hCF+Q5Dy72S3LKhZL5cbB8gaf2l+Guv/GCU/oiYTezRwLsAaN
82Sig1bnsyJeI3q67PsLS2yUhWTXiyRxv6/69EL/i30=

Générer une chaine aléatoire de 16 octets encodée en HEX :

$ openssl rand -hex 16
e114c246088060ef2af0b4f4f518b875

Générer un fichier binaire de 1024 octets :

$ openssl rand -out random-data.bin 1024
$ ls -l random-data.bin
-rw-r--r-- 1 root root 1024 Apr 11 18:21 random-data.bin

Générer une chaine aléatoire de 64 octets en utilisant le fichier spécial /dev/urandom et encodée avec la commande openssl en BASE64 :

$ head -c 64 /dev/urandom | openssl enc -base64
3wZ9RXe5bwCKzfEUElHOEJNb97SNN7QfKetKdSfAXvNhaiyCwWBWBEJupPAM2K/Q
6zx09thfwss2ffCGvencfg==

Un petit enchainement de commandes permettant de générer des mots de passe aléatoires :

$ echo $(head -c 32 /dev/urandom | strings -1) | sed 's/[[:space:]]//g'
18EC2Wl1-W

Générer un fichier d'une taille aléatoire comprise entre 0 et 50 Mo à l'aide de la fonction interne $RANDOM (50 Mo = 1024*1024*50):

$ openssl rand -out random-data.txt $(($RANDOM * 1024 * 1024 * 50 / 32767))

La fonction interne $RANDOM génère un nombre aléatoire compris entre 0 et 32767.

Générer un fichier texte de 10Mo

$ openssl rand -hex $((1024*1024*5)) > 10mbFile

1 byte étant égal à 2 caractères hexadécimal, il faut donc penser à diviser par 2 la taille du fichier désiré.

 

Etiquettes: 

openssl : Générer des mots de passe cryptés

Crypter simplement un mot de passe :

$ openssl passwd MyPasswd
hMTFVzaMHJcaA

Le mot de passe à crypter ne peut excéder 8 caractères.

Ajouter un "salt" au mot de passe à crypter (mot de passe salé ;o) :

$ openssl passwd -salt 12 MyPasswd
12q97u.MW0POc

Dans ce cas précis, le "salt" ne doit pas excéder 2 caractères.

Crypter un mot de passe avec l'algorithme MD5 :

$ openssl passwd -1 MyPasswd
$1$339eK0sF$MCPIqzRKLIWsKG1kXnQiw1

Pas de limitation au niveau de la longueur du mot de passe.

Idem mais en ajoutant un "salt" :

$ openssl passwd -1 -salt 12345678 MyPasswd
$1$12345678$I3fjXxePlXzjbz7gjOzwW0

Dans ce cas, le "salt" ne doit pas excéder 8 caractères.

Idem, mais avec un "salt" généré aléatoirement :

$ openssl passwd -1 -salt $(openssl rand -base64 8) MyPasswd
$1$nSQ9OINA$1a2pvpNHjYo41N0n6FqbQ.

 

Etiquettes: 

openssl : Générer un certificat auto-signé

Par défaut, OpenSSL utilise le fichier de configuration "/etc/ssl/openssl.cnf" pour la génération des certificats.

Pour utiliser un fichier de configuration personnalisé, il suffit d'ajouter l'argument "-config {fichier_de_configuration_personnalisé}" à la commande openssl.

Avant de pouvoir générer un certificat, il faut obligatoirement générer une clé RSA ou DSA.

Tout est expliqué ici.

Dans les exemples suivants, la clé privée "mykey.pem" sera utilisée pour la génération des certificats.

Création d'une autorité de certification (permettant de signer les demandes de certificats)

Pour générer ses propres certificats, sans passer par une autorité de certification externe :

$ openssl req -new -x509 -key mykey.pem -out ca.crt -days 1095

On indique pour le paramètre "-out" le nom de l'autorité de certification à générer puis la durée de validité en jour avec le paramètre "-days"
Cette autorité de certification permettra de signer les futures demandes de certificats auto-signés.
Cette génération est à faire une seule fois.
Le Common Name à indiquer ne doit correspondre à aucun nom de domaine ayant besoin d'un certificat.
Cette autorité de certification peut-être mis à disposition du public afin d'être intégré dans les différents navigateurs comme étant une autorité de certification reconnue.

Création d'une demande de certificat

Une demande de certificat peut être utilisée sur un site comme http://www.cacert.org/ afin d'obtenir un certificat reconnu par une autorité de certification ou afin d'être signé par le certificat ca.crt généré ci-dessus.

$ openssl req -new -key mykey.pem -out mondomaine.csr

La demande "cert.csr" peut ensuite être transmise à l'autorité de certification qui fournira par la suite le certificat résultant.
Le Common Name à indiquer doit correspondre au nom de domaine pour lequel vous souhaitez un certificat.

Génération du certificat final

A l'aide du certificat de certification généré au tout début (ca.crt), nous allons générer le certificat correspondant à la demande (mondomain.csr) générée ci-dessus.

$ openssl x509 -req -in mondomaine.csr -out mondomaine.pem -CA ca.crt -CAkey mykey.pem -CAcreateserial -CAserial ca.srl -days 90

L'option -CAcreateserial est à utiliser seulement la première fois.
Ceci va générer un identifiant (ca.srl).
Lors des prochaines certification (pour renouvellement ou pour d'autres domaines) l'identifiant, contenu dans le fichier ca.srl, sera incrémenté à l'aide de l'option -CAserial ca.srl

Etiquettes: 

openssl : Générer une authentification Apache

Avec Apache, il est possible d'utiliser des fichiers d'authentification pour protéger des répertoires web.

Pour générer ces fichiers d'authentification, il est nécessaire d'utiliser la commande htdigest.

La commande htdigest inscrit les données d'authentification obligatoirement dans un fichier.

Pour afficher ces mêmes informations mais uniquement sur la sortie standard (pour x raisons) il est nécessaire d'utiliser la commande openssl.

Voici un script qui permet de saisir les informations d'authentification et affiche le résultat sur la sortie standard :

$ cat htdigest.sh
#!/bin/bash
 
echo "Creation d'une authentification Apache"
echo "-----------------------------------------------"
 
read -p "Votre nom : " NAME
read -p "Apache AuthName: " AUTHNAME
read -s -p "Votre mot de passe : " PASS; echo
 
printf "%s:%s:%s\n" \
  "$NAME" \
  "$AUTHNAME" \
  $(printf "${NAME}:${AUTHNAME}:${PASS}" | openssl dgst -md5)
 
exit 0

Exécution du script :

$ ./htdigest.sh
Creation d'une authentification Apache
-----------------------------------------------
Votre nom : toto
Apache AuthName: monSite
Votre mot de passe :
toto:monSite:ffdcd4a53fb7dd716ec2d017e93bc563

 

Etiquettes: 

openssl : Les nombres premiers

OpenSSL possède une sous-commande qui permet de tester si le nombre passé en paramètre est un nombre premier.

$ openssl prime 2
2 is prime
$ openssl prime 3
3 is prime
$ openssl prime 4
4 is not prime
$ openssl prime 11
B is prime

Le résultat est retourné sous forme hexadécimale.

En ajoutant l'option "-hex", il est possible de tester une chaine hexadécimale :

$ openssl prime 449
1C1 is prime

La représentation hexadécimale du nombre 449 est donc 1C1

$ openssl prime -hex 1C1
1C1 is prime

En prime, un petit enchainement de commandes permettant d'obtenir la liste des 168 nombres premiers inférieurs à 1000.

$ for i in $(seq 0 1000); do \
result=$(openssl prime $i) \
echo $result | grep  -q "is prime$" \
if [[ $? -eq 0 ]]; then \
echo $i \
fi
done | column -x
2       3       5       7       11      13      17      19      23      29      31      37      41      43      47      53
59      61      67      71      73      79      83      89      97      101     103     107     109     113     127     131
137     139     149     151     157     163     167     173     179     181     191     193     197     199     211     223
227     229     233     239     241     251     257     263     269     271     277     281     283     293     307     311
313     317     331     337     347     349     353     359     367     373     379     383     389     397     401     409
419     421     431     433     439     443     449     457     461     463     467     479     487     491     499     503
509     521     523     541     547     557     563     569     571     577     587     593     599     601     607     613
617     619     631     641     643     647     653     659     661     673     677     683     691     701     709     719
727     733     739     743     751     757     761     769     773     787     797     809     811     821     823     827
829     839     853     857     859     863     877     881     883     887     907     911     919     929     937     941
947     953     967     971     977     983     991     997
Etiquettes: 

openssl : Récupérer un certificat distant

Le script suivant permet de récupérer le contenu du certificat du site indiqué en paramètre (le port est facultatif, par défaut le port 443 est utilisé).

$ cat getCertificates.sh
#!/bin/sh
 
HOST=$1
PORT=${2:-443}
 
echo | \
openssl s_client -connect ${HOST}:${PORT} 2>&1 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'
 
exit 0
$

On l'exécute en lui indiquant en paramètre le nom du site et le port (facultatif).

$ ./getCertificates.sh youtube.fr 443
-----BEGIN CERTIFICATE-----
MIIF/DCCBWWgAwIBAgIKKZeNqAABAACBuDANBgkqhkiG9w0BAQUFADBGMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu
...
Xl7/nc7F5HgE0c+bGsAYuCoYBsDNYs8/AUTnSTeih8U9mSRRDFb9NxMkQqUWPSXO
sh4FktaXQJBJRi4IAfuUDk28c9A0Bv1ygO3FqiSu6QJ3axESCoGmF26XqDwvJtFq
-----END CERTIFICATE-----

et le certificat s'affiche à l'écran.

Voir également CURL: Afficher les informations SSL d'un domaine

Voir également openssl : Extraire les informations d'un certificat

 
Etiquettes: 

openssl: Générer une paire de clé et une demande de certificat CSR

Voici une commande permettant de générer, en une seule fois, une paire de clé (publique/privée) et une demande de certificat CSR afin d'obtenir, auprès de cacert.org par exemple, un certificat X509 associant votre clé publique générée et le nom de domaine que vous souhaitez protéger avec TLS.

$ openssl req -nodes -newkey rsa:2048 -sha256 -keyout ma_cle.key -out ma_demande.csr

Avec cette commande, nous allons créer une clé RSA de 2048 bits.

Les autres arguments de la ligne de commandes indiquent que la clé privée sera stockée dans le fichier ma_cle.key et la demande CSR sera stockée dans le fichier ma_demande.csr.

Une série de questions vous est posée.
Les deux questions les plus importantes sont celles correspondantes au common name et à l'adresse mail de contact.

Ne renseignez surtout pas de mot de passe sinon il vous sera demandé à chaque démarrage du serveur web.
Un peu contraignant lors de tâches de maintenance automatisées.

Pour vérifier la demande CSR:

$ openssl req -text -noout -verify -in ma_demande.csr

Reste à transmettre à l'autorité de certification (cacert.org par exemple - certificat gratuit) la demande CSR.

 

openssl: connexion SMTP avec TLS

Il est possible d'utiliser la commande openssl pour se connecter à un serveur STMP en utilisant la sécurité TLS,

Dans l'exemple suivant, l'option -CAfile /etc/ssl/certs/ca-certificates.crt permet d'indiquer l'autorité de certification dans le cas d'un certificat auto signé.

# openssl s_client -starttls smtp -crlf -CAfile /etc/ssl/certs/ca-certificates.crt -connect smtp.server.fr:25

CONNECTED(00000003)
depth=0 CN = localhost.localdomain
verify return:1
---
Certificate chain
 0 s:/CN=localhost.localdomain
   i:/CN=localhost.localdomain
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIBtzCCASACCQCupdgFIYzFVDANBgkqhkiG9w0BAQUFADAgMR4w
b2NhbGhvaW4wHhcNMTEwNDI2MTkxMDM1WhcNMjEwNDIzMTkx
MDM1WjAgMR4wHAYDVQQDExVsb2NhbGhvc3QubG9jYWxkb21ha
hvcNAQEBBQADgY0AMIGJZS3OcCHjbQNa++F/J4WGFk6r7ICl9Ls0
ybT+1CzbFG1xE71NeRhbHkLGigZa5422afsqq/b3LBIpMX17B/gtuo+
QKRckuo+20BUqHFye7ZAVXSsw+bqmm9D0vB4uw9IwyE75jEAqU1
QX7RAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAe1iHUXoGSTj3Fv3
2Yg1EFtNm/RSYxdHV8jY6W6CtwNatN+EQqmWlfhtOSCWgXriUjX5
E4uEocf6x/vDrVkpU11u+0xDUYshmrLyk2nmX6wbopwQttAE9PLw
ahQBiuj+sz38OW8=
-----END CERTIFICATE-----
subject=/CN=localhost.localdomain
issuer=/CN=localhost.localdomain
---
No client certificate CA names sent
---
SSL handshake has read 1021 bytes and written 456 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: D2A784125E749B8C74015978412B9B6E47DDA37BFB83FD4FEE8E6ED21BE79C92
    Session-ID-ctx:
    Master-Key: 631649B5A6A2F5272137C421AFB2F2E86CD483ED2FE8099BCDC9068A11EE7087F
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1478726847
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
250 DSN
mail from: one@mail.fr
250 2.1.0 Ok
rcpt to: two@mail.fr
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
from: one@mail.fr
to: two@mail.fr
subject: send mail with TLS connection

Voluptatem veniam quasi nisi voluptatum incidunt autem. Sed provident minus ab temporibus sed. Quia blanditiis consequatur assumenda aut hic. Minima cum qui quidem dicta velit quod voluptas. Dolorem aliquam animi consequatur.

Minima alias sint libero et. Et et in ratione similique iusto aut consectetur. Sed voluptatibus modi fuga ut quibusdam ut consectetur voluptas.
.
250 2.0.0 Ok: queued as 145A53700157
quit
221 2.0.0 Bye
closed

Très utile pour tester un serveur STMP

Périphériques externes

Créer une image ISO d'un CD et/ou DVD

Avant toute chose, ne pas monter le cd-rom

Créer une image ISO avec la commande dd :

$ dd if=/dev/cdrom of=/tmp/monImage.iso

Créer une image ISO avec la commande cat :

$ cat /dev/cdrom > /tmp/monImage.iso

Pour vérifier le contenu de l'image créée :

$ mkdir /media/iso
$ sudo mount -o loop -t iso9660 /tmp/monImage.iso /media/iso/

Etiquettes: 

Formater un support de stockage

Installer la boite à outils

$ sudo apt-get install dosfstools

Lister les supports de stockage

$ sudo fdisk -l

Formater en FAT 12 OU 16

$ mkfs.vfat /dev/sdb1

Formater en FAT 32

$ mkfs.vfat -F 32 -n nom_de_ma_cle_usb /dev/sdb1

Pour formater un support de stockage, celui-ci ne doit pas être monté.

$ umount /media/ma_cle_usb

Pour les autres formats :

  1. mkfs.bfs     
  2. mkfs.ext2    
  3. mkfs.ext4    
  4. mkfs.minix   
  5. mkfs.ntfs
  6. mkfs.cramfs  
  7. mkfs.ext3    
  8. mkfs.ext4dev
  9. mkfs.msdos   
  10. mkfs.vfat

Formater en NTFS

$ apt-get install ntfs-3g ntfsprogs

$ mkntfs /dev/sde1

Afficher le format/type exact d'une partition

La commande fdisk -l ne permet pas de connaitre le type exact d'une partition.

$ fdisk -l
 
Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          66      524288   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2              66      121601   976235712   8e  Linux LVM
 
Disk /dev/sdb: 1500.3 GB, 1500299395072 bytes
255 heads, 63 sectors/track, 182401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00026d5d
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      182401  1465136001   83  Linux
 
Disk /dev/sdc: 160.0 GB, 160041885696 bytes
255 heads, 63 sectors/track, 19457 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0008ef40
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1               1       19457   156288321   83  Linux
 
Disk /dev/sdd: 2000.4 GB, 2000396746752 bytes
255 heads, 63 sectors/track, 243201 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00101aa1
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1      243201  1953512001   83  Linux
 
Disk /dev/sde: 80.0 GB, 80032038912 bytes
255 heads, 63 sectors/track, 9730 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000f2c13
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sde1               1        9730    78156193+   b  W95 FAT32

La commande blkid est beaucoup plus précise

$ blkid
/dev/sda1: UUID="cd34d37b-b73c-4ee4-820c-c8006043518a" SEC_TYPE="ext2" TYPE="ext3"
/dev/sda2: UUID="BVYNlC-AQG0-oDe9-I4zu-xVzp-RdV3-W1Fegr" TYPE="LVM2_member"
/dev/sdb1: UUID="a6ea6463-e289-4bbe-afba-572f876497da" TYPE="ext4"
/dev/sdc1: UUID="1a6c5464-6ae1-4205-97c5-edae82649198" SEC_TYPE="ext2" TYPE="ext3"
/dev/sdd1: UUID="8ea1a0ee-c21c-4892-bdc9-bda6186ebd98" TYPE="ext4"
/dev/sde1: UUID="4961B8D578F1B234" TYPE="ntfs"

Créer une image ISO d'un répertoire

Sur les distributions type Debian, le programme genisoimage permet de créer une image ISO d'un fichier ou d'un répertoire complet.

Pré-requis

Installer le programme genisoimage

$ apt-get install genisoimage

Exemple

$ genisoimage -r -v -J -o /tmp/image.iso /mon_repertoire

=> Création du fichier /tmp/image.iso contenant toutes les données de /mon_repertoire

Détail des options utilisées

-r : Pour permettre à tous les utilisateurs d'avoir accès aux données du disque, même si l'image est créée en tant qu'administrateur.
-v : Permet d'activer le mode verbeux.
-J : Supporte les noms longs de fichiers.
-o : Permet de spécifier le nom du fichier ISO

Etiquettes: 

Graver une image ISO sur un disque

Sur les distributions type Debian, le programme wodim permet de graver une image ISO sur un disque.

Pré-requis

Installer le programme wodim

$ apt-get install wodim

Connaitre l'identifiant du graveur

$ wodim --devices
wodim: Overview of accessible drives (1 found) :
-------------------------------------------------------------------------
 0  dev='/dev/scd0'     rwrw-- : 'TSSTcorp' 'CDW/DVD TS-H492B'
-------------------------------------------------------------------------
$

Exemple

Si besoin, créer une image ISO (voir détail ici) :

$ genisoimage -r -v -J -o /tmp/image.iso /mon_repertoire

Puis graver l'image sur un disque :

$ wodim -v -speed=4 dev=/dev/scd0 -data /tmp/image.iso

Détail des options utilisées :

-v : Pour activer le mode verbeux
-speed : Pour indiquer la vitesse de gravure
dev=... : Pour indiquer le fichier identifiant du graveur
-data : Pour indiquer l'image à graver

Pour effacer rapidement un disque RW :

$ wodim -v -speed=4 -force dev=/dev/scd0 blank=fast

Pour effacer complètement un disque RW :

$ wodim -v -speed=4 -force dev=/dev/scd0 blank=all

Etiquettes: 

Rendre une clé USB bootable

$ sudo syslinux -sf /dev/sdb1

A remplacer par le bon périphérique

Pour obtenir la liste des périphériques :

$ sudo fdisk -l

Etiquettes: 

Renseigner automatiquement l'UUID d'une partition dans le fichier /etc/fstab

Afficher ou sauvegarder le fichier /etc/fstab avant toute manipulation

# cat /etc/fstab

#
# /etc/fstab
# Created by anaconda on Wed Feb  8 10:04:33 2023
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=8db4ff33-9c01-42c5-89c9-851b58ad4599 /                       xfs     defaults        0 0
UUID=d107b673-8b5a-4171-b30c-d03bd72b9e55 /boot                   xfs     defaults        0 0
UUID=3AC0-FCC8                            /boot/efi               vfat    umask=0077,shortname=winnt 0 2
UUID=fe4c581c-4b22-49d7-94a7-040600a98baa /var/log                xfs     defaults        0 0
UUID=33bb87c8-90e6-4107-9d70-0c4bd2f5b852 none                    swap    defaults        0 0
UUID=5654158c-dc3e-4c69-a5f8-64c6dc7c8b08 /home                   xfs     defaults        0 0
no-device                                 /tmpfs                  tmpfs   rw,seclabel,relatime 0 0

Afficher la liste des partitions ainsi que les UUIDs

# blkid
/dev/sda5: UUID="8db4ff33-9c01-42c5-89c9-851b58ad4599" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="85254d0d-f5c4-6245-aae7-fd870d97413c"
/dev/sda1: UUID="3AC0-FCC8" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="fc72f64e-a700-459a-ba6b-0dcaa7016747"
/dev/sda2: UUID="d107b673-8b5a-4171-b30c-d03bd72b9e55" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="736002d1-0dcf-4825-8ebd-caa436560505"
/dev/sda3: UUID="33bb87c8-90e6-4107-9d70-0c4bd2f5b852" TYPE="swap" PARTUUID="6d3ef976-e9fb-425f-b7e1-7f1ff84d51e0"
/dev/sda4: UUID="fe4c581c-4b22-49d7-94a7-040600a98baa" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="894d6ef9-cfa0-4945-a3db-fefdd0c94162"
/dev/sdb1: UUID="5654158c-dc3e-4c69-a5f8-64c6dc7c8b08" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="3a6000f2-01"
/dev/sdc1: UUID="c822d42a-a492-4e80-8b2a-e929275b955c" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="51229054-01"
/dev/sdd1: UUID="921dbe26-cc98-45bd-9445-4c87ff1f4ec0" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="d8576e95-01"

Je souhaite monter la partition /dev/sdd1 dans le dossier /mnt/207, je vais copier l'UUID de la partition directement dans le fichier /etc/fstab

# echo UUID=$(blkid -o value -s UUID /dev/sdd1) >> /etc/fstab

Reste à modifier le fichier /etc/fstab pour y ajouter les informations supplémentaires

# vim /etc/fstab

#
# /etc/fstab
# Created by anaconda on Wed Feb  8 10:04:33 2023
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=8db4ff33-9c01-42c5-89c9-851b58ad4599 /                       xfs     defaults        0 0
UUID=d107b673-8b5a-4171-b30c-d03bd72b9e55 /boot                   xfs     defaults        0 0
UUID=3AC0-FCC8                            /boot/efi               vfat    umask=0077,shortname=winnt 0 2
UUID=fe4c581c-4b22-49d7-94a7-040600a98baa /var/log                xfs     defaults        0 0
UUID=33bb87c8-90e6-4107-9d70-0c4bd2f5b852 none                    swap    defaults        0 0
UUID=5654158c-dc3e-4c69-a5f8-64c6dc7c8b08 /home                   xfs     defaults        0 0
no-device                                 /tmpfs                  tmpfs   rw,seclabel,relatime 0 0
UUID=921dbe26-cc98-45bd-9445-4c87ff1f4ec0 /mnt/207                xfs     defaults        0 0

Reste à monter la partition

# mount -a

 

Etiquettes: 

Requetes MySql

Exécuter une requête MySql en ligne de commande :

Pré-requis :

Avoir un client MySql installé

$ apt-get install mysql-client

Exécution d'une requête :

$ echo "select * from ma_base.ma_table where champ1 = valeur1" | mysql -u user -pmotdepasse

De cette manière, il est tout à fait possible d'exécuter une requête SQL dans un script (en bash par exemple) et d'en exploiter le résultat.

Rediriger le résultat d'une requête MySql dans un fichier :

$ echo "select * from ma_base.ma_table where champ1 = valeur1" | mysql -u user -pmotdepasse > ~/mon_fichier

Il est également possible d'exécuter une requête uniquement avec le client mysql :

$ mysql ma_base -N -s -u user -p -e "select * from ma_table where 1=1"

L'option -N permet de na pas afficher le nom des champs.
L'option -s permet de ne pas afficher les délimiteurs du tableau.
L'option -e permet d'exécuter la requête suivante.

Le résultat de la requête est affiché dans la console et peut être traité par d'autres commandes.

Etiquettes: 

SSH

Copier la clé d'un host dans le fichier authorized_keys d'un host distant

Utilisation de la commande ssh-copy-id :

Principe : Copier la clé contenu dans le fichier ~/.ssh/id_rsa.pub dans le fichier ~/.ssh/authorized_keys d'un host distant (ici, 192.168.0.101)

Voir également  "Connexion à une machine distante (ssh, scp, sftp) sans saisir le mot de passe"

$ cd ~/.ssh
$ ls
authorized_keys  id_rsa  id_rsa.pub  known_hosts
$ ssh-copy-id -i id_rsa.pub root@192.168.0.101
root@192.168.0.101's password:#Saisir ici le mot de passe
Now try logging into the machine, with "ssh 'root@192.168.0.101'", and check in:
 
  ~/.ssh/authorized_keys
 
to make sure we haven't added extra keys that you weren't expecting.
 
$

Sécuriser la connexion ssh par clé en associant une adresse IP/un nom de host à la clé RSA dans le fichier ~/.ssh/authorized_keys

Pour plus de sécurité il est possible d'associer une adresse IP et/ou un nom de host à une clé RSA afin d'éviter l'utilisation de cette clé sur une autre machine.

Exemple d'une clé RSA

$ cat ~/.ssh/authorized_keys
ssh-rsa iMEyIOc6TAw3+w/RBkHXbabKYgI1Y4EDEifBdcmGYMe8MshE6D27NouR1hdFyvsT
LLs+DCALHJNxkitS31HO7QlWAX9YRC1D47neCk29E1SCmG9CeWNyztgAmgI91FD6
yWtWSD3MOW27/rC+CxdXoVNSFNVJ+wKuK5QGUMlAMm+nFJi6evlVLBhGKb5LVNQ6
nszYteN5dpp/asSAU+kHyQsNJPyz1m/w8KZujYpkpV3mJh4GYvBiz2A+JWo/FRbo
3LSdFiVxt9V1rqfrYSXFcHS4jky9HSH/c9b9d1iop6lPBShlLjAf2zpQYfU5Ra7u
TQsO3D1z+dZ8NXARJ4WWHkWyIEyN67q4uKMehscDGYKy3hcyRTlJ0sw0bokNMf99
oGcmN5KKasLvUsIfMyNbSC8bcYdsOb+06QDyHHE9h2aYcoBCgvX4zhkAoDSeMsK2
Bg7h2PdV5S5bLPr/hjLgNky6u9TCbDIJOInqJZwk4/gqPOMbffuKeiN1K2lzyrfk
mX3Nnwm/ivb0IBEYLQs4GM8WyamlJtlj5K32mJfflFwnbj5+dGQhjRZ/jJ1D9PsL
U441033CepoXDIf2cV3I34aZ2V7YxEgsk4rRVEX0mGCYezBn1f4IumQ1Il9ixrEf
FktHyk6kfviQC8wOTRsABiH79KSIIqc3cp9zbOSbn+4= root@ma_machine

Exemple d'une clé RSA avec association d'une adresse IP et/ou d'un nom de host

$ cat ~/.ssh/authorized_keys
from="192.168.1.1,ma_machine.mon_domaine.com" ssh-rsa iMEyIOc6TAw3+w/RBkHXbabKYgI1Y4EDEifBdcmGYMe8MshE6D27NouR1hdFyvsT
LLs+DCALHJNxkitS31HO7QlWAX9YRC1D47neCk29E1SCmG9CeWNyztgAmgI91FD6
yWtWSD3MOW27/rC+CxdXoVNSFNVJ+wKuK5QGUMlAMm+nFJi6evlVLBhGKb5LVNQ6
nszYteN5dpp/asSAU+kHyQsNJPyz1m/w8KZujYpkpV3mJh4GYvBiz2A+JWo/FRbo
3LSdFiVxt9V1rqfrYSXFcHS4jky9HSH/c9b9d1iop6lPBShlLjAf2zpQYfU5Ra7u
TQsO3D1z+dZ8NXARJ4WWHkWyIEyN67q4uKMehscDGYKy3hcyRTlJ0sw0bokNMf99
oGcmN5KKasLvUsIfMyNbSC8bcYdsOb+06QDyHHE9h2aYcoBCgvX4zhkAoDSeMsK2
Bg7h2PdV5S5bLPr/hjLgNky6u9TCbDIJOInqJZwk4/gqPOMbffuKeiN1K2lzyrfk
mX3Nnwm/ivb0IBEYLQs4GM8WyamlJtlj5K32mJfflFwnbj5+dGQhjRZ/jJ1D9PsL
U441033CepoXDIf2cV3I34aZ2V7YxEgsk4rRVEX0mGCYezBn1f4IumQ1Il9ixrEf
FktHyk6kfviQC8wOTRsABiH79KSIIqc3cp9zbOSbn+4= root@ma_machine

Les différentes valeurs doivent être séparées par une virgule.
Le point d'interrogation permet d'indiquer un plage d'adresses IP (from="192.168.0.?").
L'astérisque permet d'indiquer tous les sous-domaines d'un domaine (from="*.mon_domaine.com").
Il est possible d'exclure en ajoutant un point d'exclamation avant (from="!autre_domaine.com").

Etiquettes: 

Manipuler les clés SSH

L'outil ssh-keygen permet d'effectuer différentes manipulations sur les clés SSH.

Générer une clé SSH de type RSA:

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/xxxx/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/xxxx/.ssh/id_rsa
Your public key has been saved in /home/xxxx/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:LSPtPiOIKudk4CuQzD6MdlyJQh3RQc+xvYJTgHWPQ6E xxxx@yyyy
The key's randomart image is:
+---[RSA 3072]----+
|   .*=.=.        |
|   o .B *        |
|  . .E B o       |
| . .  o....      |
|=.  .oo.S..      |
|+= . o.o.o       |
|=.=...  .        |
|+B+o. ..o        |
|==+    ..o       |
+----[SHA256]-----+

Le contenu de la clé privée (attention, celui-ci ne doit jamais être partagé)

$ cat .ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEApgXls7gw98Hr5Q05qB5Dlg7Agzomlm/8Q7k3yawjwgJP5+SNZ0pj
edSm8PnQZnYP00vN8KPLjTWfBo39HRJpylPq8abRs8LeIqAwKOY+7bTGEvcxACvIRd8w/U
wV6XYHVH5w9vT4gW9Lb9J1AOAcaEEcGNUwCPHiqoHa6eA2kg6e36F1ZE9DUYGw2altST7P
smWvFSPMx3eXJLpbsgaGHhzvI6cwpXkbeDILoHjws1GDcmI7X3zya46p3Zrt8cagRi3lKP
cje3xfKoLMLb3zauB8tb7pW41VP4KoplgT7r4bxd/4EkQcG9bv9AoAb7avp6f0ulBvAEw9
...
UZA+eKidhEz4we4ivxwb+GSVbCVBWkWZvEIX5+5tVPJaxi18K6GC+Zw4lOlYe4nwFtDQz8
5KI7LRQSDZrNtNiMJ/R/dOXV9l74ixlafKM1uMd5x23RmSEVzGap/tgwqam+KSeHuKN+zR
O3XW2hyx1TruD3JEcN2/hVUh5bM1q9YcSfrjd/bzFL+tQIs1t9N5TQOn/EYnLHlcsUzhLq
xWp1/EulMM4qMAAAAPcm9uYW5AUEFTUE8wMDg2AQIDBA==
-----END OPENSSH PRIVATE KEY-----

Le contenu de la clé privée (celui ci peut-être partagé avec n'importe qui)

$ cat .ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmBeWzuDD3wevlDTmoHkOWDsCDOiaWb/xDuTfJrCPCAk/n5I1nSmN51Kbw+dBmdg/TS83wo8uNNZ8Gjf0dEmnKU+rxptGzwt4ioDAo5j7ttMYS9zEAK8hF3zD9TBXpdgdUfnD29PiBb0tv0nUA4BxoQRwY1TAI8eKqgdrp4DaSDp7foXVkT0NRgbDZqW1JPs+yZa8VI8zHd5ckuluyBoYeHO8jpzCleRt4MgugePCzUYNyYjtffPJrjqndmu3xxqBGLeUo9yN7fF8qgswtvfNq4Hy1vulbjVU/gqimWBPuvhvF3/gSRBwb1u/0CgBvtq+np/S6UG8ATD2mBS+mqB687hKB7BzY+SAWgOMfloavV7ms609c5/nQR2UwlQuHDo79Zz3uqbwVh02hRdomZC7Q9Elte9hUgdz5DZOMiC1lOh5Zj+X/POe+Rg/G8eXrbXO0IlaW4MZjqQTG8U7gLrbtkHYxljphI/eGSYnTo5Qmu77EHy0EAe/DE7HGGt0hOEU= xxxx@yyyy

Afficher le fingerprint d'une clé SSH (peu importe la clé fournie, privée ou publique, c'est toujours le fingerprint de la clé publique qui est affiché)

$ ssh-keygen -l -f .ssh/id_rsa
3072 SHA256:LSPtPiOIKudk4CuQzD6MdlyJQh3RQc+xvYJTgHWPQ6E xxxx@yyyy(RSA)
$ ssh-keygen -l -f .ssh/id_rsa.pub
3072 SHA256:LSPtPiOIKudk4CuQzD6MdlyJQh3RQc+xvYJTgHWPQ6E xxxx@yyyy(RSA)

Par défaut le fingerprint est affiché avec le hash SHA256, pour l'afficher avec le hash MD5, il suffit d'ajouter l'option -E avec le paramètre MD5

$ ssh-keygen -l -f .ssh/id_rsa.pub -E md5
3072 MD5:29:3c:60:2a:d2:49:20:82:12:c4:c5:e3:f0:95:2c:b3 xxxx@yyyy(RSA)

Afficher le fingerprint en représentation ASCII

$ ssh-keygen -l -f .ssh/id_rsa.pub -E md5 -v
3072 MD5:29:3c:60:2a:d2:49:20:82:12:c4:c5:e3:f0:95:2c:b3 xxxx@yyyy(RSA)
+---[RSA 3072]----+
|Ooo.. .          |
|=+ = +           |
|. = O            |
| o E o   .       |
|o +   + S        |
|..     o         |
|                 |
|                 |
|                 |
+------[MD5]------+

Recalculer la clé publique d'une clé privée

$ ssh-keygen -y -f .ssh/id_rsa
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCmBeWzuDD3wevlDTmoHkOWDsCDOiaWb/xDuTfJrCPCAk/n5I1nSmN51Kbw+dBmdg/TS83wo8uNNZ8Gjf0dEmnKU+rxptGzwt4ioDAo5j7ttMYS9zEAK8hF3zD9TBXpdgdUfnD29PiBb0tv0nUA4BxoQRwY1TAI8eKqgdrp4DaSDp7foXVkT0NRgbDZqW1JPs+yZa8VI8zHd5ckuluyBoYeHO8jpzCleRt4MgugePCzUYNyYjtffPJrjqndmu3xxqBGLeUo9yN7fF8qgswtvfNq4Hy1vulbjVU/gqimWBPuvhvF3/gSRBwb1u/0CgBvtq+np/S6UG8ATD2mBS+mqB687hKB7BzY+SAWgOMfloavV7ms609c5/nQR2UwlQuHDo79Zz3uqbwVh02hRdomZC7Q9Elte9hUgdz5DZOMiC1lOh5Zj+X/POe+Rg/G8eXrbXO0IlaW4MZjqQTG8U7gLrbtkHYxljphI/eGSYnTo5Qmu77EHy0EAe/DE7HGGt0hOEU= xxxx@yyyy

La preuve que la commande ci-dessus retourne le même résultat que la clé publique d'origine

$ ssh-keygen -y -f .ssh/id_rsa | diff .ssh/id_rsa.pub -
(le retour de la commande est vide indiquant que les contenus sont identiques)

 

Etiquettes: 

known_hosts

Manipuler le fichier "known_hosts"

 

Rechercher dans le fichier "known_hosts" l'entrée correspondante à un host particulier

$ ssh-keygen -H -F 192.168.1.101

$ ssh-keygen -H -F machine.domaine.com

 

Supprimer du fichier "known_hosts" l'entrée correspondante à un host particulier

$ ssh-keygen -R 192.168.1.101

$ ssh-keygen -R machine.domaine.com

 

Obtenir la clé d'un host encodée en base64

$ ssh-keyscan -t rsa,dsa 192.168.1.100

$ ssh-keyscan -t rsa,dsa machine.domaine.com

Screen

Screen est un multiplicateur de terminal.
Cette commande permet d'avoir plusieurs consoles au sein d'une seule.

A mon avis, l'avantage principal de cette commande, est de supprimer tous les risques liés à une déconnexion de la console principale.
Effectivement, en cas de perte de la connexion à la console, toutes les commandes en cours d'exécution sont automatiquement stoppées.
Avec screen, toutes les commandes continuent à s'exécuter.

Pour l'installer et l'utiliser, il faut obligatoirement être en mode sudo :

$ sudo apt-get install screen

Utilisation de screen

  • Pour ouvrir une console screen :

$ sudo screen -S ma_console

Exécution de screen avec l'option "-S" qui permet de nommer la nouvelle console en "ma_console".

  • Obtenir de l'aide dans screen :

Appuyer sur les touches ctrl + a puis sur shift + ?

Pour quitter l'aide, appuyer sur espace

  • Pour quitter une console screen :

Taper exit puis appuyer sur entrée

Ou appuyer sur les touches ctrl + d

  • Pour revenir à la console principale sans fermer la console screen :

Appuyer sur les touches ctrl + a puis sur d

Une fois dans la console principale, il est possible de lister les différentes consoles ouvertes avec screen sachant qu'il est tout à fait possible d'ouvrir plusieurs consoles différentes.

$ sudo screen -ls
There is a screen on:
        25065.ma_console        (14/12/12 14:48:52)     (Detached)
1 Socket in /var/run/screen/S-root.

Ci-dessus, apparait la mention "Detached" signifiant qu'une console "ma_console" est en cours d'exécution en arrière plan.

Dans le cas où il y aurait plusieurs consoles.

$ sudo screen -ls
There are screens on:
        25169.ma_console3       (14/12/12 14:54:51)     (Detached)
        25165.ma_console2       (14/12/12 14:54:45)     (Detached)
        25065.ma_console        (14/12/12 14:48:52)     (Detached)
3 Sockets in /var/run/screen/S-root.

Ci-dessus, apparait la liste de toutes les consoles en cours d'exécution avec leurs différents noms.

  • Pour retourner dans une console :

$ sudo screen -R 25065

Permet de retourner dans la console nommée "ma_console" et portant le pid "25065".

Il est important de préciser le pid de la console dans le cas où leurs noms seraient similaires (comme dans l'exemple ci-dessus : ma_console, ma_console2, ma_console3).

Sinon, le nom est suffisant.

$ sudo screen -R ma_console

  • Créer plusieurs fenêtres dans une console screen :

Il est possible de créer une multitude de fenêtres en appuyant sur les touches ctrl + a puis sur c.

La liste des fenêtres créées apparaît en bas de l'écran (0$ bash  1$ bash  2$ bash  3-$ bash  4$ bash  5$ bash  6$ bash) en appyant sur les touches ctrl + a puis sur w.

Pour passer à la fenêtre suivante, appuyer sur les touches ctrl + a puis sur n (next).
Pour retourner à la fenêtre précédente, appuyer sur les touches ctrl + a puis sur p (previous).

Pour aller à une fenêtre en particulier, appuyer sur les touches ctrl + a puis sur le chiffre correspondant (voir la liste des fenêtres en bas de l'écran).

  • Afficher plusieurs consoles screen dans une même fenêtre (split):

Dans une console screen, appuyer sur les touches ctrl + a puis sur shift + s

Une seconde fenêtre vide apparait au dessous.

Pour ouvrir une nouvelle console dans cette fenêtre vide, appuyer sur les touches ctrl + a puis sur tab.

Une fois le curseur dans la nouvelle fenêtre, appuyer sur les touches ctrl + a puis sur c.

Il est possible de spliter l'écran plusieurs fois si nécessaire.

Pour basculer d'un écran à l'autre appuyer sur les touches ctrl + a puis sur tab.

Pour supprimer la fenêtre en cours d'utilisation, appuyer sur les touches ctrl + a puis sur shift + x

Voici, à mon sens, les fonctions les plus utilses de la commande screen.

Personnalisation de screen

Il est possible de personnaliser screen avec un fichier de configuration nommé ~/.screenrc

Voici un fichier tout fait, créé par "bennyben" et que j'utilise personnellement.
Il customise vraiment bien la commande screen.

Son contenu est donc à enregistrer dans un fichier nommé ~/.screenrc

http://www.isbeta.fr/e5550

MD5SUM : a2aade4a2d4596b5df7720b3b3c3e904  screenrc.txt

Fichier attachéTaille
Plain text icon Fichier de configuration de la commande screen5.94 Ko
Etiquettes: 

Ça sert à rien mais c'est sympa !!!

Afficher différents logos Linux dans la console

Le commande linuxlogo permet d'afficher dans la console de jolis logos colorisés en caractères ASCII.

Pour l'installer :

$ sudo apt-get install linuxlogo

Le manuel de la commande : 

$ man linuxlogo

Exécution :

$ linuxlogo

Avec l'option -l, seul le logo est affiché

$ linuxlogo -l

Avec l'option -u, les informations uptime sont affichées.

$ linuxlogo -u

Avec l'option -y, les informations load average sont affichées.

$ linuxlogo -y

L'option -L list permet d'affiher la liste complète des logos disponibles.

$ linuxlogo -L list

Available Built-in Logos:

Num Type Ascii Name Description
1 Classic Yes aix AIX Logo
2 Classic Yes bsd FreeBSD Logo
3 Banner Yes bsd_banner FreeBSD Logo
4 Classic Yes irix Irix Logo
5 Banner Yes solaris The Default Banner Logos
6 Banner Yes banner-simp Simplified Banner Logo
7 Banner Yes banner The Default Banner Logo
8 Classic Yes classic-nodots The Classic Logo, No Periods
9 Classic Yes classic-simp Classic No Dots Or Letters
10 Classic Yes classic The Default Classic Logo
11 Classic Yes core Core Linux Logo
12 Banner Yes debian_banner_2 Debian Banner 2
13 Banner Yes debian_banner Debian Banner (white) 
14 Classic Yes debian_old Debian Old Penguin Logos 
15 Classic Yes debian Debian Swirl Logos
16 Classic Yes gnu_linux Classic GNU/Linux
17 Banner Yes mandrake_banner Mandrake(TM) Linux Banner
18 Banner Yes mandrake Mandrakelinux(TM) Banner
19 Banner Yes mandriva Mandriva(TM) Linux Banner
20 Banner Yes pld PLD Linux banner
21 Banner Yes redhat RedHat Banner (white) 
22 Banner Yes slackware Slackware Logo
23 Banner Yes sme SME Server Banner Logo
24 Banner Yes sourcemage_ban Source Mage GNU/Linux banner
25 Banner Yes sourcemage Source Mage GNU/Linux large
26 Banner Yes suse SUSE Logo
27 Banner Yes ubuntu Ubuntu Logo
 
Do "linux_logo -L num" where num is from above to get the appropriate logo.
Remember to also use -a to get ascii version.

 

Pour afficher le logo souhaité :

$ linuxlogo -L numéro

Tous les logos disponible ICI.

Afficher une bannière dans la console

La commande banner permet d'afficher dans la console un message de 10 caractères maximum avec uniquement des caractères "#".

Pour l'installer :

$ sudo apt-get install sysvbanner

Et ça donne ceci :

$ banner Linux

banner

Il existe également la commande printerbanner mais l'affichage se fait verticalement.

Etiquettes: 

Un effet "Matrix" dans la console

decrypt.py est un programme écrit en python qui anime la console en lui donnant un effet "Matrix"

L'animation générée est vraiment très sympa.

Le script peut être téléchargé ICI

Sinon, en ligne de commande :

Avec git :

$ git clone https://github.com/jtwaleson/decrypt.git

ou en téléchargeant le fichier ZIP :

$ wget https://github.com/jtwaleson/decrypt/archive/master.zip
$ unzip master.zip

Copier le script decrypt.py dans le répertoire /usr/local/bin

$ cp decrypt.py /usr/local/bin/

Exécution :

Afficher le contenu d'un fichier

$ cat decrypt.py | decrypt.py

Voir la vidéo : http://youtu.be/xI3JQg5BVtI

Lister le contenu d'un répertoire

$ ls -l | decrypt.py

Voir la vidéo : http://youtu.be/QnljP35j5EQ

Avec une image en ASCII (convertie avec la commande jp2a)

$ jp2a erreur404.jpg | decrypt.py

Voir la vidéo : http://youtu.be/8CAtUdl4Q5I

Etiquettes: 

Multimédia

Musique

eyeD3

eyeD3 est un outil python qui permet de visualiser/créer les tags ID3 des fichiers MP3.

Ce programme s'utilise uniquement en ligne de commande.

Installation

$ apt-get install eyeD3

ou

$ apt-get install python-eyeD3

Utilisation

Affichage des tags ID3 d'un fichier MP3

$ eyeD3 monFichier.mp3

Renseigner les tags ID3 d'un fichier MP3

Renseigner le nom de l'artiste

$ eyeD3 -a "Nom Artiste" monFichier.mp3

Renseigner le nom de l'album

$ eyeD3 -A "Nom Album" monFichier.mp3

Renseigner le titre de la chanson

$ eyeD3 -t "Titre Chanson" monFichier.mp3

Renseigner le numéro de la chanson

$ eyeD3 -n 1 monFichier.mp3

Renseigner le nombre total de chansons sur l'album

$ eyeD3 -N 10 monFichier.mp3

Afficher la liste des genres musicaux

$ eyeD3 --list-genres

Renseigner le genre de la chanson

$ eyeD3 -G "Pop / Funk" monFichier.mp3

Renseigner l'année de la chanson

$ eyeD3 -Y 2015 monFichier.mp3

Afficher la liste des types d'images

$ eyeD3 --list-image-types

Ajouter une image

$ eyeD3 --add-image=/chemin/vers/image:TYPE[:DESCRIPTION] monFichier.mp3

TYPE correspond au type d'image à insérer (voir commande du dessus). Une description optionnelle peut être ajoutée.

Pour supprimer une image, il suffit d'indiquer le type correspondant sans renseigner d'image.

Afficher l'aide de la commande eyeD3

$ eyeD3 -h

Utilisation dans un script Python

Installation

Télécharger la dernière version sur le site eyed3.nicfit.net
$ wget http://eyed3.nicfit.net/releases/eyeD3-X.Y.Z.tgz
Décompresser l'archive
$ tar -xvzf eyeD3-X.Y.Z.tgz
Exécuter le script d'installation
$ cd eyeD3-X.Y.Z
$ python setup.py install

Utilisation

import eyed3

audiofile = eyed3.load("chanson.mp3")
audiofile.tag.artist = u"Artiste"
audiofile.tag.album = u"Album"
audiofile.tag.title = u"Titre"
audiofile.tag.track_num = 4

audiofile.tag.save()

Plus d'info sur http://eyed3.nicfit.net/

Etiquettes: 

Photo

Convertir une image JPEG en caractères ASCII

jp2a est une commande qui permet d'afficher dans la console une image JPG en caractères ASCII.

Pour l'installer :

sudo apt-get install jp2a

Convertir une image :

$ jp2a erreur404.jpg

Ce qui donne :

L'effet est vraiment bluffant sur certaines photos.

La conversion ne se fait que sur des fichiers JPG.
Pour convertir une image PNG en JPG utiliser la commande convert de ImageMagick :

$ convert image.png image.jpg

ou

$ convert image.png jpg:- | jp2a -

Cette dernière commande permet de convertir l'image PNG en JPG directement sur la sortie standard afin de la transmettre à la commande jp2a via son entrée standard.

Etiquettes: 

Imagemagick

Imagemagick est un logiciel ultra-puissant qui s'utilise en ligne de commande et qui permet de manipuler quasiment tous les types d'images.

Convertion, redimensionnement, rotation, fusion, animation etc etc ... sont quelques unes des fonctions proposées par Imagemagick.

Pour installer imagemagick :

$ sudo apt-get install imagemagick

Convertir une vidéo AVI en GIF animé

$ convert -quiet -delay 1 input.avi  +map  output.gif

  Pour convertir une vidéo MOV en GIF animé.

Remplacer le fond blanc d'une image en fond transparent

$ convert test.png -transparent white transparent.png

Convertir une image png en jpg et inversement

$ convert image.png image.jpg

Convertir une série d'images png en jpg et inversement

$ for f in *.png; do convert $f $(echo $f | cut -d. -f1).jpg; done

ou

$ mogrify -format jpg *.png

Modifier le taux de compression pendant la conversion

$ mogrify -quality 90 -format jpg *.png

Convertir une série d'image jpg (ou tout autre format) en gif animé

$ convert *.jpg anime.gif

Ajouter l'option -delay num pour paramétrer la vitesse d'animation du gif animé.
num est exprimé en 1/100 seconde.

Ajouter l'option -loop num pour paramétrer le nombre de boucle.
Par défaut -loop 0 (infini).

Convertir et redimensionner une image

$ convert image.jpg -resize 50% image.png

ou en indiquant une taille en pixel

$ convert -resize 800x600 image.png image.jpg

ou en indiquant seulement la largeur (maxi) en pixel (le ratio est conservé)

$ convert -resize 800x image.png image.jpg

ou seulement la hauteur (maxi) en pixel (le ratio est conservé)

$ convert -resize x600 image.png image.jpg

et toutes les images d'un dossier avec  la commande mogrify

$ mogrify -resize 800x600 -format jpg *.png

Créer une icône à partir d'une image

$ convert -colors 256 -resize 16x16 image.jpg icone.ico

Convertir une image en niveau de gris

$ convert -type Grayscale image.jpg image.grayscale.jpg

Les différents types disponibles

$ convert -list type

Bilevel
ColorSeparation
ColorSeparationAlpha
ColorSeparationMatte
Grayscale
GrayscaleAlpha
GrayscaleMatte
Optimize
Palette
PaletteBilevelAlpha
PaletteBilevelMatte
PaletteAlpha
PaletteMatte
TrueColorAlpha
TrueColorMatte
TrueColor

Un exemple de chaque type est disponible ici.

Ajouter une bordure à une image

Une bordure transparente d'1 pixel

$ convert -bordercolor Transparent -border 1x1 image.jpg image-bordure.jpg

Une bordure noire de 10 pixels

$ convert -bordercolor black -border 10x10 image.jpg image-bordure.jpg

Créer le négatif d'une image

$ convert -negate image.jpg negate.jpg

Retourner une image verticalement

$ convert -flip image.jpg flip.jpg

Utiliser -flop pour retourner l'image horizontalement.

Pivoter une image vers la droite

$ convert -rotate 90 source.jpg destination.jpg

Utiliser -90 pour pivoter l'image vers la gauche.

Ajouter un texte en haut à gauche sur plusieurs images

$ mogrify -fill white -pointsize 16 -annotate +10+26 'Copie Interdite' *.jpg

-fill white : couleur du texte
-pointsize 16 : dimension du texte en pixel
-annotate +10+26 : position du texte sur l'image (verticale/horizontale)

Pour la position horizontale du texte, tenir compte de la dimension du texte.

Faire un montage avec plusieurs images

$ montage -background "azure3" *.jpg montage.jpg

Avec l'option -geometry, il est possible de modifier la taille des bordures entre chaque image.
La commande suivante ajoute une bordure de 5 pixels entre chaque image

$ montage -geometry +5+5 *.jpg montage.jpg

Avec l'option -resize, il est possible de redimensionner les images à assembler.
La commande suivante réduit la taille de chaque image de 80% avant de les assembler.

$ montage -resize 20% *.jpg montage.jpg

Extraire une partie d'une image

$ convert image.jpg -crop XxY+L+T newImage.jpg

X : Largeur de la nouvelle image
Y : Hauteur de la nouvelle image
L : Point de départ à gauche de l'image d'origine
T : Point de départ du haut de l'image d'origine

Par exemple, dans le schéma ci-dessous, pour extraire l'image B (500x300) de l'image A (800x600) à partir du point L (20 px à partir du bord gauche) et du point T (15 px à partir du bord haut)

$ convert image.jpg -crop 500x300+20+15 newImage.jpg

Même traitement mais sur plusieurs images

$ for f in *.png; do convert $f -crop 500x300+20+15 new_$f; done

Convertir des images png (ou autres) en un fichier pdf (avec compression des images en jpeg)

Attention, suite à une faille de sécurité, cette conversion n'est plus possible à moins de modifier un paramètre dans le fichier policy.xml de ImageMagick. Plus d'info ici.
Merci à Sergio pour l'info.

$ convert *.png -compress jpeg monDoc.pdf

Et inversement :

$ convert -density 500 monDoc.pdf monDoc.jpg

Afficher la liste de toutes les couleurs disponibles

$ convert -list color

Générer les échantillons de toutes les couleurs gérées par ImageMagick

for liste in $(convert -list color | sed '1,5d' | awk '{print $1 "|" $2}'); do couleur=$(cut -d'|' -f1 <<< $liste); code=$(cut -d'|' -f2 <<< $liste); convert -size 50x50 xc:"$code" $couleur.png; done

Cet enchainement de commandes permet de générer des images PNG de 50px de toutes les couleurs dans le dossier courant. Tous les fichiers sont nommés par leur nom de couleur.

Exemple des images générées : http://isbeta.fr/4f2fc

Centrer une image dans un calque :

La commande suivante permet de créer un calque transparent d'une taille de 100 x 100 pixels et d'y insérer, au centre,  une image proportionnellement redimensionnée à la taille du calque

convert img.png -resize 100x100 -size 100x100 xc:none +swap -gravity center -composite new_img.png

Par exemple :

Image d'origine

Image redimensionnée et centrée dans le calque

Réaliser une vidéo à partir d'images

$ convert -delay 25 *.jpg anim.mpg

-delay 25 = 25 images par seconde

Afficher les informations d'une image

$ identify image.jpg

Avec plus de détail

$ identify -verbose image.jpg

Créer une image à partir d'un texte

convert -background white -fill black -font "Cretino-Regular" -pointsize 30 -size 200x -gravity Center caption:"Mon texte." "e.gif"
$ display e.gif

e.gif

Créer une image à partir d'un texte contenu dans un fichier

$ cat texte
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sed finibus dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque ultrices erat in tempor aliquam. Ut ut facilisis lectus. Sed bibendum pellentesque urna, nec convallis massa.
convert -background "rgb(169,169,169)" -fill "rgb(255,255,255)" -font "Abecedario_pautada-Regular" -pointsize 40 -size 480x -gravity Center caption:"$(cat texte)" "d.gif"
display d.gif

d.gif

Souvenir d'école

f.gif

 

Vidéo


FFmpeg

Installation

Le site du projet ffmpeg : http://www.ffmpeg.org/

Pour installer FFmpeg :

$ sudo apt-get install ffmpeg

Depuis Ubuntu 14.04, ffmpeg n'est plus dans les dépôts Ubuntu.

Pour l'installer, sans passer par les dépôts, il suffit de télécharger le dernière version du programme sur le site officiel :

Pour la version 32 bits.

Pour la version 64 bits.

... puis décompresser l'archive téléchargée :

$ tar -xzf ffmpeg.static.[32|64]bit.latest.tar.gz

... puis copier les deux fichiers ffmpeg et ffprobe dans le répertoire /usr/local/bin :

$ sudo cp ffmpeg ffprobe /usr/local/bin

... et voilà, c'est terminé.

Pour vérifier la version installée :

$ ffmpeg -version

Ajouter un logo à une vidéo

Pour incruster une image dans une vidéo, il suffit d'utiliser le filtre overlay avec la commande ffmpeg.

Par exemple, pour ajouter un logo en bas, à droite :

$ ffmpeg -i movie.mp4 -i logo.png -filter_complex "overlay=W-w-5:H-h-5" -b:v 3000k -s 720x480 -codec:v h264 -fs 20M movie_avec_logo.mp4

Détail des options :

-b:v permet d'indiquer le bitrate

-s permet d'indiquer les dimensions

-codec:v permet d'indiquer le codec vidéo à utiliser

-fs permet d'indiquer la taille maxi du fichier généré

-i permet d'indiquer tous les fichiers à traiter en entrée

-filter_complex permet d'utiliser les filtres disponibles

Détail du filtre overlay :

Pour utiliser le filtre overlay, il suffit de lui indiquer les positions horizontale et verticale à utliser pour insérer l'image ou la vidéo.

Pour insérer un logo en haut à gauche :
overlay=0:0

Avec un décalage de 5 px des bords gauche et haut :
overlay=5:5

Il est possible d'utiliser les variables W, w, H et h qui correspondent à :
W : Largeur (width) de la vidéo principale
H : Hauteur (height) de la vidéo principale
w : Largeur (width) de l'image ou de la vidéo à incruster
h : Hauteur (height) de l'image ou de la vidéo à incruster

Donc, pour incruster une image en bas, à droite avec un décalage de 5 px des bords bas et droite :
overlay=W-w-5:H-h-5

Résultat :

Etiquettes: 

Conversion

Lister les codecs vidéos :

$ ffmpeg -codecs | grep DEV

Lister les codecs audios :

$ ffmpeg -codecs | grep DEA

Convertir une vidéo mpeg4 en GIF animé :

$ ffmpeg -i input.mp4 -vf scale=356:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif

Convertir une vidéo MOV en GIF animé :

1 - Conversion de la vidéo MOV à l'aide de la commande ffmpeg

$ ffmpeg -i input.mov -pix_fmt rgb24 output_tmp.gif

2 - Amélioration du GIF animé à l'aide de la commande convert

$ convert -layers output_tmp.gif output.gif

Convertir des images JPG/PNG en GIF animé:

$ convert -delay 20 -loop 0 *.JPG result.gif

Pour varier la vitesse du GIF animé, augmenter/diminuer la valeur du paramètre -delay

Extraire une partie d'une vidéo :

$ ffmpeg -i MOV.mp4 -ss 00:00:25 -t 00:00:10 MOV1.mp4

Cette commande permet d'extraire 10 secondes à partir de la 25ème seconde

-ss : Début de l'extraction
-t : Durée de l'extraction

Ralentir (effet slow motion) une vidéo :

$ ffmpeg -i MOV.mp4 -vf "setpts=8*PTS" MOV5.mp4

Plus le coefficient multiplicateur est élevé, plus la vidéo est ralentie.
1 étant le coefficient de base.

Accélérer une vidéo :

$ ffmpeg -i MOV.mp4 -vf "setpts=0.2*PTS" MOV5.mp4

Encoder une vidéo en 2 passes :

$ for P in 1 2; do ffmpeg -y -i MOV.mp4 -b:v 3000k -pass $P -r 30 -c:v h264 -c:a mp3 -b:a 128k -s 1024*768 MOV.avi; done

-b:v 3000k : Permet de régler le bitrate de la vidéo
-r 30 : Permet de définir le nombre d'images par seconde
-pass $P : Permet d'indiquer le numéro de la passe à effectuer
-c:v h264 : Permet d'indiquer le codec vidéo à utiliser
-c:a mp3 : Permet d'indiquer le codec audio à utiliser
-s 1024*768 : Permet d'indiquer la taille de la vidéo
-b:a 128k : Permet de régler le bitrate audio

Pivoter une vidéo :

$ ffmpeg -i MOV.mp4 -vf "transpose=1" MOV1.mp4

-vf "transpose=1" : Permet de faire pivoter la vidéo de 90 degrés vers la droite
-vf "transpose=2" : Permet de faire pivoter la vidéo de 180 degrés vers la droite
etc etc ...

Effet miroir :

$ ffmpeg -i MOV.mp4 -vf "hflip" MOV1.mp4

-vf "hflip" : Effet miroir horizontal
-vf "vflip" : Effet miroir vertical

Assembler plusieurs vidéos en un seul fichier :

$ ffmpeg -i "concat:MOV1.avi|MOV2.avi|MOV3.avi|MOV4.avi" -c copy MOV_0005.avi

-c copy : Permet d'indiquer de ne pas réencoder les vidéos

Conversion FLV To AVI :

$ ffmpeg -i mavideo.flv mavideo.avi

Conversion AVI To FLV :

$ ffmpeg -i mavideo.avi mavideo.flv

Conversion MP3 To WAV :

$ ffmpeg -i monFichier.mp3 monFichier.wav

Extraire toutes les images d'une vidéo :

$ ffmpeg -i foo.avi -r 1 -s WxH -f image2 foo-%03d.jpeg

-s WxH -> dimensions des images générées
-f image2 -> format de sortie
-r 1 -> fréquence d'images : 1 image par seconde

Créer une vidéo à partir de plusieurs images :

ffmpeg -f image2 -i foo-%03d.jpeg -r 12 -s WxH foo.avi

-r 12 -> fréquence d'images : 12 images par seconde
-s WxH -> dimension de la vidéo générée
 -f image2 -> format du/des fichiers en entrée

Mencoder


Installation

Pour installer MEncoder:

$ sudo apt-get install mencoder (installation automatique des dépendances requises)

Lister tous les codecs audios supportés par MEncoder :

$ mencoder -oac help

Lister tous les codecs vidéos supportés par MEncoder :

$ mencoder -ovc help

Lister tous les formats vidéos de sortie supportés par MEncode :

$ mencoder -of help

AVI étant le format par défaut de MEncoder et le mieux supporté

Conversion

Pour convertir des images au format JPG en une séquence vidéo au format AVI :

$ mencoder -ffourcc XVID mf://*.jpg -mf w=800:h=600:fps=25:type=jpg -ovc lavc -lavcopts vcodec=mpeg4 -oac copy -o output.avi

  • mf://*jpg tout les jpg du repertoire courant (on peut spécifier un autre répertoire en entrant son chemin complet entre guillemets)
  • w=800:h=600:fps=25:type=jpg image de type jpg de dimension 800x600 avec en sortie une video à 25 img/sec
  • -ovc lavc -lavcopts vcodec=mpeg4 encodage en mpeg4 ISO compatible xvid

Pour convertir une vidéo au format AVI en utilisant le codec de compression MJPEG :

$ mencoder fichier_in.avi -oac copy -ovc lavc -lavcopts vcodec=mjpeg -o fichier_out.avi

  • -oac copy copie sans encodage de la bande son
  • -ovc lavc -lavcopts vcodec=mjpeg encodage de la video en MJPEG avec la famille de codec lavc (projet FFmpeg)

Pour convertir une vidéo au format AVI vers le format FLV permettant sa lecture dans un navigateur internet avec le plugin Macromedia Flash :

$ mencoder input.avi -o output.flv -of lavf -oac mp3lame -lameopts abr:br=56 -ovc lavc \ -lavcopts vcodec=flv:vbitrate=500:mbd=2:mv0:trell:v4mv:cbp:last_pred=3 \ -srate 22050

Conversion WMV To MPEG-4 (Xvid) :

$ mencoder mavideo.wmv -ovc xvid -oac mp3lame -o mavideo.avi

Conversion FLV To AVI (MPEG-4/Divx) en 2 passes :

$ mencoder video.flv -ovc lavc -lavcopts vcodec=mpeg4:vpass=1 -oac copy -o a.avi
$ mencoder video.flv -ovc lavc -lavcopts vcodec=mpeg4:vpass=2 -oac copy -o a.avi

transcode

transcode est un programme de traitement vidéo.

Pour l'installer :

$ sudo apt-get install transcode

Il permet de faire énormément de choses mais mon option préférée est celle qui permet de stabiliser une vidéo.

Le résultat est vraiment très surprenant.

Exemple :

Pour stabiliser une vidéo, il faut d'abord que transcode analyse la vidéo source...

$ transcode -J stabilize -i movie.avi

Cette commande va générer un fichier trf.

Ajouter l'option --mplayer_probe pour utiliser le programme mplayer pour l'analyse si le format de la vidéo n'est pas directement supporté par transcode.

... et enfin, pour stabiliser la vidéo :

$ transcode -J transform --mplayer_probe -i movie.avi -y raw -o movie_stabilize.avi

Attention, la taille de la vidéo générée est très volumineuse avec le format raw.

Il est possible d'indiquer un codec vidéo pour l'encodage final :

$ transcode -J transform --mplayer_probe -i movie.avi -y xvid4 -o movie_stabilize.avi

La preuve en image :

A gauche, la vidéo d'origine, à droite, après stabilisation.

Surprenant, non ?

Etiquettes: 

Paramétrer un serveur proxy

Pour paramétrer un serveur proxy dans Ubuntu :

Editer le fichier /etc/apt/apt.conf

Pour un proxy sans authentification

Acquire::http::Proxy "http://IP.DU.PROXY:PORT";

Pour un proxy avec authentification

Acquire::http::Proxy "http://user:passwd@IP.OU.NOM.DNS.DU.PROXY:PORT";

Pour paramétrer un serveur proxy provisoirement avant d'exécuter une commande :

Saisir dans une console

$ http_proxy="http://user:passwd@IP.OU.NOM.DNS.DU.PROXY:PORT" sudo apt-get update

Pour paramétrer un serveur proxy provisoirement si plusieurs commandes à exécuter :

Saisir dans une console

$ export http_proxy="http://user:passwd@IP.OU.NOM.DNS.DU.PROXY:PORT"
$ sudo apt-get update
$ sudo apt-get install mon_paquet...

Etiquettes: 

Partage Samba

Accès à un partage Samba sous Ubuntu

Pré requis :

installer les paquets smbclient & cifs-utils

$ apt-get install smbclient
$ apt-get install cifs-utils

Configuration :

  • 1 poste serveur avec Ubuntu Server et des partages Samba activés
  • 1 poste client avec Ubuntu Desktop

Monter un partage via la console :

$ mount -t cifs //adresse_ip_du_serveur/nom_du_partage /point_de_montage --verbose -o user=user_samba,password=password_samba

Pour monter automatiquement les partages Samba de Ubuntu Server sur Ubuntu Desktop avec une identification

Editer le fichier /etc/fstab

$ sudo -s
$ gedit /etc/fstab

Y rajouter la ligne suivante pour un partage nommé public sur Ubuntu Server

//adresse_ip_du_serveur/public /home/user/Public smbfs _netdev,credentials=/home/user/.smbcredentials,iocharset=utf8 0 0

Explications :

//adresse_ip_du_serveur/public : adresse et nom du partage Samba sur le serveur

/home/user/Public : destination du montage

smbfs : type du montage (Samba)

_netdev : permet d'attendre que la connexion réseau soit effective avant d'effectuer le montage

credentials=/home/user/.smbcredentials : chemin et nom du fichier permettant l'identification

iocharset=utf8 : permet la prise en charge des caractères accentués

Création du fichier d'identification .smbcredentials

$ gedit /home/user/.smbcredentials

Y inscrire les informations suivantes

username=USERNAME

password=PASSWORD

Pour les partages Samba comportant un espace

Par exemple, pour accéder au partage Samba "Mes images" inscrire la ligne suivante dans le fstab

//adresse_ip_du_serveur/Mes\040images /home/user/Images smbfs _netdev,credentials=/home/user/.smbcredentials,iocharset=utf8 0 0

La chaîne de caractères "\040" correspond au caractère "espace"

Etiquettes: 

Quitter la console Ubuntu ou autres

Pour quitter une console, il existe différentes manières de le faire.

Avec la commande exit :

$ exit

Avec les touches ctrl+d (^D) ;

$ ^d     # Saisie clavier ctrl+d

Sauvegarde différentielle

Documentation Ubuntu

Pour effectuer une sauvegarde différentielle, il faut utiliser le programme rdiff-backup.

Pour l'installer :

$ sudo apt-get install rdiff-backup

Créer une sauvegarde différentielle

Pour créer une sauvegarde différentielle

$ rdiff-backup /repertoire_a_sauvegarder /repertoire_de_sauvegarde

Sauvegarder sur une machine distante via    SSH

Remplacer

  • /repertoire_de_sauvegarde

Par

  • <utilisateur>@<adresse_ip_de_la_machine_distante>::<repertoire_de_sauvegarde_sur_la_machine_distante>

Supprimer une sauvegarde différentielle

Pour supprimer une sauvegarde différentielle

$ rdiff-backup --remove-older-than 1W --force /repertoire_de_sauvegarde

Ici on enlève toutes les modifications enregistrées vieilles de plus d'une semaine. On peut mettre D(ay),W(eek), M(onth)et Y(ear).

S'il y a plusieurs sauvegardes à supprimer, la commande échoue. C'est pourquoi il faut rajouter le paramètre –forcepour ne pas prendre cette erreur en compte et que rdiff-backup effectue bien ce qu'on lui demande.

Afficher l'état d'une sauvegarde différentielle

Pour afficher l'état d'une sauvegarde différentielle en liste simple

$ rdiff-backup -l /repertoire_de_sauvegarde

Pour afficher l'état d'une sauvegarde différentielle en tableau avec la taille de chaque incrément:

$ rdiff-backup --list-increment-size /repertoire_de_la_sauvegarde

Pour enregistrer le résultat dans un fichier :

$ rdiff-backup --list-increment-size /repertoire_de_la_sauvegarde > /repertoire/etat_sauvegarde.txt

Voir les statistiques des sauvegardes

$ rdiff-backup-statistics /repertoire_de_sauvegarde

Restaurer une sauvegarde différentielle

Pour restaurer une sauvegarde différentielle

$ rdiff-backup -r now /repertoire_de_sauvegarde /repertoire_de_restauration

L'option -r nowpermet de remettre la version de la sauvegarde la plus récente. On peut remonter dans les sauvegardes, par exemple remettre le repertoire d'il y a 3 jours avec l'option -r 3D.


Rdiff-backup fait une replication parfaite des fichiers dans leur dernière version. Ainsi pour restaurer des données, il suffit de faire

$ cp -a /backupDir /restoredDir

Système

Afficher la version du noyau utilisé

Pour connaitre la version du noyau en cours d'utilisation, saisir dans une console la commande suivante :

$ sudo uname -a
Linux vm-apache 2.6.32-14-pve #1 SMP Tue Aug 21 08:24:37 CEST 2012 i686 GNU/Linux
 

 

Etiquettes: 

Afficher la version du système d'exploitation Linux

$ cat /etc/os-release

Etiquettes: 

Corriger un problème de locale

Sur un système GNU/Linux, une locale qui n'est pas correctement configurée entraîne ce genre de message d'erreur :

-bash: warning: setlocale: LC_CTYPE: cannot change locale (en_GB.UTF-8)
-bash: warning: setlocale: LC_CTYPE: cannot change locale (en_GB.UTF-8)

Pour corriger cette erreur, il suffit d'exécuter les 2 commandes suivantes :

$ sudo locale-gen en_GB.UTF-8
$ sudo update-locale LANG=en_GB.UTF-8

Une simple déconnexion / reconnexion suffit pour ne plus avoir le problème.

Forcer la vérification des disques

Sous Linux, la vérification des disques s'effectue avec la commande fsck.

L'utilisation de cette commande nécessite que la partition à controler soit démontée.

Lister les disques et partitions du système

$ fdisk -l
 
Disk /dev/sdb: 1500.3 GB, 1500299395072 bytes
255 heads, 63 sectors/track, 182401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00026d5d
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      182401  1465136001   83  Linux

Identifier le type de la partition

$ blkid
/dev/sdb1: UUID="a6ea6463-e289-4bbe-afba-572f876497da" TYPE="ext4"

Identifier le point de montage de la partition

$ mount
/dev/sdb1 on /volume1 type ext4 (rw)

Démonter la partition et lancer la vérification

$ umount /volume1
$ fsck -t ext4 /dev/sdb1


Si le démontage de la partition n'est pas possible, il existe 2 solutions permettant la vérification des disques.

1 - Redémarrer le système en forçant la vérification :

$ shutdown -r -F now

2 - Créer le fichier forcefsck et redémarrer le système :

$ touch /forcefsck
$ reboot

Après le redémarrage et la vérification des disques, le fichier /forcefsck est automatiquement supprimé.

Les pages de manuels en couleur

Par défaut, les pages de manuels utilisent la commande less pour les afficher.

La commande less est une évolution de la commande more.
Elle permet de mettre en gras les rubriques principales des pages de manuels.

La commande most, permet d'ajouter une coloration syntaxique et bien d'autres options supplémentaires.

Pour installer la commande most :

$ sudo apt-get install most

Pour utiliser most pour parcourir les pages de manuels :

$ PAGER=most man cp

Cette commande permet d'utiliser most comme PAGER pour consulter le manuel de la commande cp

Pour utiliser most comme PAGER par défaut :

En BASH, ajouter cette ligne dans le fichier ~/.bashrc

export PAGER=most

Autres options disponibles avec la commande most (partant du principe que most est le PAGER par défaut):

Non seulement most permet de consulter les pages de manuels mais il peut également afficher le contenu de n'importe quel fichier.

$ man cp                       # consultation du manuel avec la commande most si PAGER par défaut
$ most /mon_fichier
 
H : affichage de l'aide most
Espace ou D : défilement page par page vers le bas
Delete ou U : défilement page par page vers le haut
Entrée ou Flèche bas : défilement ligne par ligne vers le bas
Flèche haut : défilement ligne par ligne vers le haut
T : se positionne en haut du document
B : se positionne en bas du document
J ou G : se positionne à la ligne voulue
> : déplacement vers la droite
< : déplacement vers la gauche
S ou f ou / : permet d'effectuer une recherche vers le bas
? : permet d'effectuer une recherche vers le haut
N : recherche les occurences suivantes

En mode affichage de fichier :

E : bascule en mode édition avec l'éditeur par défaut

En mode affichage de logs en continu :

au lieu de

$ tail -f /var/log/access.log

utiliser most puis l'option F

$ most /var/log/access.log
F : simule la commande tail -f

Etiquettes: 

Processus

Les n premiers processus les plus consommateur de cpu

La commande suivante permet d'afficher les 15 premiers processus les plus gourmands en processeur.

$ sudo ps auxk -pcpu | head -n 16
Etiquettes: 

PS

Pour savoir si un processus est actif :

$ sudo ps -ef | grep nom_du_programme

ou

$ pgrep nom_du_programme

La commande ci-dessus retourne le numéro du processus recherché.

Afficher les processus d'un user particulier (par exemple root):

$ pgrep -u root

top - afficher un processus Linux spécifique

Pour afficher les statistiques d'un programme spécifique à l'aide de la commande top:

L'exemple suivant affiche tous les processus nginx.

$ top -p$(pgrep -d, nginx)

L'argument -d de la commande pgrep permet d'afficher les numéros des processus séparés par une virgule.

L'argument -p de la commande top permet de filtrer uniquement les processus indiqués.

Etiquettes: 

Réseau

Netstat

Pour savoir si un port est utilisé par une application :

$ sudo netstat -lpn | grep :80 (numéro du port)

Etiquettes: 

logger : Journaliser des évènements dans /var/log/syslog

La commande logger permet d'enregistrer des informations dans le fichier /var/log/syslog.

Cette commande est idéale pour journaliser différents évènements pendant l'exécution d'un script.

Elle peut être utilisée tout simplement :

$ logger -t monTag monMessage
$ tail /var/log/syslog
Apr 23 15:23:37 ubuntu10.04 monTag: monMessage

L'option -t permet d'indiquer un tag. Très utile pour effectuer des recherches dans le fichier syslog.

$ logger -t monTag Je peux également écrire un texte avec des espaces
$ tail /var/log/syslog
Apr 23 15:26:12 ubuntu10.04 monTag: Je peux également écrire un texte avec des espaces

La commande logger permet également d'enregistrer dans syslog le contenu d'un fichier.

$ cat fic1
systèmes
coucou
recoucou
$ logger -t FIC1 -f fic1
$ tail /var/log/syslog
Apr 23 15:29:13 ubuntu10.04 FIC1: systèmes
Apr 23 15:29:13 ubuntu10.04 FIC1: coucou
Apr 23 15:29:13 ubuntu10.04 FIC1: recoucou

Utiliser la commande logger dans un script :

$ cat comparaison.sh
#!/bin/bash
logger -t $0 Execution du script
# Test sur le nombre d'arguments
if [[ $# -ne 2 ]]
then
        logger -t $0 Nombre d\'arguments incorrect
        echo "Mauvais nombre d'arguments"
        echo "Utilisation : $0 nbr1 nbr2"
        exit 1
fi
# On compare les nombres
logger -t $0 Comparaison de $1 et $2
if [[ $1 -gt $2 ]]
then
        logger -t $0 $1 est superieur a $2
        echo "Comparaison : $1 est superieur a $2"
else
        logger -t $0 $1 est inferieur a $2
        echo "Comparaison : $1 est inferieur a $2"
fi
logger -t $0 Fin du script
exit 0

Dans ce script, j'utilise le nom du script comme tag (logger -t $0).

Exécution du script :

$ ./comparaison.sh 10 20
Comparaison : 10 est inferieur a 20
$ tail /var/log/syslog
Apr 23 15:51:57 ubuntu10.04 ./comparaison.sh: Execution du script
Apr 23 15:51:57 ubuntu10.04 ./comparaison.sh: Comparaison de 10 et 20
Apr 23 15:51:57 ubuntu10.04 ./comparaison.sh: 10 est inferieur a 20
Apr 23 15:51:57 ubuntu10.04 ./comparaison.sh: Fin du script

Tous mes évènements ont été enregistrés dans sylog.

Il est également possible, dans le script, d'enregistrer les évènements dans un fichier texte avec la commande echo et seulement, à la fin du script, d'utiliser la commande logger pour enregistrer le contenu du fichier texte dans le fichier syslog.

$ cat comparaison.sh
#!/bin/bash
echo "Execution du script" > /root/logComparaison
# Test sur le nombre d'arguments
if [[ $# -ne 2 ]]
then
        echo "Nombre d'arguments incorrect" >> /root/logComparaison
        echo "Mauvais nombre d'arguments"
        echo "Utilisation : $0 nbr1 nbr2"
        exit 1
fi
# On compare les nombres
echo "Comparaison de $1 et $2" >> /root/logComparaison
if [[ $1 -gt $2 ]]
then
        echo "$1 est superieur a $2" >> /root/logComparaison
        echo "Comparaison : $1 est superieur a $2"
else
        echo "$1 est inferieur a $2" >> /root/logComparaison
        echo "Comparaison : $1 est inferieur a $2"
fi
echo "Fin du script" >> /root/logComparaison
logger -t $0 -f /root/logComparaison
exit 0

Tous les évènements sont enregistrés dans le fichier /root/logComparaison à l'aide de la commande echo.
Attention, le premier appel à la commande echo écrase le contenu du fichier /root/logComparaison s'il existe (utilisation d'un seul chevron ">" dans la redirection).
Pour terminer, tout le contenu du fichier est enregistré dans syslog à l'aide de la commande logger et de l'option -f suivi du nom du fichier (utilisation du nom du script en tag).

$ ./comparaison.sh 100 20
Comparaison : 100 est superieur a 20
$ cat logComparaison
Execution du script
Comparaison de 100 et 20
100 est superieur a 20
Fin du script
$ tail /var/log/syslog
Apr 23 16:21:10 ubuntu10.04 ./comparaison.sh: Execution du script
Apr 23 16:21:10 ubuntu10.04 ./comparaison.sh: Comparaison de 100 et 20
Apr 23 16:21:10 ubuntu10.04 ./comparaison.sh: 100 est superieur a 20
Apr 23 16:21:10 ubuntu10.04 ./comparaison.sh: Fin du script

Etiquettes: 

Ubuntu server

Connaitre sa version Ubuntu

Pour connaitre la version d'Ubuntu installée sur le système, saisir dans une console la commande suivante :

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 10.04.4 LTS
Release:        10.04
Codename:       lucid

L'information concernant la version installée apparait à la ligne Description

Sinon, il y a le fichier /etc/issue (fonctionne avec Ubuntu et Debian)

$ file /etc/issue
/etc/issue: ASCII text
$ cat /etc/issue
Debian GNU/Linux 6.0 \n \l
 
$ cat /etc/issue
Ubuntu 10.04.4 LTS \n \l

Il y a également le fichier /etc/debian_version

$ file /etc/debian_version
/etc/debian_version: ASCII text
$ cat /etc/debian_version
squeeze/sid

Enfin, il y a le fichier /etc/lsb-release (informations fournies avec la commande lsb_release -a)

$ file /etc/lsb-release
/etc/lsb-release: ASCII text
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=10.04
DISTRIB_CODENAME=lucid
DISTRIB_DESCRIPTION="Ubuntu 10.04.4 LTS"

Pour connaitre la version du noyau Linux, saisir dans une console la commande suivante :

$ uname -a
Linux vm-apache 2.6.32-14-pve #1 SMP Tue Aug 21 08:24:37 CEST 2012 i686 GNU/Linux
 

Arrêter un serveur Ubuntu

Pour arrêter un serveur Ubuntu, saisir la commande suivante dans une console

$ sudo halt

Ou en utilisant la commande shutdown :

$ sudo shutdown -h now

L'indication now permet un arrêt immédiat.

Pour un arrêt dans 1 minute :

$ sudo shutdown -h +1 &

Pour un arrêt à une heure précise :

$ sudo shutdown -h 16:50 &

Pour annuler un arrêt programmé :

$ sudo shutdown -c

Dans le cas d'un arrêt programmé, un message est envoyé toutes les minutes à tous les utilisateurs connectés au système pour annoncer l'arrêt.

Redémarrer un serveur Ubuntu

Pour redémarrer un serveur Ubuntu, saisir la commande suivante dans une console

$ sudo reboot

Ou en utilisant la commande shutdown :

$ sudo shutdown -r now

L'indication now permet un redémarrage immédiat.

Pour un redémarrage dans 1 minute :

$ sudo shutdown -r +1 &

Pour un redémarrage à une heure précise :

$ sudo shutdown -r 16:50 &

Pour annuler un redémarrage programmé :

$ sudo shutdown -c

Dans le cas d'un redémarrage programmé, un message est envoyé toutes les minutes à tous les utilisateurs connectés au système pour annoncer le redémarrage.

Erreur --- perl: warning: Setting locale failed.

J'ai souvent rencontré sur mes VMs Ubuntu Server cette erreur :

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LANG = "fr_FR.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

J'ai donc tenté une re-configuration des locales :

$ dpkg-reconfigure locales
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LANG = "fr_FR.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory

Sans succès.

Pour corriger cette erreur, il faut utiliser la commande locale-gen :

$ locale-gen fr_FR.UTF-8
Generating locales...
  fr_FR.UTF-8... done
Generation complete.

Puis lancer la re-configuration des locales :

$ dpkg-reconfigure locales
Generating locales...
  fr_FR.UTF-8... up-to-date
Generation complete.

Voir aussi

Etiquettes: 

Locale française sur Ubuntu Server

Pour forcer Ubuntu Server à prendre en compte la langue française avec un encodage de caractères en UTF-8, il faut installer le package language-pack-fr, indiquer dans le fichier /etc/default/locale la valeur fr_FR.UTF-8 à la variable LANG puis lancer une reconfiguration du système.

$ apt-get install language-pack-fr
$ if [ -f /etc/default/locale ]; then cp /etc/default/locale /etc/default/locale_default; fi
$ echo "LANG=fr_FR.UTF-8" > /etc/default/locale
$ cat /etc/default/locale
LANG=fr_FR.UTF-8
$ dpkg-reconfigure locales

Les modifications seront effectives à la prochaine connexion.

Source : http://www.aepik.net/documentation/ubuntu/server/locales

Etiquettes: 

GPG: Communiquer de manière sécurisée

GPG (GnuPG), abréviation de GNU Privacy Guard est un outil qui permet d'envoyer des messages signés et/ou chiffrés.

Dans le cas de messages signés, cela permet au destinataire de savoir exactement que le message qu'il reçoit a bien été envoyé par l'expéditeur indiqué et qu'il n'a pas été altéré.

Dans le cas de messages chiffrés, seul le destinataire du message est en mesure de le déchiffrer. Même l'expéditeur ne peut pas le faire.

Pour fonctionner, GPG utilise un système de paire de clés.
L'association d'une clé privée et d'une clé publique.
Comme son nom l'indique, la clé privée est privée et ne doit surtout pas être divulgée.
La clé publique, quant à elle, doit obligatoirement être communiquée à toutes les personnes avec lesquelles vous souhaitez communiquer de manière sécurisée (chiffrée).
Pour cela, il existe des serveurs de clés publiques où n'importe qui est en mesure de récupérer la clé publique de n'importe qui.

Wikipedia Plus d'infos sur Wikipédia

Le site de GnuPG

Infographie conçue par la FSF

info

Une version de GnuPG est disponible pour Android, sur Google Play.
Une extension, Enigmail, est disponible pour le logiciel de messagerie Thunderbird.
Une extension, Mailvelope, est disponible pour les navigateurs Firefox et Chrome.

Principe de fonctionnement

La personne "A" souhaite communiquer de manière sécurisée avec la personne "B" et inversement.
Chacun de leur coté, "A" et "B" génèrent leurs clés privées et publiques et publient leurs clés publiques sur un serveur de clés.
"A" recherche sur le serveur de clés la clé publique de "B".
"A" rédige un message dans un fichier, le signe et le chiffre à l'aide de la clé publique de "B".
Un nouveau fichier est généré mais le contenu de celui-ci est chiffré.
"A" envoie ce fichier par mail à "B".
"B" reçoit de "A" un mail (automatiquement signé par la clé privée de "A") et ayant en pièce jointe le fichier contenant le message chiffré.
Si ce n'est pas déjà fait, "B" récupère la clé publique de "A" lui permettant de vérifier que la signature du message est bien authentique et que le contenu du message n'a pas été modifié.
Enfin, "B", à l'aide de sa clé privée, déchiffre le contenu de la pièce jointe.
Le processus est terminé.
Si le message de "A" a été intercepté par une personne malintentionnée "C", le contenu de la pièce jointe ne pourra pas être déchiffré sans la clé privée de "B". "C" ne pourra donc pas lire le contenu de la pièce jointe.

Exemple d'un message chiffré

Avant chiffrement

Ceci est un message chiffré.

Après chiffrement

-----BEGIN PGP MESSAGE-----
Version: GnuPG v2

hQEMAxetSMh+B9VxAQf/VvFi7T89Gn1gEfnsg1WTOW1uOJX9KgVnWF3QCxr87/zf
T0PCvJnEnt9HirAMu+nn9iMmnSju89RhIFjQkTVltLlfOJY7kQQBc1DdM7h2MYNI
632daM650f6LGa1ZQ3gbdc/pmdjq+YxrqJQM+lC36n3nSbuQjluJF2nv/R70sQXw
4pl2arebgqq45dRwNiGvOFsMpjyS1rLgQ9noA0p+N+OpiK/3YezRJGNl2SVspBqh
zDh/Qcweq2R66dtr8gsPhkeBy0/wy4bI2o6CC7oGEFC+RnKqkC3V4ZvfHhY3gjgu
pxlit6cKgoUfSXiMLWioKinWaqF4U+/YMyRnsLYAytJYAdI4rniMl89CbsQYhYCn
l7vTPBnUhLzHFQv/L+vcnxOPrRohVVfIm+zNyHv4QtSvtHc+/WTbhERE+cdCRvXP
l5oFR4/znpzU9XPknSjpRUZ3Ugy94bfe/Q==
=YUMp
-----END PGP MESSAGE-----

Certe, le message chiffré est un peu beaucoup plus long mais personne ne peut le lire mis à part celui pour lequel il a été chiffré.

Pour info La longueur du message chiffré n'est pas proportionnelle à la longueur du message clair.
Dans l'exemple ci-dessus, le message clair fait 29 caractères et le message chiffré 572 caractères.
Un autre test, avec un message clair de 116 caractères produit un message chiffré de 682 caractères.

Les différentes méthodes de chiffrement

Avec GPG, il est possible de chiffrer un message de trois manières différentes.

  1. Chiffrement du message en mode ASCII et copié/collé du message chiffré directement dans le corp du mail.
  2. Ecriture du message dans un fichier.
    Chiffrement du fichier contenant le message en mode ASCII.
    Envoi du mail avec le fichier en pièce jointe.
  3. Ecriture du message dans un fichier.
    Chiffrement du fichier contenant le message en mode Binaire.
    Envoi du mail avec le fichier en pièce jointe.

Pour info Pour un envoi directement dans le corp du mail, le mode ASCII est obligatoire
Pour un envoi en pièce jointe, c'est selon vos préférences ou la taille du fichier généré.

Etiquettes: 

En route avec GPG

Etiquettes: 

Pré-requis

Pour pouvoir générer correctement les paires de clés (privée et publique), GPG a besoin d'une quantité importante de données aléatoires.

Pour générer ces données aléatoires, le système (Ubuntu/Debian/CentOS etc etc ...) a besoin d'une activité importante au niveau du processeur, de l'utilisation du ou des disques durs etc et c....

Pour faciliter la tâche, il existe un programme rng-tools, qui permet de faciliter la génération de données aléatoires sans solliciter le matériel.

# apt-get install rng-tools

# echo "HRNGDEVICE=/dev/urandom" >> /etc/default/rng-tools

# service rng-tools start

Il n'y a rien de plus à faire.
Désormais, tous les programmes, comme GPG, ayant un besoin important de données aléatoires auront leurs tâches facilitées par rng-tools.

Etiquettes: 

Création de la paire de clés (privée et publique)

On exécute la commande gpg --gen-key

# gpg --gen-key
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory `/root/.gnupg' created
gpg: new configuration file `/root/.gnupg/gpg.conf' created
gpg: WARNING: options in `/root/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring `/root/.gnupg/secring.gpg' created
gpg: keyring `/root/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1

Lors du choix du type de clé, choisir (1) RSA and RSA (default)

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits

Pour la longueur de la clé, laissé le choix par défaut 2048 bits.

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

Pour la durée de validité de la clé, à vous de choisir.
Pour l'exemple je choisi 0 (La clé n'expire jamais)
Valider en répondant y pour YES

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Jean Michel A Peu Près
Email address: jm@apeupres.fr
Comment:
You selected this USER-ID:
    "Jean Michel A Peu Près <jm@apeupres.fr>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

Renseigner votre nom, votre adresse mail et un commentaire si vous le souhaitez.
Valider en répondant o pour OK

You need a Passphrase to protect your secret key.

Enter passphrase:
Repeat passphrase:

Renseigner une phrase de passe.
Surtout, ne la perdez pas.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

GPG ayant besoin d'un grand nombre de données aléatoires pour pouvoir générer les clés, il est conseillé d'utiliser le poste un maximum pendant la génération des clés.
Utiliser au maximum le clavier, la souris etc etc.

gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 0D6FE738 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   2048R/0D6FE738 2015-09-11
      Key fingerprint = C643 9D6C 9456 9961 58C1  CDC7 F700 1FBC 0D6F E738
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
sub   2048R/1C51C149 2015-09-11

Une fois terminé, GPG nous informe que la clé privée et publique ont été créées et signées.
Le détail des clés est affiché à l'écran.

En fait, on s'aperçoit que la clé publique  0D6FE738 contient une sous-clé 1C51C149 (idem pour la clé privée).
Ce sont donc au total 4 clés qui ont été créées.

note Une fois votre clé créée, vous pouvez indiquer dans votre fichier ~/.bashrc qu'il s'agit de votre clé par défaut en ajoutant la ligne:

# echo "export GPGKEY=0D6FE738" >> ~/.bashrc

 

Etiquettes: 

Lister les clés

Afficher toutes les clés (pub) et sous-clés (sub) publiques de notre trousseau

# gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
sub   2048R/1C51C149 2015-09-11

pub   2048R/488606BD 2013-10-11 [expires: 2018-10-10]
uid                  Christopher lowson <webmin@lowson.ca>
uid                  Christopher Lowson <lowson.chris@gmail.com>
uid                  [jpeg image of size 2653]
sub   2048R/CA15AA23 2013-10-11 [expires: 2018-10-10]

pub   1024D/ACC3599B 2011-03-19
uid                  BT <bt@webmin.net>
sub   1024g/DA013131 2011-03-19

pub   1024D/A7F37C67 2009-04-17 [expired: 2010-07-31]
uid                  Max Ober <max.ober@gmail.com>
uid                  mober.at <post@mober.at>
uid                  mober.at <webmin@mober.at>
uid                  Max Ober <mober.itsb2006@fh-salzburg.ac.at>
uid                  n0942544@students.meduniwien.ac.at <n0942544@students.meduniwien.ac.at>

Pour info Ma clé publique 0D6FE738 apparait bien dans la liste ainsi que différentes clés importées 488606BD, ACC3599B, A7F37C67.

Afficher uniquement une clé spécifique (on indique l'uid de la clé ou son numéro en 6 caractères)

# gpg --list-keys Jean Michel A Peu Près

ou

# gpg --list-keys 0D6FE738
pub   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
sub   2048R/1C51C149 2015-09-11

Nous voyons bien que notre clé publique 0D6FE738 contient une sous-clé 1C51C149

Afficher la liste des clés (sec) et sous-clés (ssb) privées

# gpg --list-secret-keys
/root/.gnupg/secring.gpg
------------------------
sec   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
ssb   2048R/1C51C149 2015-09-11

Nous voyons également que notre clé privée 0D6FE738 contient, comme sa clé publique, une sous-clé 1C51C149

Etiquettes: 

Editer une clé

Nous allons éditer notre clé afin d'y apporter quelques modifications.

Pour info Cette commande nous permet d'entrer dans le mode interactif de GPG

# gpg --edit-key 0D6FE738
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

pub  2048R/0D6FE738  created: 2015-09-11  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/1C51C149  created: 2015-09-11  expires: never       usage: E
[ultimate] (1). Jean Michel A Peu Près <jm@apeupres.fr>

gpg>

Le mode édition nous permet de voir notre clé plus en détail.
Nous voyons, par exemple, que la clé privée est disponible (Secret key is available).
Nous voyons également, au niveau du champ usage, que notre clé maitresse 0D6FE738 peut-être utilisée pour certifier ou signer la clé publique d'un utilisateur (lettre C) et signer un document [mail / fichier] (lettre S).
Nous voyons que nous avons une sous-clé 1C51C149 qui va nous permettre de chiffrer un document [mail / fichier] (lettre E dans le champ usage).
Pour finir, nous voyons tous les détails concernant la date de création (created), la date d'expiration (expires) et le niveau de confiance (trust).

Liste des différents usages des clés

  • C: certifier ou signer la clé publique d'un utilisateur
  • S: signer un document [mail / fichier]
  • E: chiffrer un document [mail / fichier]
  • A: authentification SSH, TLS, etc etc ...

Liste des principales commandes disponibles dans le mode interactif

quit - Quitte le menu
save - Sauvegarde les modifications et quitte le menu
help - Affiche l'aide
fpr - Affiche l'empreinte de la clé sur 40 caractères
list - Affiche le détail de la clé
uid - Permet de sélectionner l'uid désiré
key - Permet de sélectionner la sous-clé désirée
check - Vérifie les signatures
sign - Signe l'uid sélectionné
adduid - Ajoute un uid
addphoto - Ajoute une photo
deluid - Supprime l'uid sélectionné
addkey - Ajoute une sous-clé
delkey - Supprime la sous clé sélectionnée
delsig - Supprime la signature de l'uid sélectionné
expire - Change la date d'expiration de la clé ou sous-clé sélectionnée
toggle - Bascule la liste clé publique / clé privée
passwd - Change la phrase de passe
trust - Modifie le niveau de confiance
revsig - Révoquer la signature de l'uid sélectionné
revuid - Révoquer l'uid sélectionné
revkey - Révoquer la clé ou sous-clé sélectionnée
enable - Active la clé
disable - Désactive la clé

Ajout d'une sous-clé

Notre clé maitresse 0D6FE738 ayant deux usages (certification/signature de clé publique et signature de documents), nous allons ajouter une sous-clé qui nous permettra, uniquement, la signature de documents.
De la même manière que notre sous-clé 1C51C149 nous permet uniquement le chiffrement de documents.

gpg> addkey
Key is protected.

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 0D6FE738, created 2015-09-11

Il faut obligatoirement saisir la phrase de passe

gpg: gpg-agent is not available in this session
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
Your selection? 4

Nous allons choisir l'option 4 qui nous permet de créer une sous-clé RSA uniquement pour la signature.

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits

Nous laissons le choix par défaut pour la longueur de la clé (2048 bits)

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

Choisir une date d'expiration
Je choisi le choix par défaut (n'expire jamais)

Really create? (y/N) y

Je confirme la création

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

pub  2048R/0D6FE738  created: 2015-09-11  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/1C51C149  created: 2015-09-11  expires: never       usage: E
sub  2048R/219FE958  created: 2015-09-11  expires: never       usage: S
[ultimate] (1). Jean Michel A Peu Près <jm@apeupres.fr>

gpg>

Et voilà, je me trouve en possession d'une paire de clé maitresse 0D6FE738 (privée/publique) pour la certification ou la signature de clé publique chacune ayant deux sous-clés, une pour le chiffrement de documents 1C51C149 (usage: E) et une pour la signature de documents 219FE958 (usage: S).

Une fois les modifications terminées, quitter le mode interactif en sauvegardant.

gpg> save

Révoquer une sous-clé

(A faire uniquement dans le cas où la ou les sous-clés sont compromises)

Il faut éditer la clé maitresse correspondante.

# gpg --edit-key 0D6FE738
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

pub  2048R/0D6FE738  created: 2015-09-11  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/1C51C149  created: 2015-09-11  expires: never       usage: E
[ultimate] (1). Jean Michel A Peu Près <jm@apeupres.fr>

gpg>

Sélectionner la sous-clé correspondante à l'aide de la commande key suivi du numéro d'index de la sous-clé.

Pour sélectionner la sous-clé 1 (index)

gpg> key 1

Même commande pour la désélectionner

gpg> key 1

L'index des clé/sous-clés commence à 0 (zéro).
Dans mon exemple, ma clé maitresse 0D6FE738 correspond à l'index 0, la sous-clé 1C51C149 à l'index 1 etc etc...

Pour info On sait qu'une sous-clé est sélectionnée quand un asterisk est présent sur la ligne (à coté de sub)

gpg> key 1

pub  2048R/0D6FE738  created: 2015-09-11  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
sub* 2048R/1C51C149  created: 2015-09-11  expires: never       usage: E
sub  2048R/219FE958  created: 2015-09-11  expires: never       usage: S
[ultimate] (1). Jean Michel A Peu Près <jm@apeupres.fr>

Je sélectionne ma sous-clé 1C51C149 index 1 (un asterisk m'indique la clé sélectionnée)

gpg> revkey
Do you really want to revoke this subkey? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
Your decision? 0
Enter an optional description; end it with an empty line:
>
Reason for revocation: No reason specified

J'exécute la commande revkey et je choisi l'option 0 (aucune raison spécifique)
Saisir une description optionnelle

(No description given)
Is this okay? (y/N) y

Valider

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 0D6FE738, created 2015-09-11

Saisir la phrase de passe de la clé privée.

gpg: gpg-agent is not available in this session

pub  2048R/0D6FE738  created: 2015-09-11  expires: never       usage: SC
                     trust: ultimate      validity: ultimate
This key was revoked on 2015-09-11 by RSA key 0D6FE738 Jean Michel A Peu Près <jm@apeupres.fr>
sub  2048R/1C51C149  created: 2015-09-11  revoked: 2015-09-11  usage: E
sub  2048R/219FE958  created: 2015-09-11  expires: never       usage: S
[ultimate] (1). Jean Michel A Peu Près <jm@apeupres.fr>

gpg>

L'indication revoked apparait bien sur la ligne de ma sous-clé.

Une fois les modifications terminées, quitter le mode interactif en sauvegardant.

gpg> save

Pour annuler les modifications

gpg> quit

Ne pas sauvegarder les modifications sinon vous allez révoquer la sous-clé et on ne pourra plus l'utiliser par la suite.

Etiquettes: 

Créer le certificat de révocation de la clé maitresse

En effet, les clés et sous-clés ne doivent jamais être supprimées mais révoquées.
Cela permet de continuer à déchiffrer les anciens documents et vérifier les anciennes signatures.
La révocation se fait via le mode interactif et la commande revkey
Mais pour cela, il faut obligatoirement disposer de la paire de clé maitresse et de la phrase de passe.

Attention En cas de perte de la clé maitresse ou la perte de la phrase de passe, il n'est plus possible de faire quoi que ce soit, il est donc impératif de créer le certificat de révocation de notre clé maitresse afin de la révoquer et pour pouvoir en créer une nouvelle. C'est une question de sécurité nationnale ;-)

# gpg --output revoke.asc --gen-revoke 0D6FE738

sec  2048R/0D6FE738 2015-09-11 Jean Michel A Peu Près <jm@apeupres.fr>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 0

Choisir le motif de la révocation

Enter an optional description; end it with an empty line:
>

Saisir une description optionnelle

Reason for revocation: No reason specified
(No description given)
Is this okay? (y/N) y

Valider avec y pour YES

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 0D6FE738, created 2015-09-11

Saisir la phrase de passe

gpg: gpg-agent is not available in this session
ASCII armored output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

Attention Ce certificat de révocation doit être stocké en lieu sûr et surtout pas avec la clé maitresse.

En cas de guerre nucléaire ;-) pour révoquer la clé maitresse:

# gpg --import revoke.asc

Attention Aucune confirmation demandée. La clé maitresse est aussitôt révoquée.

Etiquettes: 

Exporter (sauvegarder) les clés dans des fichiers

# gpg --armor --export 0D6FE738 > clepub.asc
# gpg --armor --export-secret-keys 0D6FE738 > clesec.asc
# gpg --armor --export-secret-subkeys 0D6FE738 > subcle.asc
  1. Les clés et sous-clés publiques sont exportées dans le fichier clepub.asc
  2. La clé privée maitresse est exportée dans le fichier clesec.asc
  3. Les sous-clés privées sont exportées dans le fichier subcle.asc

Attention Seul le fichier clepub.asc contenant la clé publique peut être communiqué.

Pour éviter toute erreur de manipulation, sauvegarder également le dossier complet ~/.gnupg.

Info Enregistrer ces trois fichiers, ainsi que le fichier contenant la clé de révocation de la clé maitresse (revoke.asc) dans une archive TAR.
Chiffrer le fichier archive TAR avec la commande openssl (on est quand même jamais trop prudent)

# openssl enc -aes256 -in monFichier.tar -out monFichier.tar.aes256

Supprimer les 4 fichiers .asc ainsi que l'archive TAR. Le fichier clepub.asc peut-être conservé.
Conserver uniquement le fichier chiffré et l'enfermer à double tour dans un coffre fort sur Neptune.
Sans blague, il ne faut surtout pas le perdre.

Etiquettes: 

Supprimer la clé privée maitresse du trousseau

Pourquoi supprimer (ou plutôt enlever) la clé privée maitresse de notre trousseau ?
Pour plus de sécurité.

Nous n'avons pas besoin de la clé privée maitresse pour signer et déchiffrer des documents.
Seules les sous-clés privées sont nécessaires.

Dans le cas où vous ne souhaitez pas faire d'autres opérations nécessitant la clé privée maitresse (signatures de clés publiques, ajout de sous-clés, révocation de sous-clés etc etc...), autant la supprimer du trousseau et la conserver à l'abris des regards indiscrets.

Attention Attention, avant de procéder à la suppression de la clé privée du trousseau, sauvegarder toutes les clés dans des fichiers comme indiqué ici Exporter (sauvegarder) les clés dans des fichiers

On affiche nos clés privées.

# gpg --list-secret-keys
/root/.gnupg/secring.gpg
------------------------
sec   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
ssb   2048R/1C51C149 2015-09-11
ssb   2048R/219FE958 2015-09-11

Nous avons bien dans notre trousseau un clé privée maitresse 0D6FE738 et deux sous-clés privées 1C51C149 & 219FE958.
On supprime notre clé privée maitresse.

# gpg --delete-secret-keys 0D6FE738
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

sec  2048R/0D6FE738 2015-09-11 Jean Michel A Peu Près <jm@apeupres.fr>

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

On répond "y" aux deux questions.

On affiche à nouveau nos clés privées

# gpg --list-secret-keys

La commande ne retourne plus rien.
A ce stade, nous ne pouvons plus rien faire (ni signer, ni déchiffrer des documents) puisqu'en supprimant la clé privée maitresse, toutes les sous-clés privées ont été également supprimées.
Nous allons donc importer uniquement nos sous-clés privées (vous les avez normalement sauvegardées ;-).

# gpg --import subcle.asc
gpg: key 0D6FE738: secret key imported
gpg: key 0D6FE738: "Jean Michel A Peu Près <jm@apeupres.fr>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

La commande nous indique que la clé privée a bien été importée.
On vérifie en affichant la liste des clés privées.

# gpg --list-secret-keys
/root/.gnupg/secring.gpg
------------------------
sec#  2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
ssb   2048R/1C51C149 2015-09-11
ssb   2048R/219FE958 2015-09-11

Le symbole dièse "#" à coté de "sec" nous indique que la clé privée maitresse est manquante mais que nous avons bien les deux sous-clés privées (ssb).

Si, pour une raison x vous avez besoin de réimporter dans votre trousseau votre clé privée maitresse, procéder de cette manière.

Ouvrer votre coffre-fort et récupérer la sauvegarde de vos clés (Elles sont censées être stockées dans un lieu sûr).
Supprimer les sous-clés privées.

# gpg --delete-secret-keys 0D6FE738
gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

sec  2048R/0D6FE738 2015-09-10 Jean Michel A Peu Près <jm@apeupres.fr>

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

Confirmer la suppression.
Vérifier la liste des clés privées.

# gpg --list-secret-keys

Notre trousseau ne contient plus de clés et sous-clés privées.
On importe notre clé privée maitresse (qui contient également les sous-clé privées).

# gpg --import clesec.asc
gpg: key 0D6FE738: secret key imported
gpg: key 0D6FE738: "Jean Michel A Peu Près <jm@apeupres.fr>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

On affiche nos clés privées importées.

# gpg --list-secret-keys
/root/.gnupg/secring.gpg
------------------------
sec   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
ssb   2048R/1C51C149 2015-09-11
ssb   2048R/219FE958 2015-09-11

Le symbole "#" à coté de "sec" a disparu.
Notre clé privée maitresse a bien été restaurée.

Etiquettes: 

Partager sa clé publique

Plusieurs méthodes sont disponibles pour partager sa clé publique.

  1. La joindre à chaque message envoyé (en pièce jointe d'un mail - clepub.asc).
  2. La mettre à disposition sur un serveur HTTP (le fichier clepub.asc)
  3. La publier sur un serveur de clé.

Pour la troisième méthode

# gpg --send-keys 0D6FE738
gpg: sending key CEA84BBA to hkp server keys.gnupg.net
Etiquettes: 

Importer la clé publique d'un utilisateur

Si vous avez sa clé publique dans un fichier

# gpg --import cle_publique_user_a.asc

En effectuant une recherche sur un serveur de clé

Par exemple, pour communiquer de manière chiffrée avec le président des Etats-Unis (trop la classe)

# gpg --fingerprint --search-keys Barack Obama
gpg: searching for "Barack Obama" from hkp server keys.gnupg.net
(1)     Barack Obama <barack.obama@whitehouse.gov>
          2048 bit RSA key CF4C3F70443ED0C5C3EE47555F31A132F752764E, created: 2015-01-02
(2)     barack obama (hej) <hejhejhej@hotmail.com>
          2048 bit RSA key FC54600AB3CB8A408005DCEE9001C823CB3D68F7, created: 2014-10-09
(3)     Barack Hussein Obama <president@whitehouse.gov>
          2048 bit RSA key 4AC1999F0BA293E8960AF2DA428C3085AF19CFE9, created: 2014-05-25, expires: 2018-05-25
(4)     Barack Obama (Barack Obama) <xxgiricsxx@gmail.com>
          1024 bit RSA key 03387652B395C4EC9DDA6AAFC9086C33FAE0BD4A, created: 2014-03-12
(5)     Barack Hussein Obama (PoC) <presidente@casabranca.gov>
          1024 bit RSA key 080C65142395677B22533461601F1B3201206B11, created: 2014-03-12 (revoked)
(6)     Barack Obama (Si quieres pasar un buen rato llamma al timbre de la cas
          3072 bit DSA key 06FE8A6A00EF1482C7AF545A06601E108440BE33, created: 2014-01-10, expires: 2014-01-11 (expired)
(7)     Barack Obama <mmmyom@tormail.org>
          2048 bit RSA key 09D7F58194D51A08F0DC8C6D0B998F9E9C8EFF37, created: 2013-05-28, expires: 2017-05-28
(8)     Barack Obama (Pruba SAD) <shus_presi_EEUU@yopmail.com>
          1024 bit RSA key 128F925760B14B1EBE7DFCD35F25557DE7D21FF0, created: 2012-10-10
(9)     barack obama <bjjbb@ufl.edu>
          2048 bit RSA key 099A86AA0E3383D0396A59E457435C9EE35A5D9F, created: 2011-11-10
(10)    Barack Obama <baracko@whitehouse.com>
          2048 bit RSA key 4E984FCDD33CC9F11B2E774FB9AA7F50D3EF826C, created: 2011-10-20
(11)    Barack Hussein Obama (PoC) <presidente@casabranca.gov>
          1024 bit DSA key DFC2235748027DE8897FF893A69404F176F5FE21, created: 2010-04-07, expires: 2010-04-14 (expired)
Keys 1-11 of 12 for "Barack Obama".  Enter number(s), N)ext, or Q)uit >

Choisir la clé à importer.
L'option --fingerprint permet d'afficher l'empreinte de la clé et de la vérifier si celle-ci nous a été communiquée par le destinataire lui-même. Deux précautions valent mieux qu'une.

Je choisi d'importer la clé (1) - Je valide en appuyant sur Entrée

gpg: requesting key F752764E from hkp server keys.gnupg.net
gpg: key F752764E: public key "Barack Obama <barack.obama@whitehouse.gov>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)

Quand on liste les clés

# gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
sub   2048R/1C51C149 2015-09-11
sub   2048R/219FE958 2015-09-11

...

pub   2048R/F752764E 2015-01-02
uid                  Barack Obama <barack.obama@whitehouse.gov>
sub   2048R/C4CDB034 2015-01-02

On voit que la clé F752764E a bien été importée.
Dorénavant, je suis en mesure d'envoyer des messages chiffrés au président des Etats-Unis Barack Obama (whaoo).

Etiquettes: 

Signatures et chiffrements

A ce stade, nous sommes en mesure de signer et chiffrer tous les documents que nous souhaitons.

Alors, suivez le guide ...

Etiquettes: 

Signatures

Pour info La signature d'un message exige la saisie de la phrase de passe de la clé privée à chaque fois.

Rappel des différentes générations de signatures

"message" correspond à un fichier texte contenant le message à signer.

# cat message
Ceci est un message signé.

# gpg --sign message

Un nouveau fichier message.gpg est généré contenant le message et la signature au format binaire.
Pour lire le contenu du message, il faut obligatoirement utiliser gpg avec l'option --decrypt.
En effet, le message n'est pas chiffré, il est seulement signé, mais étant au format binaire, seule l'option --decrypt est capable de vérifier la signature et d'afficher le message en clair.

# gpg --clearsign message

Un nouveau fichier message.asc est généré contenant le message et la signature au format ASCII.

# gpg --detach-sign message

Un nouveau fichier message.sig est généré contenant uniquement la signature au format binaire.

#  gpg --detach-sign --armor message

Un nouveau fichier message.asc est généré contenant uniquement la signature au format ASCII.

Pour modifier le nom du fichier généré, il faut utiliser l'option -o nomDuFichier (ou --output).

# gpg --detach-sign --armor -o signature.asc message

Il est possible de signer un message sans passer par l'intermédiare de fichiers (entrées/sorties)

# echo "Ceci est un message signé." | gpg --clearsign

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 219FE958, created 2015-09-11 (main key ID 0D6FE738)

gpg: gpg-agent is not available in this session
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ceci est un message signé.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJV9enkAAoJED/atMwhn+lYiz8H+wc1peA+zFNh0pI0Xl9LaCng
2BPlhod4MYO7yAJTKh76MHx1pXZAjPRZdvQf7WockS9gyHC2/h3MGcFMJZ4LAYdQ
16LfwCC2ip08FsiWuIjnAdC+qEcvLVM6MkH5iiKVgLRnwn7b7oqp9M/mwQGb8JmA
gxRwMTDZxxSBHjjIKPtbOzzN/Wp308WQiI1G0d/uM+GE8JmOQcGAz2pNS/i01KbR
7ft0RUGOtdlnZOyBpuTl90CEkEhFjICvoyB3EEoXAR+DorxhgRUGeGliZzgoKSeU
cWsGlMaigvofC67xSaWacIpk/GOv0hZUPMR9EqmKuCDqSHpcrnWX3W2JWYqwkZM=
=XBKV
-----END PGP SIGNATURE-----

Copier/coller le message signé dans un mail au format texte (tout le contenu de -----BEGIN PGP SIGNED MESSAGE----- jusqu'à -----END PGP SIGNATURE-----).

Pour info Pour envoyer la sortie vers un fichier, utiliser l'option -o monFichier (--output).

Exemple d'un fichier signé puis altéré volontairement afin de simuler un piratage.

On signe le fichier message

# gpg --armor --detach-sign message

On vérifie la signature du fichier message.
L'option --verify prend en premier paramètre le nom du fichier contenant la signature (si la signature est dans un fichier à part) et le nom du fichier signé en second paramètre.

# gpg --verify message.asc message
gpg: Signature made Sun 13 Sep 2015 11:40:49 PM CEST using RSA key ID 219FE958
gpg: Good signature from "Jean Michel A Peu Près <jm@apeupres.fr>"

La signature est correcte.
On altère le fichier message pour simuler un piratage du fichier.

# echo "" >> message

On vérifie à nouveau la signature du fichier.

# gpg --verify message.asc message
gpg: Signature made Wed 16 Sep 2015 07:14:28 PM CEST using RSA key ID 219FE958
gpg: BAD signature from "Jean Michel A Peu Près <jm@apeupres.fr>"

Le résultat nous indique que la signature est incorrecte.
Nous avons la preuve que le fichier d'origine a été altéré.

Franchement, c'est quand même pas si compliqué de signer un fichier et c'est quand même plus sécurisant.

Et si maintenant on le chiffrait, ce fichier ...

Etiquettes: 

Chiffrements

Chiffrer un document n'est pas plus compliqué que de le signer.
Il faut juste avoir la clé publique du ou des destinataires du fichier. (ou avec sa propre clé publique si vous voulez seulement chiffrer des fichiers personnels)

Première question: A qui puis-je envoyer des messages/fichiers chiffrés ?
Pour le savoir, il faut lister toutes les clés publiques de notre trousseau.

# gpg --list-public-keys
/root/.gnupg/pubring.gpg
------------------------
pub   2048R/0D6FE738 2015-09-11
uid                  Jean Michel A Peu Près <jm@apeupres.fr>
sub   2048R/1C51C149 2015-09-11
sub   2048R/219FE958 2015-09-11

pub   2048R/F752764E 2015-01-02
uid                  Barack Obama <barack.obama@whitehouse.gov>
sub   2048R/C4CDB034 2015-01-02

Je vois que je peux chiffrer des documents pour moi-même et pour Barack Obama (oh la vache, j'avais oublié que j'avais importé sa clé publique).

Allez, c'est parti, envoyons un message chiffré à Barack.

# echo "Hello Barack, how are you ?" | gpg --encrypt --armor --recipient barack.obama@whitehouse.gov
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 1f, 0u
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

hQEMAxK9BLnEzbA0AQf8CLkg04MMJMxCrUiwHzcgaqL94gavPLBP/GIQP5bnxJFE
ujBziWFTwb9OxyKv6MWs9cegB1/qNKnYdgIWpDwjMWv+G/8wJfDqif002rEYPE+B
rRsnTXBQM+7QHXwATGpxudP85cY+WGp+cEb6PjkGvDzcBuTdlj2eQ8hm01TOKlpz
CDvbgZ58v4vz2W229fHDOD1sZ8WhYv5Q9+vX1mcTODC2terKtE+ekRrpfOjUE0Kf
U+tdUIqMrop63oWJmQgclejevfzmLGvhSaZPlZy8HlWUNfgPCxspJIu4+r89EJST
364F2n/cMig5f+o1dYWHRO+aY6OapatE/zxEul33G9JXAWqnqZQ8XsA9p3H0bGe3
vAFAAKvG3C0RUgvbwJaG2yzX5GYbunyek/f/B7LdugM+r+D8qo+3Ts61ZvjkunhO
kv34a7xaYuA4ckA3VZZgJ9/DQgoqBHO7
=QP5y
-----END PGP MESSAGE-----

Pour chiffrer un message, il suffit d'utiliser l'option --encrypt et l'option --recipient suivie de l'adresse mail du destinataire. (il est possible d'indiquer plusieurs fois l'option --recipient si il y a plusieurs destinataires)

Pour info Si vous n'avez pas signé la clé publique de chiffrement du destinataire, un message vous demandera si vous souhaitez vraiment utiliser la clé correspondante à l'adresse mail indiquée.
Pour signer une clé, il suffit de l'éditer gpg --edit-key xxxxxx, une fois dans le mode interactif de gpg, sélectionner la clé servant au chiffrement (drapeau E) avec la commande key suivi de l'index de la clé et la signer avec la commande sign. Tout est indiqué ici "Editer une clé".

Il reste à copier tout le contenu du message chiffré (de -----BEGIN PGP MESSAGE----- jusqu'à -----END PGP MESSAGE-----) et le coller dans un mail au format texte.

L'option -o (--output) peut être utilisée pour enregistrer le contenu du message chiffré dans un fichier.
Sans l'option --armor, le contenu sera chiffré au format binaire.

Pour chiffrer un fichier, il suffit d'indiquer son nom

# gpg --encrypt --armor --recipient barack.obama@whitehouse.gov message

Avec l'option --armor, un fichier .asc est généré (ASCII).
Sans l'option --armor, un fichier .gpg est généré (binaire).

Pour info En combinant les options de signatures, il est possible de signer et chiffrer un document.

# echo "Hello Barack, how are you ?" | gpg --sign --encrypt --armor --recipient barack.obama@whitehouse.gov

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 219FE958, created 2015-09-11 (main key ID 0D6FE738)

gpg: gpg-agent is not available in this session
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

hQEMAxK9BLnEzbA0AQf8CFu2Jkc3wXtpg/H8eJ+jH5mf/pcm2NDUV17q8D7zohTf
sr7lsYW6IqWD5WtTd0hA/LVPHEl6UI4uXdzskNOdam+AJUUYqRDhXIos4mWof9Q/
e4hM1mz01tMSCIqmvIHvmYTRQOqu80wv2az7WJk7D7uxqVRfktA4dumVGwn3aIIc
RQ8chuHWIEUIteUJixsiYUIZj8jlQsvsZ9JMQmA3ugf3agOpNKcEwVyxhFhHw77q
oyogcVwfsI5mbKX6oZH93tsHG4gjUn/iiRXoWOsQuxzoqc7q90ndZHXRbQlsRZKw
qw4t9lsUg/NVsMGDDBxO4GpKgoQzoj27uqigV01q69LAyAFiR2YdUVtbqnNQ/HOJ
EcplnhCSmQNiHth5MY2yYFs9C7v1+HxItbUkufLIqv1qSHev6WmTaK9Ro+RkNVDQ
xwYwmyOZEcLpONRF6qWLkKk+PpctiSjSCd6lg7EvvEnPh9Lkz+n6fDgOIVCilKUX
IXn1l+JZX4e9Flan7KonyC7GEMJAUFedzxxVAKgcL0omFKqQq7iqRcBwXeTEZXd3
s1qYv1aQMN90sWfBamI3i9T8rtMyVABjEb+Mj1Qdp/vf+uNmuo14WZOyJT8zklnH
M+o+eG48dOSeg52FPUSw0L0BucqdXRu9llNWUiXYz2UnBe3KURMnpyMYrl7p2swm
AmAZTJ9zYfEAZkSzkArrdq2DcWYYW4CRtCLGojjxyDkN9AfZ79oDOl2qyy7xOlJ5
VY/HSX/gfMgYRKXR8do+fSW3cbm8u5j7cO2IdDzsahxZGdpPClFN3Mwjy6Zr59M+
tqlxKE+6Cyc1a44CeOUp6j2i01zqgDqG5Ru3CiPWTjk7GPP56/ELGdaz
=UKz1
-----END PGP MESSAGE-----

Ce message étant signé et chiffré, il est ultra sécurisé.
Difficle de faire mieux.


Pour info Vous avez un fichier contenant une multitude de "utilisateurs/mots de passe".
Chiffrer le avec GPG et supprimer le fichier d'origine.
Et quand vous avez besoin d'un mot de passe, déchiffrer le fichier avec l'option --decrypt et votre phrase de passe.

# cat motsdepasse
aaa fghdfkgjhdfgjkhdfgk
bbb mljfklsjgksjghskgjh
ccc sjhgjhkfgjkfhgjkfhg
# gpg --encrypt --recipient jm@apeupres.fr motsdepasse
# rm motsdepasse
# gpg --decrypt motsdepasse.gpg

You need a passphrase to unlock the secret key for
user: "Jean Michel A Peu Près <jm@apeupres.fr>"
2048-bit RSA key, ID 1C51C149, created 2015-09-11 (main key ID 0D6FE738)

gpg: gpg-agent is not available in this session
gpg: encrypted with 2048-bit RSA key, ID 1C51C149, created 2015-09-11
      "Jean Michel A Peu Près <jm@apeupres.fr>"
aaa fghdfkgjhdfgjkhdfgk
bbb mljfklsjgksjghskgjh
ccc sjhgjhkfgjkfhgjkfhg

Vous remarquerez que le fichier est chiffré avec ma propre clé publique, et que pour le déchiffrer j'ai besoin de ma clé privée et de ma phrase de passe.

Etiquettes: 

Scripts

Quelques scripts pour bien utiliser Linux

Bonnes pratiques pour écrire un script

Dans Linux, un script est un fichier rassemblant une succession de commandes.
Toutes les commandes peuvent être écrites les unes à la suite des autres.
Elles sont exécutées séquentiellement par le shell.

Pour une bonne exécution du script, il est toutefois conseillé de le structurer correctement pour éviter les mauvaises surprises.

Personnellement, je respecte toujours les règles suivantes :

1 - Toujours nommer son script avec l'extension ".sh" (facilite la recherche)

2 - La première ligne d'un script doit toujours indiquer quel interpréteur de commande utiliser, par exemple :
#!/bin/bash
#!/bin/sh
#!/usr/bin/python
#!/usr/bin/php

3 - Renseigner la variable PATH :
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

La variable PATH permet d'indiquer au shell les répertoires où sont situés les commandes externes utilisées.
En console, la variable PATH est initialisée à la connexion ce qui fait qu'il n'est pas nécessaire d'indiquer le chemin complet d'une commande afin de l'exécuter. Si le script est exécuté uniquement en console, il n'est pas nécessaire d'initialiser la variable PATH mais si le script est exécuté via une tâche cron, la variable PATH est inconnue, dans ce cas, toutes les commandes externes utilisées dans le script ne pourront pas être exécutées.

4 - Rediriger la sortie d'erreur standard dans un fichier de log spécifique, par exemple :
exec 2>>/var/log/mon_script_error.log
exec 2>/dev/null (s'il n'est pas nécessaire de "logguer" les différentes erreurs rencontrées)

5 - Terminer le script avec la commande suivante :
exit 0

Exemple avec un script basique :

$ cat mon_script.sh
#!/bin/bash
 
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 
exec 2>/dev/null
 
...
mes commandes
...
 
exit 0

Créer une sauvegarde différentielle

Pour effectuer une sauvegarde différentielle, il faut utiliser le programme rdiff-backup.

Pour l'installer :

$ sudo apt-get install rdiff-backup

Pour créer une sauvegarde différentielle :

Créer un fichier, par exemple sauvegarde_differentielle.sh et y inscrire les deux lignes suivantes

$ cat sauvegarde_differentielle.sh
#!/bin/sh
 
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 
exec 1>>/var/log/sauvegarde_differentielle.log
exec 2>>/var/log/sauvegarde_differentielle_error.log
 
nice -n 19 rdiff-backup /repertoire_a_sauvegarder /destination_de_la_sauvegarde && nice -n 19 rdiff-backup --remove-older-than 1W --force /destination_de_la_sauvegarde
 
exit 0
 
$

Rendre le scripts exécutable

  • nice -n 19 : exécute le programme avec une priorité basse pour éviter que le processeur soit utilisé à 100%
  • rdiff-backup --remove-older-than 1W --force : force la suppression des sauvegardes de plus d'une semaine (1W)

Voir également cette rubrique pour plus de renseignements

Mise à jour des paquets

  • Créer un fichier maj.sh

Ecrire dans le fichier la ligne suivante

 

$ cat maj.sh
#!/bin/sh
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
sudo apt-get update && sudo apt-get upgrade
exit 0

Pour exécuter ce script, taper dans la console

$ $MAJ


Une autre méthode, plus simple, consiste à créer un alias dans le fichier ~/.bashrc.

$ echo "alias maj='sudo apt-get update && sudo apt-get upgrade'" >> ~/.bashrc

Prendre en compte le nouvel alias créé :

$ . ~/.bashrc

Pour l'exécution :

$ maj

Etiquettes: 

Redémarrer un processus automatiquement

Pour surveiller un processus et le redémarrer en cas de plantage, créer une tâche cron exécutée en tant que root avec la commande suivante :

Par exemple, pour le processus DOVECOT (serveur POP / IMAP)

$ pgrep dovecot > /dev/null || { /etc/init.d/dovecot restart ; }

Etiquettes: 

Sauvegarder la liste des sources et des paquets

$ cat sauvegarde.sh
#!/bin/bash
 
####### sauvegarde - Sauvegarde le sources.list et les paquets #######
 
echo
echo "Script de sauvegarde APT'"
echo
 
DATE=`date +%F`
 
if test -d $DATE
    then echo "Le dossier \"$DATE\" existe déjà. Voulez-vous refaire la sauvegarde ? [o/n]"
    read choix
    
    if [ $choix = "n" ]
    then
        echo
        echo "Abandon."
        exit 0
    elif [ $choix = "o" ]
    then
        echo
        echo "Réécriture de la sauvegarde $DATE..."
    else
        echo "Veuillez taper \"o\" ou \"n\"."
        exit 2
    fi
else
    mkdir $DATE
fi
 
( cp /etc/apt/sources.list ./$DATE/sources.$DATE.list && dpkg --get-selections > ./$DATE/paquets.$DATE.list ) \
&& echo "Sauvegarde terminée dans $PWD/$DATE" \
|| echo "Sauvegarde échouée..."
 
exit 0

Etiquettes: 

Restaurer la sauvegarde des sources et des paquets

$ cat restauration.sh
#!/bin/bash
 
####### restauration - Script de restauration APT #######
 
echo
echo "Script de restauration APT"
echo "Une connexion Internet fonctionnelle est requise pour le bon fonctionnement de ce script."
echo
 
##### DEBUT GESTION DES ERREURS #####
 
ls -1 | grep '^[[:digit:]]\{4\}\-[[:digit:]]\{2\}\-[[:digit:]]\{2\}$' > /dev/null
 
if test $? -ne 0
then
    echo "Il n'y a rien à restaurer !"
    exit 2
elif [ ! `whoami` = "root" ]
then
    echo "Il faut être root ... Executez \"sudo !!\"."
    exit 2
elif test $# -ne 1
then
    echo "Il faut un seul paramètre. Tapez \"./restauration sources\" pour restaurer le sources.list ou \"./restauration paquets\" pour restaurer les paquets."
    exit 2
fi
 
##### FIN GESTION DES ERREURS #####
 
##### DEBUT SELECTION D'UNE DATE #####
 
echo
echo "Par défaut le script restaure votre sauvegarde la plus récente. Voulez-vous en restaurer une autre ? [o/n]"
read choix
 
if [ $choix = "n" ] # Si il veut la plus récente, on prend la plus récente en fonction du nom des dossiers de sauvegarde
then
    DATE=`ls -1 | grep '^[[:digit:]]\{4\}\-[[:digit:]]\{2\}\-[[:digit:]]\{2\}$' | tail -n 1`
    echo "La date choisie est $DATE."
elif [ $choix = "o" ] # Sinon on lui demande de rentrer une date
then
    echo "Veuillez entrer la date souhaitée, sous la forme AAAA-MM-JJ : "
    read DATE
    
    if test -d $DATE
    then
        echo "La date choisie est $DATE."
    else
        echo "La date demandée n'existe pas dans les sauvegardes !"
        exit 2
    fi
else
    echo "Veuillez entrer \"o\" ou \"n\"."
    exit 2
fi
 
echo
echo "Est-ce bien ce que vous voulez ? [o/n]" # Confirmation
read choix2
 
if [ $choix2 = "o" ]
then
    echo "Continuons donc !"
    sleep 2 # On dort 2 secondes pour la lisiblité
elif [ $choix2 = "n" ]
then
    echo "Abandon."
    exit 2
else
    echo "Veuillez entrer \"o\" ou \"n\"."
    exit 2
fi
 
echo
 
##### FIN SELECTION D'UNE DATE #####
 
##### DEBUT RESTAURATION #####
 
if test $1 = "sources" # Si on veut restaurer le sources.list
then
    cp /etc/apt/sources.list /etc/apt/sources.list.svgorig.$DATE
    ( cp $DATE/sources.$DATE.list /etc/apt/sources.list && apt-get update ) \
    && (echo ; echo "Restauration du sources.list terminée avec succès.") \
    || (echo ; echo "Restauration du sources.list échouée... Vous n'avez probablement pas les droits d'écriture dans /etc/apt/" ; \
    cp /etc/apt.sources.list.svgorig.$DATE /etc/apt/sources.list && echo "Votre sources.list original a été restauré." ; exit 2)
    
elif test $1 = "paquets" # Si on veut restaurer les paquets
then
    ( dpkg --set-selections < $DATE/paquets.$DATE.list && apt-get update ; apt-get dselect-upgrade ) \
    && (echo ; echo "Restauration des paquets terminée avec succès") \
    || (echo ; echo "Restauration des paquets échouée... Votre connexion Internet est peut-être défaillante." ; exit 2)
    
else
    echo "Paramètre inconnu"
    exit 2
fi
 
exit 0
 
##### FIN RESTAURATION #####

Etiquettes: 

"Explode" version "Bash"

Détail du script

$ nl explode.sh
     1  #!/bin/bash
 
     2  chemin="chemin 1|chemin 2 chemin 2 et demi|chemin 3"
 
     3  #Sauvegarde de la valeur du IFS
     4  #La variable $IFS doit être obligatoirement entre guillemets
     5  old="$IFS"
 
     6  #Modification de la valeur du IFS avec le "|"
     7  IFS="|"
 
     8  #Creation du tableau
     9  tab=( $chemin )
 
    10  #Restauration de la valeur du IFS
    11  #La variable $old doit être obligatoirement entre guillemets
    12  IFS="$old"
 
    13  #Parcourt du tableau
    14  for (( i=0 ; i<${#tab[*]} ; i++ )) ; do
    15          echo $i" -> "${tab[$i]}
    16  done
 
    17  exit 0
$

Résultat :

$ ./explode.sh
0 -> chemin 1
1 -> chemin 2 chemin 2 et demi
2 -> chemin 3
$

Afficher le détail des caractères d'une chaine de texte ou d'un fichier

Le script suivant permet d'afficher, par caractères, le nombre de fois qu'il est utilisé.

$ cat compteCaracteres.awk
BEGIN {
        RS = "\n"
}
 
{
        split($0, tab, "")
        for (var in tab) {
                tab2[tab[var]]+=1
        }
}
 
END {
        for (var in tab2) {
                printf "%1s --> %6d\n" ,  var , tab2[var]
        }
        exit 0
 
}

Exemple :

$ echo "ceci est un test" | awk -f compteCaracteres.awk
u -->      1
i -->      1
n -->      1
  -->      3
c -->      2
e -->      3
s -->      2
t -->      3

On peut également effectuer un tri sur le résultat :

$ echo "ceci est un test" | awk -f compteCaracteres.awk | sort
  -->      3
c -->      2
e -->      3
i -->      1
n -->      1
s -->      2
t -->      3
u -->      1

En utilisant un fichier en entrée :

$ cat file
TARGUANT
LOGOS
SOCRATISERONS
TEMPORISERENT
PLASTIQUAIENT
CORROBORER
BOTTELES
LIGOTA
SATINERONT
HYPNOTISERA

Affichage du résultat avec un tri décroissant sur le nombre de fois que le caractère est utilisé

$ cat file | awk -f compteCaracteres.awk | sort -b -n -k 3 -r
T -->     13
O -->     12
R -->     11
E -->     10
S -->      9
A -->      8
N -->      7
I -->      7
L -->      4
P -->      3
G -->      3
U -->      2
C -->      2
B -->      2
Y -->      1
Q -->      1
M -->      1
H -->      1

Etiquettes: 

Afficher toutes les interfaces réseau et leur adresse IP v4 correspondante

Voici un script qui permet d'afficher les adresses IP (v4) de toutes les interfaces réseau installées sur la machine.

Exécution :

./getInetAddr.sh 
eth0       ==>     10.33.43.10
lo         ==>       127.0.0.1
wlan0      ==>     172.22.8.35

Ce script est disponible en téléchargement ici .

Egalement disponible via GIT :

$ git clone http://git.quennec.fr/ronan/scripts_pub.git

Détail du script commenté :

#!/bin/bash
 
# Ce script retourne l'adresse IP
# de chaque interface réseau trouvée
 
# Pour le debuggage
#set -x
 
# Initialisation de la variable PATH
PATH="/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
 
# Nom du script
BASE=`basename $0`
# Le répertoire du script
DIR=`dirname $0`
 
# On boucle sur toutes les différentes interfaces trouvées
for I in `\
# On exécute la commande ifconfig
ifconfig | \
# On récupère le nom des différentes interfaces
# Le nom des interfaces se trouve dans la première
# colonne des informations retournées par la commande ifconfig
cut -d' ' -f1 | \
# On supprime toutes les lignes vides
sed -e '/^$/d'`; do
# On affecte à la variable J l'adresse IP correspondante
# à l'interface en cours de traitement par la boucle I
J=$(\
# On exécute la commande ifconfig pour l\'interface
# en cours de traitement par la boucle I
ifconfig $I | \
# On récupère la partie contenant l\'adresse IP
grep -E -o 'inet add?r:([0-9]{1,3}\.){3}[0-9]{1,3}' | \
# On concerve uniquement la partie IP
cut -d':' -f2 \
# On affiche le résultat
# sous la forme nomInterface ==> adresseIp
) && printf "%-10s ==> %15s\n" $I $J
done
 
# On quitte le script
exit 0
Etiquettes: 

Anagrammes

Voici un script qui permet de trouver les anagrammes d'un mot ou d'une suite de lettres.

Ce script utilise un dictionnaire de plus de 336500 mots.

Ce dictionnaire de mots peut être téléchargé ICI.

Il est également disponible ICI au format UTF-8.

Ce script s'utilise de cette manière :

anagramme.sh -f fichierDictionnaire [-d] -l nbLettre -c listeLettres

L'option -f permet d'indiquer le fichier "dictionnaire" à utiliser.

L'option -l permet d'indiquer le nombre de lettres des anagrammes à rechercher.

L'option -c permet d'indiquer le mot ou les lettres des anagrammes à rechercher.

L'option -d permet de ne pas prendre en compte les caractères accentués.

Cliquez ICI pour télécharger le script ou via mon GitLab.

Exécution du script :

# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -l 6 -c aspire
Liste des mots de 6 lettre(s) et contenant les lettres "aspire" :
1 - aspire
2 - paires
3 - paries
4 - parsie
5 - repais

# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -l 8 -c fuaaieujoslw
Liste des mots de 8 lettre(s) et contenant les lettres "fuaaieujoslw" :
1 - jalousai
2 - jalousie

Avec l'option -d :

# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -d -l 6 -c aspire
Liste des mots de 6 lettre(s) et contenant les lettres "aspire" :
1 - aspire
2 - aspire
3 - epairs
4 - paires
5 - paries
6 - paries
7 - parsie
8 - repais

Ci-dessous le détail du script avec un maximum de commentaires :

# nl scripts/anagramme.sh
     1  #!/bin/bash

     2  # Activation du debug
     3  # Décommenter pour activer
     4  #set -x

     5  # Fonction permettant de tester l'existence des commandes passées en argument
     6  function preRequis {
     7          for arg in $@; do
     8                  if ! which $arg >/dev/null; then
     9                          logger -t $0 "La commande $arg n'est pas installée"
    10                          echo "La commande $arg n'est pas installée !!!"
    11                          echo "Fin du script."
    12                          exit 1
    13                  fi
    14          done
    15  }

    16  # Journalisation de l'exécution du script
    17  logger -t $0 "Exécution du script"

    18  # Initialisation de la variable PATH
    19  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

    20  # Exécution de la fonction preRequis
    21  preRequis grep sed awk

    22  # Vérification du nombre d'arguments passés au script
    23  #if [[ ! $# -eq 6 ]]; then
    24  if [[ $# -lt 6 || $# -gt 7 ]]; then
    25          echo "Nombre d'arguments incorrect"
    26          echo "Utilisation : $0 -f fichierDictionnaire [-d] -l nbLettre -c listeLettres"
    27          exit 1
    28  fi

    29  # Suppression des caractères accentués (0 = NON / 1 = OUI)
    30  supprAccents=0

    31  # Validation des arguments passés au script
    32  while getopts ":f:c:l:d" option; do
    33          case $option in
    34                  f)
    35                          if [ -f "$OPTARG" ]; then
    36                                  LISTE="$OPTARG"
    37                          else
    38                                  echo "L'option -f requiert un fichier existant."
    39                                  exit 1
    40                          fi
    41                          ;;
    42                  c)
    43                          listeDesLettres="$OPTARG"
    44                          if ! grep -E -q -i "^[a-z]*$" <<< "$listeDesLettres"; then
    45                                  echo "L'option -c requiert les lettres [a-z]."
    46                                  exit 1
    47                          fi
    48                          ;;
    49                  l)
    50                          nbLettre="$OPTARG"
    51                          if ! grep -E -q "^[0-9]*$" <<< "$nbLettre"; then
    52                                  echo "L'option -l requiert une valeur numérique."
    53                                  exit 1
    54                          fi
    55                          ;;
    56                  d)
    57                          supprAccents=1
    58                          ;;
    59                  :)
    60                          echo "L'option $OPTARG requiert un argument."
    61                          exit 1
    62                          ;;
    63                  \?)
    64                          echo "$OPTARG : option invalide."
    65                          exit 1
    66                          ;;
    67          esac
    68  done

    69  # Initialisation des variables utilisées pour le script
    70  listeDesLettres2="$listeDesLettres"
    71  listeDesMots2=""
    72  ind=1

    73  # Réécriture de la liste des lettres en incluant un pipe entre chaque lettre pour l'utiliser avec la commande grep
    74  # abcdef -> a|b|c|d|e|f|
    75  listeDesLettres=$(sed -r 's/([a-zA-Z])/\1\|/g' <<< "$listeDesLettres")

    76  # Recherche tous les mots contenant le nombre et les lettres indiqués
    77  if [[ $supprAccents = "0" ]]; then
    78          listeDesMots=$(cat "$LISTE" | grep -E -e "^.{$nbLettre}$" | grep -E -i "^[$listeDesLettres]+$")
    79  else
    80          listeDesMots=$(cat "$LISTE" | sed 'y/àâäéèêëîïôöùûüç/aaaeeeeiioouuuc/' | grep -E -e "^.{$nbLettre}$" | grep -E -i "^[$listeDesLettres]+$")
    81  fi

    82  # On parcourt tous les mots trouvés par la commande précédente.
    83  # Chaque mot et la liste des lettres sont passés à la commande AWK.
    84  # AWK initialise un tableau avec la liste des lettres (avec la commande split).
    85  # Chaque lettre est remplacée dans le mot par un blanc avec la commande sub ...
    86  # ... et le résultat renvoyé par la commande (1 en cas de succès et 0 en cas d'échec) ...
    87  # ... est multiplié à la variable "a". Pour finir, la commande AWK retourne le résultat de la variable "a".
    88  # Si la commande AWK retourne "1" (signifiant une erreur en BASH - D'où le "!" après le "if" )
    89  # c'est que toutes les lettres du mot correspondent à la liste des lettres à chercher.
    90  # On sauvegarde donc le mot en cours dans la variable "listeDesMots2".
    91  while read mot; do
    92  if ! echo "$mot|$listeDesLettres2" | awk -F'|' 'BEGIN{a=1;b=1}{split($1,tab,"");for(var in tab){b=sub(tab[var],"",$2);a*=b}}END{exit a}'; then
    93          listeDesMots2="$listeDesMots2\n$ind - $mot"
    94          ind=$(expr $ind + 1)
    95  fi
    96  done <<< "$listeDesMots"

    97  # Affichage de la liste des mots trouvés
    98  echo -n "Liste des mots de $nbLettre lettre(s) et contenant les lettres \"$listeDesLettres2\" :"
    99  echo -e "$listeDesMots2"

   100  # Journalisation de la fin du script
   101  logger -t $0 "Fin d'exécution du script"

   102  # Fin du script
   103  exit 0

 

Etiquettes: 

BASH : Parcourir tous les caractères d'une chaine

$ cat parcourtCaracteres
#!/usr/bin/env bash

STR="foobar"
for L in $(seq 1 ${#STR}); do
    echo $(echo $STR | cut -c$L)
done

 

La commande ${#STR} permet de connaitre la longueur de la chaine de caractères $STR.
Voir  pour plus d'explications.

Etiquettes: 

Calculer le PGCD et le PPCM d'une série de nombre

Voici un script qui permet de calculer le PGCD et le PPCM d'au moins deux nombres.

Il s'utilise de cette manière :

$ ./pgcd.sh 50 68 54 etc etc ...

Voici le contenu du script :

nl scripts/pgcd.sh

     1  #!/bin/bash
 
     2  # Ce script permet de calculer le PGCD et le PPCM
     3  # d'au moins deux nombres passés en argument.
     4  #
     5  # Il utilise quelques commandes intéressantes.
     6  # Elles sont toutes expliquées en détail sur www.quennec.fr
 
     7  # Fonction permettant de tester si les arguments
     8  # passés au script sont bien numériques et supérieur à 0
     9  function isNumeric() {
    10          for arg in $@; do
    11                  if ! expr "$arg" : '-\{0,1\}[0-9]\{1,\}$' >/dev/null; then
    12                          echo "ERREUR !!! $arg n'est pas un nombre."
    13                          exit 1
    14                  else
    15                          if expr "$arg" = 0 >/dev/null; then
    16                                  echo "ERREUR !!! Saisir un nombre superieur a 0"
    17                                  exit 1
    18                          fi
    19                  fi
    20          done
    21  }
 
    22  # On initialise la variable PATH
    23  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
 
    24  # On test si le nombre d'arguments passés
    25  # au script est bien supérieur ou égal à 2
    26  if [ $# -lt 2 ]; then
    27          echo "Usage: `basename $0` premier_nombre deuxieme-nombre etc etc ..."
    28          exit 1
    29  fi
 
    30  # On exécute la fonction isNumeric en lui passant en argument
    31  # tous les arguments passés au script
    32  isNumeric $@
 
    33  # Fonction permettant de calculer le PGCD
    34  # Cette fonction, je l'ai trouvé sur le site :
    36  function pgcd() {
    37          dividende=$1
    38          diviseur=$2
    39          reste=1
    40          until [ "$reste" -eq 0 ]; do
    41                  let "reste=$dividende%$diviseur"
    42                  dividende=$diviseur
    43                  diviseur=$reste
    44          done
    45  }
 
    46  NBR1=$1
    47  PPCM=$1
    48  VAR="($1"
 
    49  # On décale les arguments du scripts vers la gauche
    50  shift
 
    51  # On calcul le PPCM et le PGDC
    52  # de tous les nombres passés au script
    53  until [ "$#" -eq 0 ]; do
    54          NBR2=$1
    55          VAR="$VAR,$1"
    56          shift
    57          pgcd $PPCM $NBR2
    58          let "PPCM=$PPCM*$NBR2/$dividende"
    59          pgcd $NBR1 $NBR2
    60          NBR1=$dividende
    61  done
 
    62  VAR="$VAR)"
 
    63  # On affiche le résultat
    64  echo; echo "PGCD $VAR = $dividende"; echo
 
    65  echo "PPCM $VAR = $PPCM"; echo
 
    66  exit 0
 
Pour le télécharger, cliquer ici ou via mon GitLab.
Ou alors en ligne de commande :
$ wget -O pgcd.sh.tar http://www.isbeta.fr/83640

 

Chiffrer automatiquement un fichier avec mcrypt

Tous les détails de la commande mcrypt

La commande mcrypt propose différentes solutions afin de l'utiliser via un script.

Soit en utilisant un fichier de configuration, soit en utilisant des variables d'environnement.

Il est possible d'utiliser un fichier de configuration qui sera utilisé par défaut par la commande mcrypt.
Ce fichier doit être obligatoirement nommé ".mcryptrc" et placé dans le home de l'utilisateur.
Il devra contenir 3 paramètres :
- l'algorithme à utiliser
- le mode d'encryptage
- la phrase de passe

$ ls -l .mcryptrc
-rw------- 1 root root 46 31 oct.  09:09 .mcryptrc
$ cat .mcryptrc
algorithm des
mode cbc
key ma_phrase_de_passe
$

Lors de l'utilisation de la commande mcrypt, les paramètres d'encryptage seront automatiquement récupérés.

$ mcrypt monFichier
Warning: It is insecure to specify keywords in the command line
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 root root  0 31 oct.  08:32 monFichier
-rw------- 1 root root 84 31 oct.  08:32 monFichier.nc
$

Il est également possible d'utiliser un fichier de configuration nommé différemment mais dans ce cas, il faudra le spécifier lors de l'utilisation de la commande mcrypt avec l'option -c.

$ ls -l my_config_file
-rw------- 1 root root 46 31 oct.  09:15 my_config_file
$ cat my_config_file
algorithm des
mode cbc
key ma_phrase_de_passe
$ mcrypt -c my_config_file monFichier
Warning: It is insecure to specify keywords in the command line
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 root root  0 31 oct.  08:32 monFichier
-rw------- 1 root root 84 31 oct.  08:32 monFichier.nc
$

Il est possible égalment d'utiliser des variables d'environnement qu'il faudra initialiser et exporter directement dans le script utilisant la commande mcrypt.

Ces variables sont :

MCRYPT_KEY:la phrase de passe
MCRYPT_ALGO:l'algorithme à utiliser
MCRYPT_MODE:le mode d'encryptage

Exemple d'un script :

$ cat mon_script.sh
#!/bin/bash
 
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 
export MCRYPT_KEY="la phrase de passe"
export MCRYPT_ALGO="DES"
export MCRYPT_MODE="CBC"
 
if [[ -e monFichier.nc ]]; then
        rm monFichier.nc
fi
 
mcrypt monFichier
 
exit 0
$

Dans ce script, les variables propres à mcrypt sont initialisées et exportées.
Un test sur l'existence du fichier crypté permet de le supprimer avant d'exécuter la commande mcrypt (cela évite d'avoir un message de confirmation de la commande mcrypt pour écraser le fichier)
Enfin, la commande mcrypt est exécutée sur le fichier "monFichier".

Exécution du script :

$ ./mon_script.sh
File monFichier was encrypted.
$

Ce script peut être utilisé pour envoyer un fichier sur un serveur FTP par exemple sans compromettre les données du fichier.

Etiquettes: 

Convertion OGG to MP3

Pré-requis : Installer les paquets suivants

$ apt-get install ffmpeg
$ apt-get install ubuntu-restricted-extras
$ apt-get install lame


Exécuter le script suivant :

$ cat convert.sh
#!/bin/bash
for i in *.ogg
do
     j=${i%.ogg} ffmpeg -ab 192k -i "$j.ogg" "$j.mp3"
done  
exit 0

Convertit tous les fichiers présents dans le dossier où le script est exécuté

Etiquettes: 

Découper un mot en syllabes

Voici un script qui permet de découper un ou plusieurs mots en syllabes.

Merci à http://www.bertrandboutin.ca/Folder_151_Grammaire/P_b_division.htm pour la définition des syllabes.

Ce script peut être téléchargé ICI. ou via mon GitLab

Il s'utilise de cette manière :

$ ./syllabes.sh mot1 mot2 mot3 mot4 etc etc ...

Le script accepte au minimum un mot.

Voici le détail du script avec un maximum de commentaires :

$ cat syllabes.sh
#!/bin/bash

# Activer pour le debug
#set -x

function preRequis {
        for arg in $@; do
                if ! which $arg >/dev/null; then
                        echo "La commande $arg n'est pas installée !!!"
                        echo "Fin du script."
                        exit 1
                fi
        done
}

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

preRequis egrep sed

# Si le nombre d'arguments passé au script est inférieur à 1
# On quitte le script immédiatement.
if [ $# -lt 1 ]; then
        echo "Usage: `basename $0` mot1 [mot2 mot3 ...]"
        exit 1
fi

# Liste des voyelles
VOY="aeiouyàâäéèêëîïôöùûü"
# Liste des consonnes
CONS="zrtpqsdfghjklmwxcvbnç"
# Liste des groupes de lettres insécables
GR="bl cl fl gl pl br cr dr fr gr pr tr vr ch ph gn th"

for MOT in $@; do

        # On convertit toutes les majuscules en minuscules
        _MOT=$(tr "[A-Z]" "[a-z]" <<< "$MOT")

        # On exécute la première règle de découpe des syllabes
        # avec les groupes de lettres insécables.
        for _GR in $(echo $GR); do
                _MOT=$(sed "s/$_GR/-$_GR/g" <<< "$_MOT")
        done

        # Boucle infinie
        while :; do

                # Une consonne placée entre deux voyelles introduit une nouvelle syllabe.
                if SYL=$(grep -E -o "[$VOY]{1}[$CONS]{1}[$VOY]{1}" <<< "$_MOT"); then
                        for __SYL in $SYL; do
                                _SYL=$(sed -r "s/^(.)(.{2})$/\1-\2/" <<< "$__SYL")
                                _MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
                        done
                        continue
                fi

                # Deux consonnes placées entre deux voyelles,
                # la première appartient à la syllabe précédente,
                # la seconde, à la syllabe suivante.
                if SYL=$(grep -E -o "[$VOY]{1}[$CONS]{2}[$VOY]{1}" <<< "$_MOT"); then
                        for __SYL in $SYL; do
                                _SYL=$(sed -r "s/^(.{2})(.{2})$/\1-\2/" <<< "$__SYL")
                                _MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
                        done
                        continue
                fi

                # Quand il y a trois consonnes ou voyelles consécutives à l’intérieur d’un mot,
                # ordinairement les deux premières terminent une syllabe,
                # l’autre commence une nouvelle syllabe.
                if SYL=$(grep -E -o "[$CONS]{3}|[$VOY]{3}" <<< "$_MOT"); then
                        for __SYL in $SYL; do
                                _SYL=$(sed -r "s/^(.{2})(.)$/\1-\2/" <<< "$__SYL")
                                _MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
                        done
                        continue
                fi

                # On quitte la boucle infinie si aucune des conditions précédentes n'est vraie
                break

        done

        # On affiche le résultat
        _MOT=$(sed "s/^-//" <<< "$_MOT")
        echo -e "$MOT => $_MOT"

done

exit 0

Enregistrer les erreurs d'exécution d'une commande ou d'un script dans un fichier log avec horodatage

Enregistrer les erreurs d'exécution d'une commande ou d'un script dans un fichier de log peut se faire tout simplement en redirigeant la sortie d'erreur standard (descripteur #2) vers un fichier :

$ cp fichierA fichierB 2>>/var/log/cp_error.log

Petit rappel :
2>/mon_fichier : Tout le contenu du fichier est écrasé
2>>/mon_fichier : Les données sont ajoutées à la fin du fichier

La consultation du fichier log :

$ cat /var/log/cp_error.log
cp: impossible d'évaluer « fichierA »: Aucun fichier ou dossier de ce type
$

C'est clair et précis. L'erreur rencontrée par la commande cp a bien été enregistrée dans le fichier.
Mais la précision pourrait être un peu plus détaillée surtout si cela concerne une commande ou un script exécuté régulièrement par une tâche cron.

Le but, obtenir un fichier de log avec la date et l'heure de l'erreur rencontrée :

$ cat /var/log/cp_error.log
ven. 24 août 2012 15:10:01 CEST --- cp: impossible d'évaluer « fichierA »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier1 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier2 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier3 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier4 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier5 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier6 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier7 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier8 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier9 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier10 »: Aucun fichier ou dossier de ce type
$

C'est quand même mieux de connaitre la date et l'heure de l'erreur rencontrée.

La manière de le faire ? Avec un simple script awk.

Pré-requis :

Avoir gawk installé sur la machine.

$ dpkg -l | grep -i awk
ii  gawk                                                   1:3.1.7.dfsg-5               GNU awk, a pattern scanning and processing language
ii  mawk                                                   1.3.3-15                     a pattern scanning and text processing language
$

S'il n'est pas installé :

$ apt-get install gawk

Détail du script awk :

$ cat log.awk
{
        date_heure=strftime("%c",systime())
        printf ("%s --- %s\n", date_heure, $0)
}
 
END {
     exit 0
}
$

Que fait-il :

Ce script reçoit une donnée en entrée récupérée grâce à la variable $0, initialise une variable date_heure avec la date et l'heure courante grâce à la commande systime() et formatée grâce à la commande strftime puis envoi le tout sur sa sortie standard grâce à la fonction printf.

Comment l'utiliser :

Avec une commande shell :

$ cp fichierA fichierB 2>&1 >&- | /usr/bin/awk -f /root/log.awk >> /var/log/cp_error.log

La commande cp tente de copier le fichierA vers le fichierB.
La sortie d'erreur standard de la commande cp est renvoyée en premier vers la sortie standard.
La sortie standard est ensuite fermée ce qui fait que seules les erreurs sont envoyées sur la sortie standard de la commande.
La sortie standard de la commande cp est ensuite envoyée au script log.awk.
Enfin, la sortie standard du script log.awk est envoyée vers le fichier de log souhaité.

L'utilisation avec un script est aussi possible :

$ /root/mon_script.sh 2>&1 >&- | /usr/bin/awk -f /root/log.awk >> /var/log/mon_fichier.log

Si l'on souhaite envoyer la totalité des résultats d'une commande ou d'un script dans le fichier de log, il suffit de supprimer la fermeture du descripteur #1.

$ cp fichierA fichierB 2>&1 | /usr/bin/awk -f /root/log.awk >> /var/log/cp_error.log

Etiquettes: 

Rechercher les doublons d'un ou plusieurs fichiers

Il n'est pas rare d'avoir sur son disque dur le même fichier présent à plusieurs endroits.

Comme des photos, par exemple ou des programmes.

Que de place perdue pour pas grand chose.

Voici un petit script qui scrute le répertoire, passé en argument, à la recherche de fichiers en double (voir plus).

Comme dans la plupart de mes scripts, vous y retrouverez la fonction "preRequis" permettant de savoir si toutes les commandes utilisées dans le script sont biens installées sur le système et la commande logger permettant de journaliser l'exécution du script dans /var/log/syslog.

Téléchargé le script ici.

Le script s'utilise de cette manière :

$ ./rechercheDoublons.sh mon_repertoire

J'utilise la commande find avec l'option -type f qui permet de rechercher tous les fichiers réguliers dans le répertoire passé en argument.
Pour chaque fichier retourné par la commande find, la commande md5sum lit le fichier en mode binaire (avec l'option -b) et écrit l'empreinte md5 du fichier dans le fichier /tmp/filesMd5sum.
Pour finir, le contenu du fichier /tmp/filesMd5sum est trié avec la commande sort, et tous les fichiers ayant la même empreinte md5 (grâce aux commandes awk '{print $1}' | uniq -d) sont affichés à l'écran.

# cat rechercheDoublons.sh
#!/bin/bash

function preRequis {
        for arg in $@; do
                if ! which $arg >/dev/null; then
                        logger -t $0 La commande $arg n\'est pas installée
                        echo "La commande $arg n'est pas installée !!!"
                        echo "Fin du script."
                        exit 1
                fi
        done
}

logger -t $0 Exécution du script

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

preRequis md5sum sort awk uniq grep cut find

if [ ! $# -eq 1 ]; then
        echo "Erreur !!!"
        echo "Utilisation : $0 répertoire"
        exit 1
fi

FILE="/tmp/filesMd5sum"
REP="$1"

if [ ! -d "$REP" ]; then
        echo "Erreur !!!"
        echo "$REP n'est pas un répertoire."
        exit 1
fi

rm -f "$FILE"

find "$REP" -type f -exec md5sum -b {} \; > "$FILE"

DOUBLON=0

for file in $(cat "$FILE" | sort | awk '{print $1}' | uniq -d); do
        DOUBLON=1
        echo "Empreinte MD5 identique : $file"
        grep $file "$FILE" | cut -d' ' -f2
done

if [ "$DOUBLON" -eq 0 ]; then echo "Aucun doublon trouvé"; fi

logger -t $0 Fin d\'exécution du script

exit 0

 

Script d'archivage incrémental et transfert sftp automatique

CPIO

Pré-requis pour que le script suivant fonctionne :

1 - Paramétrer sur le poste à sauvegarder un accès ssh au serveur de sauvegarde via un système d'authentification par clé privée / publique.

Tout est expliqué ici : http://quennec.fr/linux/utilisation/connexion-a-une-machine-distante-sans-saisir-le-mot-de-passe

2 - Le programme bzip2 doit être installé sur le poste à sauvegarder.

$ apt-get install bzip2

3 - Rendre les scripts exécutables

$ chmod +x uploadBackup.sh && chmod +x fonctions.inc.sh

Script d'archivage incrémental et transfert sftp automatique

Ce script permet d'effectuer une sauvegarde incrémentale d'un répertoire vers un serveur de sauvegarde.
Il utilise un système de niveau correspondant au jour du mois - 1.
Tous les 1er de chaque mois, une sauvegarde totale (niveau 0) est effectuée.
Une sauvegarde incrémentale est effectuée les jours suivants.

Le script est composé de 2 fichiers.
Un fichier comportant le script principal (uploadBackup.sh) et un fichier comportant les fonctions personnalisées utiles au script principal (fonctions.inc.sh).

Les fichiers à sauvegarder (variable DATADIR) sont enregistrés dans une archive CPIO compressée avec BZIP2 et stockée dans un répertoire local sur le poste à sauvegarder (variable ARCHIVEDIR).
Avant chaque sauvegarde, un fichier de niveau est créé dans le répertoire local.
Toutes les logs sont enregistrées dans un fichier stocké également dans le répertoire local.
La sauvegarde incrémentale utilise le fichier niveau précédent afin de sauvegarder tous les fichiers modifiés depuis la sauvegarde précédente grâce à la commande find et l'option -newer.
Enfin, l'archive CPIO est envoyée sur le serveur de sauvegarde (variable serveur_sauvegarde) via SFTP et stockée dans des sous-répertoires correspondant à l'année et au mois de la sauvegarde dans le dossier de sauvegarde (variable dossier_distant) .

Détail du fichier uploadBackup.sh

$ nl uploadBackup.sh
     1  #!/bin/bash
     2  # set -x
     3  # Répertoire des scripts shell
     4  SCRIPTDIR="/root/script_archivage_incremental"
     5  # Répertoire des fichiers à sauvegarder
     6  DATADIR="/root/dossier_a_sauvegarder"
     7  # Répertoire local des archives
     8  ARCHIVEDIR="/root/local_backup"
     9  # Inclure les fonctions
    10  . $SCRIPTDIR/fonctions.inc.sh
    11  # Fichier de log
    12  LOG=$ARCHIVEDIR/`getDate`.log
    13  # Redirection de toutes les sorties du script vers le fichier de log
    14  exec 1>$LOG 2>&1
    15  # Déterminer le niveau de sauvegarde
    16  # Le 1er du mois => niveau 0
    17  jourDuMois=`getDayForCalcul`
    18  ((niveau=$jourDuMois-1))
    19  case $niveau in
    20          0) # Sauvegarde totale
    21                  # Nettoyage du répertoire d'archive
    22                  rm -i $ARCHIVEDIR/*.bz2 $ARCHIVEDIR/niveau*
    23                  # Création du fichier de niveau (niveau 0)
    24                  touch $ARCHIVEDIR/niveau0
    25                  archive="$ARCHIVEDIR/`getDate`_niveau0.cpio"
    26                  find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    27                  ;;
    28          *)
    29                  # Creation du fichier de niveau
    30                  touch $ARCHIVEDIR/niveau$niveau
    31                  archive="$ARCHIVEDIR/`getDate`_niveau${niveau}.cpio"
    32                  # Determination du niveau precedent
    33                  ((niveauPrec=$niveau-1))
    34                  # Test si le fichier de niveau precedent existe
    35                  if [[ ! -f $ARCHIVEDIR/niveau$niveauPrec ]]
    36                  then
    37                          # Si il n'existe pas, sauvegarde integrale du repertoire
    38                          echo "Fichier de niveau $niveauPrec inexistant"
    39                          echo "Execution d'une sauvegarde integrale en cours de mois"
    40                          find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
    41                  else
    42                          # Sauvegarde incrementale
    43                          find $DATADIR -newer $ARCHIVEDIR/niveau$niveauPrec | cpio -ocv | bzip2 -c > $archive.bz2
    44                  fi
    45                  ;;
    46  esac
    47  # Vérification de la validité de l'archive
    48  if isArchiveInvalide $archive.bz2 ; then
    49          echo "Archive $archive.bz2 INVALIDE - Fichier non transfere"
    50          exit 1
    51  fi
    52  # Transfert du fichier vers le serveur de sauvegarde
    53  if transfert ${archive}.bz2 ; then
    54          echo "Transfert realise avec succes"
    55          exit 0
    56  fi
    57  # Si le transfert a echoue
    58  echo "Echec de transfert"
    59  exit 1
$

Détail du fichier fonctions.inc.sh

$ nl fonctions.inc.sh
     1  #!/bin/bash
     2  function transfert {
     3          typeset mois
     4          typeset annee
     5          # Recuperation de la valeur du premier argument passe a la fonction
     6          # Recuperation du nom de l'archive a envoyer au serveur de sauvegarde
     7          typeset ficATransferer=$1
     8          # Adresse du serveur de sauvegarde
     9          typeset serveur_sauvegarde="192.168.1.113"
    10          # Compte utilise pour se connecter au serveur de sauvegarde
    11          typeset user="root"
    12          # Chemin absolu du dossier ou sera stocke la sauvegarde
    13          typeset dossier_distant="/root/sauvegarde_dossier"
    14          mois=`getMonth`
    15          annee=`getYear`
    16          # Test si le dossier de sauvegarde existe sur le serveur de sauvegarde
    17          # Connexion au serveur avec le user root
    18          ssh $user@$serveur_sauvegarde test -d $dossier_distant/$annee/$mois
    19          # Test sur le code retour de la commande precedente
    20          case $? in
    21                  0)
    22                          ;;
    23                  255)
    24                          echo "Echec de la commande SSH"
    25                          return 1
    26                          ;;
    27                  *)
    28                          # Si code retour different de 0 et 255
    29                          # Creation du repertoire de la sauvegarde
    30                          ssh $user@$serveur_sauvegarde mkdir -p $dossier_distant/$annee/$mois
    31                          ;;
    32          esac
    33          # Connexion au serveur de sauvegarde en FTP sécurisé
    34          # Ne pas oublier les doubles chevrons << avant le mot cle FIN
    35          # Ne pas mettre d'espace entre << et FIN
    36          sftp -b - $user@$serveur_sauvegarde <<FIN
    37          # Positionnement dans le repertoire de la sauvegarde
    38          cd $dossier_distant/$annee/$mois
    39          pwd
    40          # Envoi de la sauvegarde
    41          put $ficATransferer
    42  FIN
    43  # Ne pas mettre de tabulation ou d'espace devant le mot clé FIN
    44  # Sinon celui-ci n'est pas reconnu
    45          # Test sur le code retour de la commande SFTP
    46          # Si le code retour est different de 0
    47          # Une anomalie a ete rencontree
    48          (( $? != 0 )) && return 1
    49          # Tester si archive valide sur serveur de sauvegarde
    50          ficSurMachineCible=$(basename $ficATransferer)
    51          ssh $user@$serveur_sauvegarde bzip2 -t $dossier_distant/$annee/$mois/$ficSurMachineCible
    52          case $? in
    53                  0)
    54                          ;;
    55                  255)
    56                          echo "Echec de la commande SSH"
    57                          return 1
    58                          ;;
    59                  *)
    60                          # Si code retour different de 0 et 255
    61                          # Alors l'archive est invalide
    62                          echo "Archive $dossier_distant/$annee/$mois/$ficSurMachineCible INVALIDE"
    63                          return 1
    64                          ;;
    65          esac
    66          return 0
    67  }
    68  function isArchiveInvalide {
    69          typeset archive=$1
    70          bzip2 -t $archive 2>/dev/null && return 1
    71          return 0
    72  }
    73  function getDate {
    74          date '+%Y_%m_%d'
    75  }
    76  function getYear {
    77          date '+%Y'
    78  }
    79  function getMonth {
    80          date '+%m'
    81  }
    82  function getDayForCalcul {
    83          date '+%e' | sed 's/ //'
    84  }
$

Une fonction permettant de vérifier si les commandes utilisées dans le script sont installées sur le système

Quand on développe des scripts pour soi-même, toutes les commandes utilisées dans le script sont obligatoirement installées sur le système. Mais qu'en est-il si le script est partagé avec différentes personnes sur différents sytèmes ?

Il faut être sûr et certain que le script sera utilisable n'importe où et par n'importe qui.

C'est pour cette raison que j'utilise de plus en plus cette fonction dans mes scripts.

function preRequis {
        for arg in $@; do
                if ! which $arg >/dev/null; then
                        echo "La commande $arg n'est pas installée !!!"
                        echo "Fin du script."
                        exit 1
                fi
        done
}

Cette fonction, je l'ai appelé "preRequis". Elle reçoit des noms de commandes en arguments et vérifie à l'aide de la commande which (commande primitive du shell) l'existence sur le système de ces commandes.

Grâce à cette fonction, si une personne exécute un script contenant une commande qui n'est pas installée sur son système, un message explicite sera affiché à l'écran.

Cette fonction peut être intégrée dans n'importe quel script.

$ cat sauvegarde_differentielle.sh
#!/bin/bash
function preRequis {
        for arg in $@; do
                if ! which $arg >/dev/null; then
                        echo "La commande $arg n'est pas installée !!!"
                        echo "Fin du script."
                        exit 1
                fi
        done
}

preRequis nice rdiff-backup
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
exec 1>>/var/log/sauvegarde_differentielle.log
exec 2>>/var/log/sauvegarde_differentielle_error.log
nice -n 19 rdiff-backup /repertoire_a_sauvegarder /destination_de_la_sauvegarde && nice -n 19 rdiff-backup --remove-older-than 1W --force /destination_de_la_sauvegarde
exit 0
$

Dans cet exemple, j'utilise la fonction preRequis pour vérifier que les commandes nice et rdiff-backup sont bien installées sur le système.

Vérifier régulièrement le contenu d'une page web et recevoir un avertissement par mail

Voici un script qui se charge de vérifier les différences qu'il peut y avoir sur une page web entre 2 périodes de temps définies.

$ nl check_url.sh
     1  #!/bin/bash
 
     2  export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
     3  URL="http://quennec.fr"
     4  PREC="/root/precedent"
     5  SUIV="/root/suivant"
 
     6  exec 1>/dev/null 2>&1
 
     7  if [ -e $SUIV ]; then
     8          rm $SUIV
     9  fi
 
    10  curl $URL > $SUIV
 
    11  if [ -e $PREC ]; then
 
    12          diff $SUIV $PREC
    13          if [ $? == 1 ]; then
    14                  mail -s "Ecart sur la page $URL" ronan@quennec.fr<<EOF
    15  EOF
    16          fi
 
    17  fi
 
    18  if [ -e $PREC ]; then
    19          rm $PREC
    20  fi
 
    21  mv $SUIV $PREC
 
    22  exit 0
$

Lignes 2 à 5 : initialisation des variables utilisées dans le script.
Ligne 6 : Redirection des entrées et sorties standards.
Ligne 7 à 9 : Suppression du fichier $SUIV si celui-ci existe.
Ligne 10 : Téléchargement du contenu de la page web et enregistrement dans le fichier $SUIV
Ligne 11 à 17 : Si le fichier $PREC existe, le programme diff compare les 2 fichiers $SUIV et $PREC. Si il y a des différences ($? == 1), un mail est envoyé à l'adresse indiquée.
Ligne 18 à 20 : Si le fichier $PREC existe, il est supprimé.
Ligne 21 : Le fichier $SUIV est renommé en $PREC.

Rendre le script exécutable :

$ chmod +x check_url.sh

Reste à exécuter le script

$ ./check_url.sh

Pour l'automatisation, une ligne sera ajoutée à crontab :

$ crontab -e
*/5     *       *       *       *       /root/check_url.sh

Le script sera exécuté toutes les 5 minutes.

Fichier attachéTaille
Plain text icon check_url.sh_.txt460 octets
Etiquettes: 

Utilisation

Accès à distance

Pour accéder à distance au bureau Ubuntu ou d'une autre ditribution Linux même si la session est verouillée, il faut utiliser vnc4server

Procédure à suivre :

 

Paramétrage coté serveur

1 - Installer vnc4server

$ sudo apt-get install vnc4server

2 - Création du répertoire .vnc dans le home

$ cd ~
$ mkdir .vnc

3 - Création du fichier xstartup dans le répertoire .vnc avec des droits d'exécution

$ touch .vnc/xstartup
$ chmod a+x .vnc/xstartup

4 - Edition du fichier xstartup

$ gedit ~/.vnc/xstartup

5 - Y inscrire ce contenu

#!/bin/sh
# Uncomment the following two lines for normal desktop:
unset SESSION_MANAGER
exec sh /etc/X11/xinit/xinitrc
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
#twm &
gnome-session &

Action coté client

  1. Se connecter à la machine serveur via ssh en utilisant putty par exemple (pour Windows)
  2. S'identifier puis taper la commande vncserver
  3. La commande retourne à l'écran l'adresse à utiliser pour s'y connecter (nom_de_la_machine:1 par exemple)
  4. Se connecter sur l'adresse indiquée via vnc_viewer par exemple (pour Windows)

Il est possible, également, d'utiliser sous GNU/Linux le programme xrdp qui émule parfaitement le protocole RDP (Remote Desktop Protocol Server).

$ apt-get install xrdp

Pour la connexion, n'importe quel client de connexions bureau à distance compatible RDP peut être utiliser.
Le très célèbre mstsc de Windows, mRemoteNG, Remmina (installé par défaut dans Ubuntu depuis la 12.04) etc etc...

Etiquettes: 

Activer le verrouillage numérique au démarrage d'Ubuntu

Par défaut, le verrouillage du pavé numérique n'est pas activé.

Pour le faire, installer le paquet numlockx

$ sudo apt-get install numlockx

Ensuite, éditer le fichier /etc/gdm/Init/Default

$ sudo gedit /etc/gdm/Init/Default

Ajouter les lignes suivantes à la fin du fichier avant la ligne "exit 0"

if [ -x /usr/bin/numlockx ]; then
    exec /usr/bin/numlockx on
fi
Etiquettes: 

Afficher les adresses IP (triées numériquement et numérotées) bloquées par iptables

L'enchainement des commandes suivantes permet d'afficher à l'écran toutes les adresses ip bloquées par iptables, triées numériquement et  numérotées :

$ iptables -L -n | grep 'DROP' | awk '{print $4}' | sort -n | uniq | nl
     1  37.59.20.164
     2  61.38.252.112
     3  85.118.62.43
     4  91.236.74.176
     5  91.236.74.249
     6  93.182.139.42
     7  93.182.147.141
     8  93.182.174.159
     9  93.182.175.25
    10  117.21.225.66
    11  173.242.126.130
    12  199.15.234.138
    13  199.15.234.143
    14  199.15.234.145
    15  199.15.234.146
    16  199.15.234.147
    17  199.15.234.213
    18  208.177.72.206
$

Explications :

La commande suivante permet d'afficher les règles iptables (option -L) avec les ip au format numérique (option -n) :

$ iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  173.242.126.130      0.0.0.0/0
DROP       all  --  199.15.234.146       0.0.0.0/0
DROP       all  --  93.182.139.42        0.0.0.0/0
DROP       all  --  93.182.175.25        0.0.0.0/0
DROP       all  --  93.182.174.159       0.0.0.0/0
DROP       all  --  93.182.147.141       0.0.0.0/0
DROP       all  --  208.177.72.206       0.0.0.0/0
DROP       all  --  199.15.234.138       0.0.0.0/0
DROP       all  --  91.236.74.176        0.0.0.0/0
DROP       all  --  85.118.62.43         0.0.0.0/0
DROP       all  --  91.236.74.249        0.0.0.0/0
DROP       all  --  37.59.20.164         0.0.0.0/0
DROP       all  --  117.21.225.66        0.0.0.0/0
DROP       all  --  199.15.234.143       0.0.0.0/0
DROP       all  --  199.15.234.145       0.0.0.0/0
DROP       all  --  199.15.234.147       0.0.0.0/0
DROP       all  --  61.38.252.112        0.0.0.0/0
DROP       all  --  199.15.234.213       0.0.0.0/0
 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Suivi de la commande suivante, seules les chaines DROP sont affichées :

$ iptables -L -n | grep 'DROP'
DROP       all  --  173.242.126.130      0.0.0.0/0
DROP       all  --  199.15.234.146       0.0.0.0/0
DROP       all  --  93.182.139.42        0.0.0.0/0
DROP       all  --  93.182.175.25        0.0.0.0/0
DROP       all  --  93.182.174.159       0.0.0.0/0
DROP       all  --  93.182.147.141       0.0.0.0/0
DROP       all  --  208.177.72.206       0.0.0.0/0
DROP       all  --  199.15.234.138       0.0.0.0/0
DROP       all  --  91.236.74.176        0.0.0.0/0
DROP       all  --  85.118.62.43         0.0.0.0/0
DROP       all  --  91.236.74.249        0.0.0.0/0
DROP       all  --  37.59.20.164         0.0.0.0/0
DROP       all  --  117.21.225.66        0.0.0.0/0
DROP       all  --  199.15.234.143       0.0.0.0/0
DROP       all  --  199.15.234.145       0.0.0.0/0
DROP       all  --  199.15.234.147       0.0.0.0/0
DROP       all  --  61.38.252.112        0.0.0.0/0
DROP       all  --  199.15.234.213       0.0.0.0/0

Suivi de la commande suivante, seule la 4ème colonne (celle contenant les ip) est affichées :

$ iptables -L -n | grep 'DROP' | awk '{print $4}'
173.242.126.130
199.15.234.146
93.182.139.42
93.182.175.25
93.182.174.159
93.182.147.141
208.177.72.206
199.15.234.138
91.236.74.176
85.118.62.43
91.236.74.249
37.59.20.164
117.21.225.66
199.15.234.143
199.15.234.145
199.15.234.147
61.38.252.112
199.15.234.213

Suivi de la commande suivante (sort), les ip sont triées numériquement (option -n) et les doublons sont exclus avec la commande uniq:

$ iptables -L -n | grep 'DROP' | awk '{print $4}' | sort -n | uniq
37.59.20.164
61.38.252.112
85.118.62.43
91.236.74.176
91.236.74.249
93.182.139.42
93.182.147.141
93.182.174.159
93.182.175.25
117.21.225.66
173.242.126.130
199.15.234.138
199.15.234.143
199.15.234.145
199.15.234.146
199.15.234.147
199.15.234.213
208.177.72.206

Enfin, la commande suivante numérote toutes les lignes affichées :

$ iptables -L -n | grep 'DROP' | awk '{print $4}' | sort -n | uniq | nl
     1  37.59.20.164
     2  61.38.252.112
     3  85.118.62.43
     4  91.236.74.176
     5  91.236.74.249
     6  93.182.139.42
     7  93.182.147.141
     8  93.182.174.159
     9  93.182.175.25
    10  117.21.225.66
    11  173.242.126.130
    12  199.15.234.138
    13  199.15.234.143
    14  199.15.234.145
    15  199.15.234.146
    16  199.15.234.147
    17  199.15.234.213
    18  208.177.72.206

Au total, ce sont donc 5 commandes qui sont utilisées pour afficher le résultat souhaité.

Si la liste est trop longue pour être affichée en une seule fois, une petite commande supplémentaire fera très bien l'affaire :

$ iptables -L -n | grep 'DROP' | awk '{print $4}' | sort -n | uniq | nl | less

Afficher les logs en couleur

Pré-requis :

Installer le programme ccze

$ apt-get install ccze

Utilisation :

$ tail -f /var/log/apache2/access.log | ccze

La commande tail permet d'afficher les 10 dernières lignes d'un fichier.

L'option -f associée à la commande tail permet d'afficher le fichier en continu.

Enfin, l'envoi à la commande ccze via un tube permet la colorisation des données.

Astuce supplémentaire :

Dans les logs du proxy Squid, la date et l'heure sont enregistrés au format timestamp.
Pas très pratique à lire.

Pour convertir le timestamp dans un format plus lisible pour l'homme, il suffit d'utiliser la commande ccze avec l'option -C

$ tail -f /var/log/squid/access.log | ccze -C

Etiquettes: 

Afficher uniquement les adresses IP des logs Apache2

Pour filtrer en continu uniquement les adresses IP des logs Apache, saisir les commandes suivantes :

$ tail -f /var/log/apache2/access.log | egrep -o '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'

ou

$ tail -f /var/log/apache2/access.log | egrep -o '^([0-9]{1,3}\.){3}[0-9]{1,3}'

La commande tail suivie de l'option -f permet d'afficher le contenu d'un fichier en continu.

La commande egrep suivie de l'option -o permet d'afficher uniquement la regex recherchée.

Ajouter une clé au gestionnaire des clés d'APT

Voici les deux manières les plus simple pour ajouter une clé au gestionnaire des clés APT.

Exemple avec la clé webmin.

1 - On télécharge le fichier contenant la clé :

wget http://www.webmin.com/jcameron-key.asc

2 - On ajoute la clé contenu dans le fichier :

sudo apt-key add jcameron-key.asc

Sinon, en envoyant la clé récupérée par la commande wget directement à la commande apt-key en passant par un tube :

wget -O- http://www.webmin.com/jcameron-key.ascsudo apt-key add -

Pour lister toutes les clés installées dans le gestionnaire :

$ sudo apt-key list

Pour supprimer une clé : 

Récupérer l'identifiant public de la clé à supprimer avec l'option list de la commande apt-key :

$ sudo apt-key list

Par exemple, pour la clé webmin, son identifiant public est le "1024D/11F63C51"

On supprime la clé avec la commande suivante :

$ sudo apt-key del 11F63C51

Etiquettes: 

Autologin sur un serveur FTP

Sous Linux, la commande ftp permet de se connecter à un serveur FTP.

Pour le mode passif, il est possible d'utiliser directement la commande pftp.

$ ftp mon-serveur-ftp.com

Les informations de connexions, utilisateur et mot de passe, doivent ensuite être saisis.

Pour éviter ce comportement et permettre l'authentification automatique, il est nécessaire de créer le fichier ~/.netrc avec les informations suivantes:

$ cat ~/.netrc
machine mon-serveur-ftp.com login mon-login password mot-mot-de-passe

Et ne pas oublier de modifier les permissions du fichier comme suit:

$ chmod 600 ~/.netrc

Et voilà, plus besoin de saisir son nom d'utilisateur et son mot de passe

Etiquettes: 

Autologin sur une version serveur (Debian / Ubuntu)

Pour X raisons, il peut être intéressant d'avoir une session automatiquement ouverte au démarrage du serveur.

Pour activer cette option :

1 - Localiser le programme login

# which login
/bin/login

2 - Modifier le fichier /etc/inittab

# vim /etc/inittab

Pour une ouverture de session automatique sur la console tty6 (par exemple), commenter la ligne :
#6:23:respawn:/sbin/getty 38400 tty6
Et ajouter au dessous de cette ligne :
6:23:respawn:/bin/login -f root tty6 </dev/tty6 >/dev/tty6 2>&1

Attention à renseigner correctement le chemin d'accès au programme login (/bin/login) obtenu grâce à la commande `which login` ci-dessus et le numéro de la console à utiliser (tty6).

3 - Sauvegarder les modifications et redémarrer le serveur (pour vérification).
Une session root devrait être automatiquement ouverte sur la console tty6.

Pour rappel, sur une version serveur, il existe 6 consoles (tty1 ... tty6).
Pour basculer d'une console à une autre, il suffit d'utiliser les combinaisons de touches suivantes [ctrl]+[alt]+[F1] pour la console tty1, [ctrl]+[alt]+[F2] pour la console tty2 et ainsi de suite jusqu'à [ctrl]+[alt]+[F6] pour la console tty6.

BASH : exécuter une requête MySql et exploiter le résultat

Il est possible d'exécuter une requête MySql en BASH grâce à la commande echo et au client MySql.

Inconvénient

Mot de passe en clair dans la commande.

Avantage

Automatisation possible avec crontab.

Pré-requis

Un client MySql installé sur la poste

$ apt-get install mysql-client

Au préalable

En fonction de l'inconvénient cité ci-dessus, il est conseillé de créer un utilisateur MySql spécifique pour l'occasion. C'est à dire, un utilisateur ayant uniquement le privilège "SELECT" sur la base à utiliser. Affiner les privilèges sur les champs d'une table de la base pour plus de sécurité.

Pour cela, plusieurs solutions :

  • avec phpmyadmin
  • en ligne de commande

Exemple en ligne de commande :

Dans l'exemple, je souhaite utiliser les champs "champ1", "champ3" et "champ6" de la table "ma_table" de la base "ma_base". Je créé un user "toto" avec le mot de passe "pass" ayant le privilège "SELECT" sur les champs "champ1", "champ3" et "champ6" de la table "ma_table" de la base "ma_base". Pour terminer, je recharge les privilèges.

$ mysql -u root -p
mysql> CREATE USER 'toto'@'%' IDENTIFIED BY 'pass';
Query OK, 0 rows affected (0.00 sec)
 
mysql> GRANT SELECT ( `champ1` , `champ3` , `champ6` ) ON `ma_base`.`ma_table` TO 'toto'@'%';
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH PRIVILEGES ;
Query OK, 0 rows affected (0.00 sec)
 
mysql> Bye
$

Exemple

Dans l'exemple ci-dessous, je vais donc effectuer une requête avec echo et le client MySql sur les champs "champ1", "champ3" et "champ6" de la table "ma_table" de la base "ma_base" sur mon serveur MySql avec l'utilisateur toto, mettre le résultat en forme grâce à awk et à la commande pr et enfin, envoyer le résulat par mail avec la commande mail.

$ echo "select champ1, champ3, champ6 from ma_base.ma_table order by 3 desc;" | mysql -h mon_serveur_mysql -u toto -ppass | awk 'BEGIN{printf("%-15s %-35s %-5s\n","COL1","COL2","COL3")}NR>1{printf("%15d %-35s %5d\n",$1,$2,$3)}' | pr -h "Le resultat de ma requete SQL" | mail -s "Le resultat de ma requete SQL" moi@domaine.fr

Avec la commande awk, j'ajoute des en-têtes de champs personnalisés dans la section BEGIN et je récupère uniquement les données de la requête en supprimant la première ligne qui contient les noms des champs MySql (NR>1) et le tout mis en forme avec la fonction printf.

Pour ne pas afficher la première ligne contenant le nom des champs, il est également possible d'utiliser la commande mysql avec l'option -N.

Avec la commande pr et l'option -h, j'ajoute un titre à mon rapport.

Etiquettes: 

BASH: Horodater le contenu de la commande history

Petite astuce trouvée sur le site http://www.it-connect.fr/ajouter-un-horodatage-a-la-commande-history/

Sous Linux, la commande history permet d'afficher l'historique des commandes utilisées dans le shell.

$ history
  475  java sdz1
  476  ll -tr
  477  cat tokill.java
  478  ll
  479  ll -tr
  480  vim sdz1.class
  481  java sdz1.class
  482  java sdz1
  483  w

Pour ajouter l'horodatage, il suffit d'ajouter cette ligne au fichier .bashrc

$ echo 'export HISTTIMEFORMAT="%F %T : "' >> ~/.bashrc

Résultat, après s'être bien-sûr reconnecté

$ history
  472  2016-11-03 11:51:45 : java sdz1
  473  2016-11-03 11:51:45 : ll -tr
  474  2016-11-03 11:51:45 : cat tokill.java
  475  2016-11-03 11:51:45 : ll
  476  2016-11-03 11:51:45 : ll -tr
  477  2016-11-03 11:51:45 : vim sdz1.class
  478  2016-11-03 11:51:45 : java sdz1.class
  479  2016-11-03 11:51:45 : java sdz1
  480  2016-11-03 11:51:45 : w

A l'aide de la commande grep, il est très facile de retrouver une commande exécutée un jour précis, voir même à une heure précise.

Etiquettes: 

BASH: Quelques fonctions utiles

Voici quelques fonctions bien utiles permettant de manipuler différentes variables et pouvant être inclues dans des scripts.

Le script est disponible ici.

Détail du script

Par exemple, vérifier qu'une variable est numérique, qu'une variable est de type int, qu'une variable est un booléen etc etc...
Ces fonctions (quelques unes) ont été inspirées de PHP.

Ce script peut être inclu dans d'autres scripts de cette manière (l'une ou l'autre des deux méthodes):

source functions.inc
. functions.inc

Le fichier peut également être inclu dans le fichier ~/.bashrc (de la même manière) afin de disposer de toutes ces fonctions directement dans le prompt.

Exemples:

$ is_numeric azerty ; echo $?
1
$ is_numeric ee55ee ; echo $?
1
$ is_numeric 65465484 ; echo $?
0

infoN'hésiter pas à me faire part de vos commentaires.
Penser à vérifier régulièrement si des nouvelles fonctions ont été ajoutées.
Si vous avez des idées de nouvelles fonctions, partager les.

 

BASH: Redimensionner tout un lot de fichiers images

Les commandes suivantes permettent de redimensionner tout un lot d'images (jpg/png) en indiquant une taille maximum à ne pas dépasser pour la largeur et la hauteur.

Les commandes identify et mogrify sont disponibles dans le paquet imagemagick.

Dans l'exemple suivant, je vais redimensionner toutes les images PNG avec une taille de 700px maxi pour la largeur et la hauteur.

# identify *.png | awk '{split($3, TAB, "x"); W = TAB[1]; H = TAB[2]; if(W > 700){system("mogrify -resize 700x "$1)} if (H > 700){system("mogrify -resize x700 "$1)}}'

Explications:

La commande identify permet d'afficher les caractéristiques d'une ou plusieurs images.
A l'aide de la commande awk, on extrait les dimensions de l'image (fonction split) et si la largeur est supérieur à 700px on redimensionne l'image (700x) à l'aide de la commande mogrify (via la fonction system) - Idem pour la hauteur de l'image (x700).

attention La commande mogrify modifie directement le fichier d'origine.
Plus de détail ici sur les commandes imagemagick.

BASH: grep multi ligne (dotall) et non gourmant (non-greedy)

Avec bash, pour effectuer ce genre de regex avec grep, j'ai trouvé l'astuce suivante:

# cat monfichier.xml | tr -d "\n" | grep -Po "<desc>.*?</desc>"

Dans mon fichier xml, mes balises <desc> sont étalées sur 3 lignes

...
    <desc>
        13.0 km, 0:13
    </desc>
    <desc>
        4.0 km, 0:6
    </desc>
...

du coup, pour les extraire correctement de mon fichier, je suis obligé de faire une recherche avec une regex qui ne tient pas compte des retours à la ligne et qui n'est surtout pas gourmande (non-greedy) afin de ne pas extraire en une seule fois toutes les balises <desc>

Pour le problème des retours à la ligne, j'utilise la commande tr pour les supprimer.

Pour le problème du "non-greedy", j'utilise grep avec l'option -P, afin d'indiquer à grep d'utiliser la regex comme une regex Perl pour pouvoir interpréter correctement l'indicateur "?" qui permet justement d'obtenir le comportement "non-greedy"

Configurer une bonne fois pour toutes ses connexions SSH

Créer une configuration propre à chaque session SSH est très utile dans le cas où les paramètres de connexions sont très hétérogènes.

Par exemple:

1 - Connexion SSH sur le serveur A avec le user USERA sur le port 2000
2 - Connexion SSH sur le serveur B avec le user USERA sur le port 22
3 - Connexion SSH sur le serveur C avec le user USERB sur le port 443 via un proxy
etc etc...

Ca fait beaucoup de paramètres à mémoriser pour chaque connexion.

Il existe donc un fichier config se trouvant dans le répertoire utilisateur ~/.ssh/ (le créer s'il n'existe pas) et dans lequel il est possible d'y paramétrer toutes les connexions SSH.

voir man 5 ssh_config pour plus de détail.

Détail de la config pour la connexion 1 (en rouge, les paramètres à modifier):

Host serveurA
    Hostname mon.serveur.mon.domaine.com (ou l'ip 10.10.10.10)
    User USERA
    Port 2000

   TCPKeepAlive yes

Bien respecter l'indentation

La variable Host accepte n'importe quelle valeur. C'est un identifiant pour le serveur.
La variable Hostname correspond au serveur distant sur lequel on se connecte. On peut y renseigner soit un nom de domaine soit une adresse IP.
La variable User correspond au user avec lequel on se connecte.
La variable Port correspond au port sur lequel on se connecte.

En fonction de cette configuration, la connexion SSH au serveur A s'effectue de cette manière:

$ ssh serveurA

On précise donc uniquement le nom renseigné dans la variable Host du fichier ~/.ssh/config

Détail de la config pour la connexion 3 (en rouge, les paramètres à modifier):

Host serveurC
    Hostname mon.serveur.mon.domaine.com (ou l'ip 12.12.12.12)
    User USERB
    Port 443

   TCPKeepAlive yes
    ProxyCommand corkscrew proxy.domaine.com 8080 %h %p

La variable ProxyCommand contient la ligne de commande à exécuter pour se connecter via le proxy indiqué en rouge. La commande utilise le programme corkscrew. Penser à l'installer s'il ne l'est pas déjà.

$ apt-get install corkscrew

Pour se connecter au serveur C:

$ ssh serveurC

C'est quand même vachement simple ;)

Etiquettes: 

Connexion à une machine distante (ssh, scp, sftp) sans saisir le mot de passe

opensshPour pouvoir se connecter à une machine distante sans être obligé de saisir un mot de passe, il faut utiliser un système d'authentification par clé privée / publique.

Ce système est très pratique dans le cas où vous utilisez les commandes ssh, scp et sftp dans des scripts.

Pour cela, il suffit d'utiliser la commande ssh-keygen.

 

Pré-requis :

Avoir un client SSH sur le poste client (openssh-client installé par défaut sur Ubuntu)
Sinon, installé le paquet suivant :

$ sudo apt-get install openssh-client

Avoir un serveur SSH sur la machine distante.
Installé, par exemple, openssh-server :

$ sudo apt-get install openssh-server

Génération d'une paire de clé privée / publique sur la machine client :

Dans l'exemple suivant, le poste client se nomme zeus et la machine distante zimbra.

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):# Laisser la valeur par défaut
Enter passphrase (empty for no passphrase):# Appuyer directement sur "Entrée"
Enter same passphrase again:# Appuyer directement sur "Entrée"
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
a0:f3:e4:93:12:c4:41:5e:f3:ea:23:0e:79:f0:02:35 root@zeus
The key's randomart image is:
+--[ RSA 2048]----+
|   .o o          |
|   o o o         |
|  E + . .        |
| . o . o         |
|. . + o S        |
| . + B .         |
|  + = B          |
|   = o o         |
|    .            |
+-----------------+
$

Vérifier que les clés privée et publique ont bien été créées :

Les clés id_rsa et id_rsa.pub sont générées dans le répertoire $HOME/.ssh

$ cd $HOME/.ssh
$ ls -l
total 16
-rw------- 1 root root 1675 2011-12-21 19:43 id_rsa
-rw-r--r-- 1 root root  391 2011-12-21 19:43 id_rsa.pub
-rw-r--r-- 1 root root 4420 2011-11-02 21:25 known_hosts
$

Copier la clé publique id_rsa.pub du poste client dans le réperoire $HOME/.ssh de la machine distante via ssh-copy-id :

$ ssh-copy-id -i id_rsa.pub root@zimbra
root@zimbra's password:    # Saisir le mot de passe et appuyer sur "Entrée"
Now try logging into the machine, with "ssh 'root@zimbra'", and check in:
 
     .ssh/authorized_keys
 
to make sure we haven't added extra keys that you weren't expecting.
 
$

Comme indiqué dans le message précédent, se connecter au serveur distant afin de vérifier que la clé publique a bien été ajoutée au fichier ".ssh/authorized_keys" (si la procédure a fonctionnée correctement, aucun mot de passe ne doit être demandé) :

$ ssh -l root zimbra

Se rendre dans le répertoire $HOME/.ssh :

$ cd $HOME/.ssh
$ ls -l
total 12
-rw-r--r-- 1 root root 391 2011-12-21 20:40 authorized_keys
-rw-r--r-- 1 root root 442 2011-05-20 08:53 known_hosts
-rw-r--r-- 1 root root 391 2011-12-21 20:26 ma_cle_publique
$ cat authorized_keys       # Contenu du fichier "authorized_keys"
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA492yBTCnwE3B7Xws0+bLYJt5G3tBI2P1v1Ld7Alp7FpJmBGzwwF
/dTwv+zh5Xr6ti2ElLQaotKj2YsR8WhFBAQqWcUR4J5/RLhMFttkANVp+3ixeDI8Id609JYcTACMz1h/lOFs38lp17KwwPZW1+tWBFovR4bwVpBNMucbuFy30LsarzdZsWnAiYfU/sLHRm/4cBZq1BF/fHjvaU3PT5NVsEuDyQg3vloW4vkAHErg6CUhp9GEUbBAPXqw== root@zeus
$ exit       # On quitte la machine distante

Pour se connecter à un serveur par un autre port que celui par défaut, utiliser la commande suivante :

$ ssh-copy-id -i id_rsa.pub "-p 10022 root@zimbra"

ou

$ ssh-copy-id -i id_rsa.pub -p 10022 root@zimbra


La procédure est terminée.

Vous pouvez désormais vous connecter à cette machine distante via ssh, scp ou sftp sans avoir à saisir un mot de passe.

Par contre, il faut être conscient des risques que cela peut engendrer.
N'importe qui ayant accès à la machine client aura automatiquement accès à la machine distante.
Ceci est à faire dans le cas où vous n'avez pas d'autres solutions.

Voir également cet article.


Dans l'exemple précédent, l'authentification automatique concernait l'utilisateur root de la machine zeus entre l'utilisateur root de la machine zimbra mais il est tout à fait possible de paramétrer une authentification automatique pour des utilisateurs différents entre le poste client et la machine distante.

Exemple :

Paramétrer une authentification automatique entre l'utilisateur toto du poste client client1 et l'utilisateur tutu de la machine distante serveur1.

Pour cela, il suffit de générer les clés privée/publique pour l'utilisateur toto sur le poste client client1 et de copier le contenu de la clé publique id_rsa.pub dans le fichier "authorized_keys" dans le répertoire $HOME/.ssh de l'utilisateur tutu de la machine distante serveur1.

La connexion suivante se fera sans saisie de mot de passe :

toto@client1:~$ ssh -l tutu serveur1

Convertir les fichiers de logs du système en page HTML colorisée

Pour générer des pages HTML colorisées contenant les différentes logs du système, il suffit d'utiliser la commande ccze.

Pour l'installer :

$ apt-get install ccze

Pour créer une page HTML contenant les données du fichier de log /var/log/syslog :

$ ccze -h < /var/log/syslog > /var/www/logsccze/syslog.html

Penser à créer auparavant le répertoire /var/www/logsccze

$ mkdir /var/www/logsccze

Utiliser un navigateur pour admirer le résultat en vous connectant sur votre serveur apache.

Etiquettes: 

Convertir un paquet RPM en DEB

Sous Debian/Ubuntu, les paquets sont au format DEB.

A l'inverse, sous Red Hat (et ses dérivés), les paquets sont au format RPM.

Il arrive parfois que les paquets permettant l'installation d'applications ne soient disponible qu'au format RPM.

Pas de panique, sous Debian/Ubuntu, il existe une application qui permet de convertir des paquets RPM en DEB.

Cette application se nomme alien.

Pour l'installer :

$ sudo apt-get install alien

Convertir un paquet RPM en DEB :

$ alien --to-deb package.rpm

Convertir un paquet DEB en RPM :

$ alien --to-rpm package.deb

Convertir un paquet RPM en DEB et l'installer automatiquement :

$ alien -i package.rpm

La conversion vers le format DEB étant la conversion par défaut, il n'est pas nécessaire de le préciser.

Etiquettes: 

Créer un fichier d'échange SWAP

Si, lors de l'installation du système, vous avez sous-estimé la taille de la partition d'échange SWAP (il est recommandé d'avoir une partition d'échange SWAP équivalent à la quantité de mémoire vive installée sur le système), il est possible de créer des fichiers supplémentaires post-installation.

Pour l'exemple, je vais créer un fichier SWAP d'1 Go dans le répertoire /mnt (avec le user root)

$ cd /mnt
$ dd if=/dev/zero of=1G.swap bs=1k count=1024000
$ du -sh ./1G.swap
1001M   ./1G.swap
$ chmod 600 /mnt/1G.swap
$ mkswap 1G.swap -f
$ swapon /mnt/1G.swap
$ swapon -s

Explications:

  1. Avec la commande dd, je créé un fichier vide d'1 Go
  2. Avec la commande du, je vérifie la taille de mon fichier créé
  3. Avec la commande chmod, j'autorise la lecture et l'écriture uniquement pour le user root
  4. Avec la commande mkswap, je convertis mon fichier en fichier d'échange SWAP
  5. Avec la commande swapon, j'active mon nouveau fichier d'échange SWAP
  6. Avec la commande swapon et le paramètre -s, je liste tous les fichiers SWAP actifs sur le système

Ajuster la configuration du swap

$ cat /proc/sys/vm/swappiness
60

Par défaut, dès 40% d'utilisation de la RAM (60% de mémoire libre), les données sont écrites dans la swap.

Avec un serveur équipé de 16Go de RAM, il est intéressant de diminuer cette valeur.

$ sysctl vm.swappiness=10

Utilisation de la swap à partir de 90% d'utilisation de la ram.

Pour une modification permanente, éditer le fichier /etc/sysctl.conf

$ echo "vm.swappiness=10" >> /etc/sysctl.conf

Debian: Ajouter une autorité de certification

Dans le cadre d'une gestion de certificats SSL auto signés, il peut-être intéressant d'ajouter son autorité de certification personnelle à la liste des autorités de certification reconnues.

Pour cela, rien de plus simple, créer le dossier perso (par exemple) dans le dossier /usr/share/ca-certificates

# mkdir /usr/share/ca-certificates/perso

Y ajouter son autorité de certification puis modifier le fichier /etc/ca-certificates.conf afin de l'ajouter à la liste

# cat <<EOF >> /etc/ca-certificates.conf
perso/ca-perso-internal.cert
EOF

et pour finir, exécuter la commande de mise à jour

# update-ca-certificates -v

Cette commande a pour effet de recopier les autorités listées dans le fichier /etc/ca-certificates.conf et présentes dans le dossier /usr/share/ca-certificates/... dans le dossier /etc/ssl/certs/

Effacer un disque de manière sûre

Pour effacer un disque dur de manière sûre, il suffit d'utiliser la commande dd et le fichier spécial /dev/urandom.

Premièrement, il faut identifier le disque à effacer et être certain de ne pas se tromper car la commande dd va détruire tout ce qui se trouve sur le disque.

$ fdisk -l
 
Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          66      524288   83  Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2              66      121601   976235712   8e  Linux LVM
 
Disk /dev/sdb: 1500.3 GB, 1500299395072 bytes
255 heads, 63 sectors/track, 182401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00026d5d
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      182401  1465136001   83  Linux
 
Disk /dev/sdc: 160.0 GB, 160041885696 bytes
255 heads, 63 sectors/track, 19457 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0008ef40
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1               1       19457   156288321   83  Linux
 
Disk /dev/sdd: 640.1 GB, 640135028736 bytes
255 heads, 63 sectors/track, 77825 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x3d4b96dc
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1       77825   625129312   83  Linux
 
$

Dans mon cas, je souhaite détruire toutes les données de mon disque de 160 GB. La commande fdisk -l m'indique qu'il s'agit du périphérique /dev/sdc.

J'exécute ensuite la commande suivante :

$ for n in `seq 7`; do dd if=/dev/urandom of=/dev/sdc bs=8b conv=notrunc; done

La commande précédente va donc écrire 7 fois de suite sur tout le disque des données aléatoires rendant ainsi illisible toutes les données précédentes.

Etiquettes: 

Forcer la coloration de la commande grep

Pour coloriser la commande grep, il existe 2 méthodes :

1 - Préciser l'option --color=always à chaque utilisation de la commande grep

$ cat .bashrc | grep --color=always '^alias'
alias ls='ls $LS_OPTIONS'
alias ll='ls $LS_OPTIONS -la'
alias l='ls $LS_OPTIONS -lA'
alias maj='apt-get update && apt-get upgrade'
$

2 - Ajouter un alias au fichier .bashrc

$ echo "alias grep='grep --color=always'" >> .bashrc
$ cat .bashrc
...
alias grep='grep --color=always'
$

Puis recharger le fichier .bashrc

$ . .bashrc
$

La colorisation sera automatique à chaque utilisation de la commande grep

$ cat .bashrc | grep '^alias'
alias ls='ls $LS_OPTIONS'
alias ll='ls $LS_OPTIONS -la'
alias l='ls $LS_OPTIONS -lA'
alias maj='apt-get update && apt-get upgrade'
alias grep='grep --color=always'
$

Etiquettes: 

Forcer la coloration du prompt

Pour forcer la coloration du prompt dans une distribution type Debian, il suffit de décommenter la ligne suivante dans le fichier ~/.bashrc

$ cat .bashrc
...
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes
...

GIT : Authentification http(s) automatique

Si on utilise un projet GIT via une connexion http(s) nécessitant une authentification, il est donc nécessaire de s'authentifier à chaque pull et commit effectués.

Ca devient vite contraignant au bout d'un moment.

Il est donc possible d'enregistrer dans un fichier les paramètres de connexion.

Le fichier ~/.gitconfig :

[user]
        name = monNom
        email = monEmail
[credential]
        helper = store
[http]
        sslVerify = false
        postBuffer = 524288000
[credential "http://monDepotGit.com"]
        username = monUser
[credential "https://monDepotGit.com"]
        username = monUser

- La section [user] contient les infos personnelles (nom, email etc etc ...)
- Le fichier doit contenir la section [credential] avec la variable helper = store
- La section [http] avec la variable sslVerify = false permet de se connecter en https sur un dépot GIT avec un certificat non reconnu (auto-signé)
infoLa variable postBuffer = 524288000 permet de résoudre l'erreur suivante lors des commits:
error: RPC failed; result=22, HTTP code = 411
Cela permettra à tous les projets locaux d'envoyer jusqu'à 500 Mo de données.
- Enfin, la section [credential "adresse http(s)"] contient l'adresse http(s) du dépot GIT ainsi que le username correspondant. La section est répétée autant de fois qu'il y a d'adresses différentes de dépots GIT.

Le fichier ~/.git-credentials :

Ce fichier contient le username associé au mot de passe et à l'adresse http(s) renseignée dans le fichier ~/.gitconfig

Ce fichier doit être lisible uniquement par le user concerné (chmod 0600).

http://monUser:monPassword@monDepotGit.com
https://monUser:monPassword@monDepotGit.com

Si le username et/ou le password contient un des caractères spécifiques à la chaine de connexion (:/@), il est obligatoire de l'écrire en hexadécimal.

@ = %40 en hexadécimal

Sinon, le simple fait de créer le fichier ~/.git-credentials et de lui attribuer le bon mode 0600, il sera automatiquement complété avec le bonne combinaison user:password@adresseHttps à la première utilisation de la comme git pull (par exemple).

Gammu - Utilisation d'un téléphone portable sur un serveur/pc

Gammu est un utilitaire permettant d'utiliser un téléphone portable via le port USB.

Pour installer gammu :

$ apt-get install gammu

Quelques commandes utiles :

Envoyer un sms

$ echo "Mon premier SMS" | gammu --sendsms TEXT 06xxxxxxxx

Afficher les dossiers SMS du téléphone

$ gammu getsmsfolders

Afficher tous les sms du téléphone

$ gammu getallsms

Astuce :

Un vieux téléphone qui ne sert à rien + une carte sim Free avec un forfait 2H à 0€ = un système de communication pour un serveur afin d'avertir l'administrateur système en cas de défaillance et permettre également à l'administrateur d'envoyer des commandes au serveur. La classe !!!

Par exemple, avertir en cas d'intrusion, démarrer un service SSH ou autre, avertir en cas de défaillance réseau (internet), avertir en cas de coupure de courant (disposer d'un UPS bien sûr) etc etc ... Le potentiel est énorme.

Le seul inconvénient, pour que gammu fonctionne parfaitement (via des scripts exécutés automatiquement), il faut avoir une session constamment ouverte sur le système.

Etiquettes: 

Gérer les services

Pour gérer les services qui se lancent automatiquement au démarrage, il suffit d'utiliser le programme SYSV-RC-CONF.

Pour l'installer :

$ sudo apt-get install sysv-rc-conf

Utilisation :

$ sudo sysv-rc-conf

Les services lancés automatiquement au démarrage ont un niveau d'exécution de 2 à 5

Pour désactiver un service au démarrage, il suffit de décocher tous les niveaux d'exécution de 2 à 5 en se positionnant sur les "X" à l'aide des flèches du clavier et en appuyant ensuite sur la barre espace.

Pour faire défiler les pages, il suffit d'appuyer sur les touches CTRL + N pour la page suivante ou CTRL + P pour la page précédente.

Installer les certificats racine CAcert sur Debian/Ubuntu

CAcert.org est une association qui fourni gratuitement des certificats ssl (signature, chiffrement, connexion, certificats https).

Pour un bon fonctionnement du service, il est nécessaire d'installer les certificats racine de CAcert.org.

Sur Debian/Ubuntu, tous les certificats racine sont installés dans le répertoire /etc/ssl/certs.

$ ls -l /etc/ssl/certs | head
total 792
lrwxrwxrwx 1 root root     26 juil.  2 18:31 00673b5b.0 -> thawte_Primary_Root_CA.pem
lrwxrwxrwx 1 root root     29 juil.  2 18:31 024dc131.0 -> Microsec_e-Szigno_Root_CA.pem
lrwxrwxrwx 1 root root     31 juil.  2 18:31 02b73561.0 -> Comodo_Secure_Services_root.pem
lrwxrwxrwx 1 root root     50 juil.  2 18:31 039c618a.0 -> TURKTRUST_Certificate_Services_Provider_Root_2.pem
lrwxrwxrwx 1 root root     23 juil.  2 18:31 03f0efa4.0 -> Wells_Fargo_Root_CA.pem
lrwxrwxrwx 1 root root     40 juil.  2 18:31 052e396b.0 -> AddTrust_Qualified_Certificates_Root.pem
lrwxrwxrwx 1 root root     21 juin  21 07:53 052e8d54 -> ssl-cert-snakeoil.pem
lrwxrwxrwx 1 root root     21 juil.  2 18:31 052e8d54.0 -> ssl-cert-snakeoil.pem
lrwxrwxrwx 1 root root     27 juil.  2 18:31 062cdee6.0 -> GlobalSign_Root_CA_-_R3.pem
L'installation ou la mise à jour de certificats se fait avec la commande update-ca-certificates
Comme indiqué dans la documentation, les certificats (avec l'extension .crt) doivent être déposés dans le répertoire /usr/local/share/ca-certificates.
 
1 - Création du répertoire cacert.org dans le répertoire /usr/local/share/ca-certificates :
sudo mkdir /usr/local/share/ca-certificates/cacert.org
2 - Téléchargements des certificats dans le répertoire créé ci-dessus :
sudo wget -P /usr/local/share/ca-certificates/cacert.org https://www.cacert.org/certs/root.crt https://www.cacert.org/certs/class3.crt
3 - Installation des certificats :
$ sudo update-ca-certificates
 
 

Inverser le contenu d'un fichier texte

Pour x raisons, il peut être nécessaire d'inverser le contenu d'un fichier texte.
Par exemple, pour afficher le contenu d'un fichier de logs dans l'ordre inverse.

Suite à la remarque de barbay, la commande tac permet d'inverser le contenu d'un fichier texte.

$ tac /var/log/syslog

Sinon, pour inverser le contenu d'un fichier texte, il est possible d'utiliser l'enchainement de commandes suivant :

Pour commencer, j'affiche le contenu du fichier avec la commande cat :

$ cat /var/log/syslog

J'enchaine avec la commande nl qui permet de numéroter les lignes du fichier :

$ cat /var/log/syslog | nl

J'enchaine avec la commande sort qui permet de trier le contenu du fichier numériquement (avec l'option -n) et en ordre inverse (avec l'option -r) sur la première colonne du fichier contenant les numéros de lignes générés par la commande nl :

$ cat /var/log/syslog | nl | sort -n -r

Il reste maintenant à supprimer la première colonne du fichier contenant les numéros de lignes avec la commande cut :

$ cat /var/log/syslog | nl | sort -n -r | cut -f2

Et voilà, le fichier s'affiche à l'écran dans l'ordre inverse.

Etiquettes: 

La console sous Linux

Sous Linux, que ce soit sur une version Desktop ou Server, 6 terminaux sont en permanence à disposition.

Pour les activer, rien de plus simple.

Sur un version Desktop ou Server, les différents terminaux sont accessibles
via les combinaisons des touches suivantes :

  • Ctrl + Alt + F1 : terminal 1 (tty1)
  • Ctrl + Alt + F2 : terminal 2 (tty2)
  • Ctrl + Alt + F3 : terminal 3 (tty3)
  • Ctrl + Alt + F4 : terminal 4 (tty4)
  • Ctrl + Alt + F5 : terminal 5 (tty5)
  • Ctrl + Alt + F6 : terminal 6 (tty6)

Sur une version Desktop, une fois basculé en mode console, il est possible de revenir au mode graphique grâce à la combinaison des touches suivantes :

  • Ctrl + Alt + F7 : retour au mode graphique

Linux: Utiliser la mémoire vive comme espace de stockage

Le fait d'utiliser la mémoire vive d'une machine comme espace de stockage permet d'accéder aux données à des vitesses défiant toute concurrence.

Si l'avantage est grand, l'inconvénient l'est tout autant. Effectivement, en cas de coupure de courant, toutes les données se trouvant en mémoire vive sont perdues à jamais.

C'est pour cette raison que je préconise l'usage de cette méthode à des fins de lecture uniquement.
Ce qui est déjà un bon compromis.

Un autre inconvénient réside dans le fait que la taille des données à stocker est limitée à la taille de la mémoire vive disponible.
Sur les systèmes récents, la quantité de mémoire vive disponible étant assez conséquente, ça ne devrait donc pas être un problème.

Si vous devez utiliser une base de données sqlite et/ou des fichiers textes pour y lire des données, pourquoi ne pas les stocker dans la mémoire vive afin d'y accéder plus rapidement.

Voici la marche à suivre (on utilise comme type de système de fichier tmpfs):

On créé le répertoire qui servira de point de montage

# mkdir /tmpfs

On monte le répertoire en mémoire

# mount -t tmpfs no-device /tmpfs

Dorénavant, tout ce qui sera stocké dans le répertoire /tmpfs sera automatiquement stocké dans la mémoire vive.

Un petit script au démarrage du système qui se charge d'effectuer le point de montage et d'y copier les fichiers (fichiers textes, base de données sqlite etc etc...) et le tour est joué.

Etiquettes: 

Liste complète des extensions de domaines

Le site www.iana.org recense la liste complète des extensions de domaines.

Il peut-être utile d'en avoir une copie dans un fichier (pour l'utiliser avec la commande grep par exemple).

Pré requis:

$ apt-get install curl html2text awk

Création du fichier ROOT:

$ curl http://www.iana.org/domains/root/db -o - 2>/dev/null | html2text | awk '$1 ~ "^\\." {printf("\\%s\n",$1)}' > ROOT

$ head ROOT
\.abbott
\.abogado
\.ac
\.academy
\.accountant
\.accountants
\.active
\.actor
\.ad
\.ads

Ce fichier peut ensuite être utilisé avec la commande grep pour filtrer des adresses web par exemple.

$ grep -f ROOT monFichierLog

Un ajout dans crontab permet d'avoir cette liste constamment à jour.

Postfix + Spamassassin

Configurer Postfix sous Ubuntu est un jeu d'enfant grâce à la commande

$ dpkg-reconfigure postfix

Quand il s'agit d'ajouter l'anti-spam spamassassin à la configuration de Postfix, ça se complique un peu.

Dans un premier temps, il faut installer spamassassin

$ apt-get install spamassassin

Il faut ensuite créer le répertoire de spamassassin, ajouter le user et le group suivant au système et enfin régler les permissions sur le répertoire

$ mkdir /var/spool/spamassassin
$ useradd -d /var/spool/spamassassin -s /bin/false spamassassin
$ chown spamassassin:spamassassin /var/spool/spamassassin -Rc

Il faut activer spamassassin en modifiant le fichier suivant comme indiqué

$ cat /etc/default/spamassassin | egrep -v '^(#|$)'
ENABLED=1
OPTIONS="--create-prefs --max-children 5 --helper-home-dir -u spamassassin -g spamassassin"
PIDFILE="/var/run/spamd.pid"
CRON=1

J'ai également modifié le fichier /etc/spamassassin/local.cf afin de ré-écrire le début de l'objet des mails considérés comme du spam
    rewrite_header Subject __SPAM__

Redémarrer spamassassin afin de prendre en compte la nouvelle configuration

$ service spamassassin restart

Il faut ensuite modifier le fichier /etc/postfix/master.cf afin d'ajouter les éléments suivants
1 - Remplacer la ligne smtp par celle-ci
    smtp      inet  n       -       n       -       -       smtpd   -o content_filter=spamassassin
2 - A la fin du fichier, ajouter la ligne suivante
    spamassassin unix -     n       n       -       -       pipe user=spamassassin argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

Reste à redémarrer Postfix

$ service postfix restart

Dorénavant, tous les spams auront la mention __SPAM__ dans l'objet, reste à faire une règle dans son client mail afin de les placer directement dans le dossier spam

Etiquettes: 

QWERTY to AZERTY / AZERTY to QWERTY

Dans Linux, pour modifier le clavier à utiliser, saisir dans une console :

Pour un passage de qwerty en azerty

$ loadkeys fr
$ apt-get install console-data (si besoin)
$ dpkg-reconfigure console-setup
$ loadkeys fr
$ dpkg-reconfigure console-data

Pour un passage de azerty en qwerty

$ loadkeys us
$ apt-get install console-data (si besoin)
$ dpkg-reconfigure console-setup
$ loadkeys fr
$ dpkg-reconfigure console-data


Clavier AZERTY

Clavier AZERTY


Clavier QWERTY

Clavier QWERTY

RAID logiciel avec mdadm

Créer un volume RAID 1

Le RAID1 permet de mettre en parallèle au minimum 2 disques durs afin d'avoir une reproduction parfaite des données sur tous les disques de la grappe.

En cas de panne matériel d'un disque, les données restent accessibles grâce aux autres disques.

C'est une excellente solution pour la sécurité des données.

L'inconvénient, l'espace de stockage total est égal à la capacité du plus petit des disques.

Prenons l'exemple d'une grappe de 2 disques d'une capacité de 40 Gb chacun. L'espace de stockage du volume RAID1 sera donc de 40 Gb.

Il n'est pas nécessaire d'avoir des disques de même capacité pour créer un volume RAID1. Il est tout à fait possible de créer un volume RAID1 avec un disque de 30 Gb et un disque de 40 Gb. Il faudra par contre avoir des partitions de même taille. Il faudra donc partitionner le disque le plus grand afin d'avoir une partition de taille identique à celui du plus petit.

Pour l'exemple, je vais créer un RAID1 avec deux disques de 10 Gb chacun. J'utilise Ubuntu 12.04 LTS pour les tests.

Premièrement, lister les disques présents sur le système.

$ fdisk -l
Disk /dev/vda: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/vda doesn't contain a valid partition table
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/vdb doesn't contain a valid partition table

Mon système contient bien deux disques de 10 Gb correspondant à /dev/vda et /dev/vdb.

Il faut ensuite créer sur chaque disque une partition de type "Linux RAID autodetect".

$ fdisk /dev/vda

La commande "m" du programme fdisk permet d'afficher l'aide.

Nous allons donc ajouter une partition primaire égale à la taille du disque avec la commande "n (add a new partition)" puis la commande "p (primary)".

Je valide ensuite les choix par défaut afin de créer une seule partition sur le disque.

Une fois la partition créée, il faut ensuite modifier le type de la partition avec la commande "t (change a partition's system id)".

Le code correspondant a une partition Linux RAID autodetect est "fd"

Il faut donc saisir "fd" pour le choix de la partition Linux RAID autodetect.

Pour terminer, il faut valider les changements avec la commande "w (write table to disk and exit)".

Répéter l'opération pour le second disque /dev/vdb.

$ fdisk /dev/vdb

Lister à nouveau les disques pour vérifier que nos modifications ont bien été prises en compte.

$ fdisk -l
Disk /dev/vda: 10.7 GB, 10737418240 bytes
2 heads, 17 sectors/track, 616809 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x050bac38
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
2 heads, 17 sectors/track, 616809 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x8df86189
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048    20971519    10484736   fd  Linux RAID autodetect

Les deux disques /dev/vda et /dev/vdb contiennent bien une partition Linux RAID autodetect /dev/vda1 et /dev/vdb1.

Nous pouvons donc, à présent, créer notre volume RAID1 que nous nommerons /dev/md0.

Pour créer un volume RAID, nous utiliserons le programme mdadm.

Pour l'installer, rien de plus simple.

$ apt-get install mdadm

Nous allons donc créer un volume RAID1 /dev/md0 composé de 2 disques et contenant les 2 partitions /dev/vda1 et /dev/vdb1.

$ mdadm --create /dev/md0 --level=1 --assume-clean --raid-devices=2 /dev/vda1 /dev/vdb1
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
Continue creating array?
Continue creating array? (y/n) y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

L'option "--create" indique à mdadm de créer le volume /dev/md0.
L'option "--level=1" indique que nous créons un RAID de type 1.
L'option "--raid-devices=2" indique que le volume est constitué de 2 disques.
Pour terminer, nous indiquons les partitions à insérer dans le volume RAID1 /dev/md0.

La commande "fdisk -l" nous montre notre volume RAID1 que nous venons de créer.

$ fdisk -l
Disk /dev/md0: 10.7 GB, 10727849984 bytes
2 heads, 4 sectors/track, 2619104 cylinders, total 20952832 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/md0 doesn't contain a valid partition table

La commande "mdadm --detail /dev/md0" nous montre l'état de notre volume RAID1 /dev/md0.

$ mdadm --detail /dev/md0
/dev/md0:
        Version : 1.2
  Creation Time : Sat May 18 17:08:08 2013
     Raid Level : raid1
     Array Size : 10476416 (9.99 GiB 10.73 GB)
  Used Dev Size : 10476416 (9.99 GiB 10.73 GB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent
 
    Update Time : Sat May 18 17:08:08 2013
          State : clean
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0
 
           Name : vm-vpn:0  (local to host vm-vpn)
           UUID : b131679e:e5b15236:8003d92b:be9f610b
         Events : 0
 
    Number   Major   Minor   RaidDevice State
       0     253        1        0      active sync   /dev/vda1
       1     253       17        1      active sync   /dev/vdb1

Maintenant que notre volume RAID1 est créé, il nous reste à créer le système de fichier. Nous allons créer un système de fichier EXT4.

$ mkfs.ext4 /dev/md0
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2619104 blocks
130955 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
 
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks):
done
Writing superblocks and filesystem accounting information: done

Nous pouvons maintenant utiliser notre nouvel espace de stockage. Pour cela nous devons créer un point de montage.

$ mkdir /media/raid
$ mount /dev/md0 /media/raid
$ ll /media/raid
total 24
drwxr-xr-x 3 root root  4096 May 18 22:48 .
drwxr-xr-x 4 root root  4096 May 18 22:59 ..
drwx------ 2 root root 16384 May 18 22:48 lost+found

Pour un montage automatique au démarrage du système, nous devons récupérer l'UUID du volume /dev/md0 ...

$ blkid /dev/md0
/dev/md0: UUID="59fd912b-e1a9-4693-ab91-f7d9eb883bae" TYPE="ext4"

... et enregistrer dans le fichier "/etc/fstab" la ligne suivante

UUID=59fd912b-e1a9-4693-ab91-f7d9eb883bae /media/raid ext4 rw 0 0

Pour finir, enregistrer les informations du raid dans le fichier /etc/mdadm/mdadm.conf :

$ mdadm --detail --scan >> /etc/mdadm/mdadm.conf

Etiquettes: 

Créer un volume RAID1 avec 1 seul disque (1 disque manquant)

Dans l'exemple suivant, je souhaite créer un volume RAID1 qui contiendra les partitions /dev/vda1 et /dev/vdb1.

La partition /dev/vda1 n'existe pas et sera donc créée (Linux RAID autodetect).
La partition /dev/vdb1 existe et est du type Linux avec un système de fichier ext4 et contenant des données qu'il faudra transférer sur le nouveau volume RAID1.
La partition /dev/vdb1 sera ensuite modifiée en Linux RAID autodetect puis intégrée au volume RAID1.

$ fdisk -l
 
Disk /dev/vda: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/vda doesn't contain a valid partition table
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
2 heads, 17 sectors/track, 616809 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xb4e1f868
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048    20971519    10484736   83  Linux

Création d'une partition /dev/vda1 du type Linux RAID autodetect :

$ fdisk /dev/vda

On vérifie que la partition a bien été créée :

$ fdisk -l /dev/vda
 
Disk /dev/vda: 10.7 GB, 10737418240 bytes
2 heads, 17 sectors/track, 616809 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x4ef75a9a
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    20971519    10484736   fd  Linux RAID autodetect

Création du volume RAID1 /dev/md0 et contenant, pour l'instant, uniquement la partition /dev/vda1 (ajout de l'argument "missing" à la place de la seconde partition /dev/vdb1):

$ mdadm --create /dev/md0 --level=1 --assume-clean --raid-devices=2 /dev/vda1 missing
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
Continue creating array?
Continue creating array? (y/n) y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

La commande suivante nous montre que notre volume RAID1 /dev/md0 a bien été créé :

$ fdisk -l /dev/md0
 
Disk /dev/md0: 10.7 GB, 10727849984 bytes
2 heads, 4 sectors/track, 2619104 cylinders, total 20952832 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/md0 doesn't contain a valid partition table

La commande suivant nous montre bien l'état dégradé de notre volume RAID1 /dev/md0 :

$ cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid1 vda1[0]
      10476416 blocks super 1.2 [2/1] [U_]
 
unused devices: <none>

Maintenant que notre volume RAID1 est créé, il nous reste à créer le système de fichier. Nous allons créer un système de fichier EXT4.

$ mkfs.ext4 /dev/md0
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2619104 blocks
130955 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
 
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

Nous pouvons maintenant utiliser notre nouvel espace de stockage. Pour cela nous devons créer un point de montage.

$ mkdir /media/raid
$ mount /dev/md0 /media/raid
$ ll /media/raid
total 24
drwxr-xr-x 3 root root  4096 May 18 22:48 .
drwxr-xr-x 4 root root  4096 May 18 22:59 ..
drwx------ 2 root root 16384 May 18 22:48 lost+found

Nous allons maintenant transférer toutes les données de la partition /dev/vdb1 montée dans le répertoire /mnt vers la partition /dev/md0 montée dans le répertoire /media/raid

$ rsync -av /mnt/* /media/raid/

Une fois les données copiées, il faut démonter la partition /dev/vdb1

$ umount /mnt

Il faut maintenant recopier la table de partition du disque /dev/vda vers le disque /dev/vdb.

$ sfdisk --dump /dev/vda | sfdisk /dev/vdb

$ fdisk -l /dev/vdb
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xb4e1f868
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048    20971519    10484736   fd  Linux RAID autodetect

Nous pouvons donc ajouter à notre volume RAID1 /dev/md0 la partition /dev/vdb1

$ mdadm /dev/md0 -a /dev/vdb1

Puis surveiller en temps réel la reconstruction du volume RAID1 /dev/md0

$ watch -n 1 cat /proc/mdstat

Une fois la reconstruction terminée ...

$ cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid1 vdb1[2] vda1[0]
      10476416 blocks super 1.2 [2/2] [UU]

 
unused devices: <none>

... nous récupérons l'UUID du volume /dev/md0 ...

$ blkid /dev/md0
/dev/md0: UUID="e1f4bff2-bbb7-4449-a964-474a6f23495b" TYPE="ext4"

... et nous enregistrons dans le fichier /etc/fstab la ligne suivante :

UUID=e1f4bff2-bbb7-4449-a964-474a6f23495b /media/raid ext4 rw 0 0

pour que notre volume /dev/md0 soit monté automatiquement à chaque démarrage du système.

Reconstruire un volume RAID1 dégradé

Dans l'exemple suivant, le disque /dev/vdb est en panne.
La partition /dev/vdb1 intégrée au volume RAID1 /dev/md127 n'est plus accessible.

Dans le cas où la panne ne serait pas détectée par le système, exécuter la commande suivante :

$ mdadm --manage --set-faulty /dev/md127 /dev/vdb1

On vérifie l'état du volume RAID :

$ cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 vda1[2] vdb1[1](F)
      10476416 blocks super 1.2 [2/1] [U_]
 
unused devices: <none>

La partition /dev/vdb1 est bien indiquée comme n'étant plus accessible (flag à F).

On supprime la partition /dev/vdb1 du volume RAID /dev/md127 :

$ mdadm /dev/md127 -r /dev/vdb1
mdadm: hot removed /dev/vdb1 from /dev/md127

On vérifie que la partition /dev/vdb1 a bien été supprimée :

$ cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 vda1[2]
      10476416 blocks super 1.2 [2/1] [U_]
 
unused devices: <none>

La partition n'apparait plus.

Il faut maintenant arrêter la machine, remplacer le disque défectueux puis redémarrer la machine.

Après redémarrage de la machine, on liste les disques :

$ fdisk -l
 
Disk /dev/vda: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/vdb doesn't contain a valid partition table
 
Disk /dev/md127: 10.7 GB, 10727849984 bytes
2 heads, 4 sectors/track, 2619104 cylinders, total 20952832 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/md127 doesn't contain a valid partition table

Mon nouveau disque /dev/vdb apparait bien dans la liste.
Il faut maintenant recopier la table de partition du disque sain /dev/vda vers le nouveau disque /dev/vdb.

$ sfdisk --dump /dev/vda | sfdisk /dev/vdb

Puis on vérifie que l'opération s'est bien déroulée :

$ fdisk -l
 
Disk /dev/vda: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/md127: 10.7 GB, 10727849984 bytes
2 heads, 4 sectors/track, 2619104 cylinders, total 20952832 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/md127 doesn't contain a valid partition table

Mon nouveau disque /dev/vdb contient bien désormais une partition du type Linux RAID autodetect /dev/vdb1.

Pour terminer, il faut maintenant ajouter notre nouvelle partition /dev/vdb1 à notre volume RAID1 /dev/md127 :

$ mdadm /dev/md127 -a /dev/vdb1

Reste à vérifier la reconstruction du RAID :

$ cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 vdb1[3] vda1[2]
      10476416 blocks super 1.2 [2/1] [U_]
      [>....................]  recovery =  2.7% (286656/10476416) finish=13.0min speed=13029K/sec
 
unused devices: <none>

Pour information, la reconstruction du RAID peut être suivie en temps réel grâce à cette commande :

$ watch -n 1 cat /proc/mdstat

Une fois la reconstruction terminée, on vérifie l'état de notre volume RAID1 /dev/md127 :

$ cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 vdb1[3] vda1[2]
      10476416 blocks super 1.2 [2/2] [UU]
 
unused devices: <none>

$ mdadm --detail /dev/md127
/dev/md127:
        Version : 1.2
  Creation Time : Mon May 20 22:18:07 2013
     Raid Level : raid1
     Array Size : 10476416 (9.99 GiB 10.73 GB)
  Used Dev Size : 10476416 (9.99 GiB 10.73 GB)
   Raid Devices : 2
  Total Devices : 2
    Persistence : Superblock is persistent
 
    Update Time : Tue May 21 13:29:20 2013
          State : clean
 Active Devices : 2
Working Devices : 2
 Failed Devices : 0
  Spare Devices : 0
 
           Name : vm-vpn:127  (local to host vm-vpn)
           UUID : 6479c625:120e2001:65ef33d1:dfb60c0f
         Events : 90
 
    Number   Major   Minor   RaidDevice State
       2     253        1        0      active sync   /dev/vda1
       3     253       17        1      active sync   /dev/vdb1

Etiquettes: 

Supprimer un volume RAID1

Dans l'exemple suivant, je vais sauvegarder mes données sur le disque /dev/vdc et supprimer mon volume RAID1 /dev/md127 .

$ fdisk -l
 
Disk /dev/vda: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/vdb: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048    20971519    10484736   fd  Linux RAID autodetect
 
Disk /dev/vdc: 10.7 GB, 10737418240 bytes
16 heads, 63 sectors/track, 20805 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/vdc doesn't contain a valid partition table
 
Disk /dev/md127: 10.7 GB, 10727849984 bytes
2 heads, 4 sectors/track, 2619104 cylinders, total 20952832 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
 
Disk /dev/md127 doesn't contain a valid partition table

Création d'une partition du type ext4 sur le disque /dev/vdc :

$ fdisk /dev/vdc

$ fdisk -l /dev/vdc
 
Disk /dev/vdc: 10.7 GB, 10737418240 bytes
2 heads, 17 sectors/track, 616809 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xb4e1f868
 
   Device Boot      Start         End      Blocks   Id  System
/dev/vdc1            2048    20971519    10484736   83  Linux

Montage de la partition /dev/vdc1 dans le répertoire /mnt

$ mount /dev/vdc1 /mnt

Puis sauvegarde des données (mon volume /dev/md127 est monté dans le répertoire /media/raid)

$ rsync -av /media/raid/ /mnt/

Il faut ensuite démonter le volume RAID1 /dev/md127

$ umount /media/raid

Arrêt du volume /dev/md127 :

$ mdadm --stop /dev/md127
mdadm: stopped /dev/md127

On supprime le volume /dev/md127 :

$ mdadm --remove /dev/md127

On supprime toutes les informations présentes sur les partitions /dev/vda1 et /dev/vdb1

$ mdadm --zero-superblock /dev/vd[ab]1

Pour terminer, éditer le fichier /etc/mdadm/mdadm.conf et supprimer la ligne concernant le volume RAID1 /dev/md127

Ne pas oublier également de supprimer le point de montage du volume RAID1 /dev/md127 dans le fichier /etc/fstab.

Etiquettes: 

SELinux: Autoriser un nouveau service systemd

Tout dépend du niveau de sécurité configuré pour SELinux.

Il existe 3 modes différents:
- enforcing
- permissive
- disabled

Dans le cas des 2 derniers (permissive & disabled) il ne devrait pas y avoir de problème pour activer et démarrer un nouveau service systemd.

Par contre, si le mode est enforcing, il y a de grandes chances pour que cela empêche le démarrage d'un nouveau service.

Pour le vérifier:

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33

Donc, si le mode SELinux est enforcing et que vous avez créé un nouveau service et que celui-ci n'arrive pas à démarrer, suivre la procédure ci-dessous.

Exemple avec une distribution AlmaLinux:

Installer le paquet policycoreutils-python-utils.noarch

$ sudo dnf install -y policycoreutils-python-utils.noarch

Exécuter les 2 commandes suivantes:

$ sudo ausearch -m avc -ts today | audit2allow -M le_nom_de_mon_service
$ sudo semodule -i le_nom_de_mon_service.pp

En théorie, l'exécution de la première commande indique d'exécuter la seconde commande.
Modifier le terme le_nom_de_mon_service

Et enfin:

$ sudo systemctl start le_nom_de_mon_service.service
$ sudo systemctl status le_nom_de_mon_service.service

Le problème devrait être réglé.

Suppressions des retours à la ligne

Récapitulatifs des différentes manières de supprimer les retours à la ligne dans une chaine de caractères.

$ A=$(echo -e "Ceci est une chaine\navec des retours\nà la ligne.")
$ echo "$A"
Ceci est une chaine
avec des retours
à la ligne.

Avec la commande tr

Suppression du retour à la ligne :

$ tr -d "\n" <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ tr "\n" " " <<< "$A"
Ceci est une chaine avec des retours à la ligne.

Avec la commande sed

Suppression du retour à la ligne :

$ sed ':a;N;$!ba;s/\n//g' <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ sed ':a;N;$!ba;s/\n/ /g' <<< "$A"
Ceci est une chaine avec des retours à la ligne.

Avec la commande read

Suppression du retour à la ligne :

$ while read L; do printf "%s" "$L"; done <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ while read L; do printf "%s" "$L "; done <<< "$A"
Ceci est une chaine avec des retours à la ligne.

Avec la commande perl

Suppression du retour à la ligne :

$ perl -p -e 's/\n//' <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ perl -p -e 's/\n/ /' <<< "$A"
Ceci est une chaine avec des retours à la ligne.

Avec la commande paste

Suppression du retour à la ligne :

$ paste -s -d '' <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ paste -s -d ' ' <<< "$A"
Ceci est une chaine avec des retours à la ligne.

Avec la commande awk

Suppression du retour à la ligne :

$ awk 1 ORS='' <<< "$A"
Ceci est une chaineavec des retoursà la ligne.

Remplacement du retour à la ligne par un espace :

$ awk 1 ORS=' ' <<< "$A"
Ceci est une chaine avec des retours à la ligne.

.

Sécuriser son serveur SSH en automatisant le changement du password de connexion

Le changement régulier du password permettant la connexion à son serveur SSH est la garantie d'avoir une haute sécurité.

Le dire, c'est bien mais le faire, c'est mieux.

Le faire tous les combiens de temps ? Que mettre en password ? Vais-je m'en souvenir ?

Voici un petit enchainement de commandes, à enregistrer dans crontab et qui permet de générer aléatoirement un password pour le user désiré grâce à la commande pwgen, de le modifier automatiquement grâce à la commande chpasswd puis de l'envoyer par mail.

$ passwd="user1:`/usr/bin/pwgen -s -y -c 14 1`" && /bin/echo $passwd | /usr/sbin/chpasswd && /bin/echo $passwd | /usr/bin/mail -s "Il fait beau aujourd\'hui" user1@domaine.fr && passwd=""

Pour installer la commande pwgen (par défaut sur Ubuntu) :

$ apt-get install pwgen

Détail de la commande pwgen :

-s : génère des mots de passe complètement aléatoires
-y : insère au moins 1 caractère spécial
-c : insère au moins 1 lettre majuscule
Le nombre 14 indique que l'on veut générer un mot de passe de 14 caractères
Le chiffre 1 indique que l'on veut générer 1 seul mot de passe

L'envoi par mail peut être remplacé par un envoi par sms grâce à la commande gammu.

Etiquettes: 

Tentative de récupération de données sur un disque défectueux

Pour récupérer les données sur un disque ayant des secteurs défectueux, il est possible d'utiliser la commande dd comme ceci.

L'opération doit être effectuée sur un disque ou une partition non montée.

Première méthode :
On recopie les données de disque à disque.

if=/dev/hdx : correspond au disque source (défectueux)
of=/dev/hdy : correspond au disque cible

Attention à ne pas inverser le if et le of

if pour input (entrée)
of pour output (sortie)

$ dd bs=4k if=/dev/hdx of=/dev/hdy conv=noerror,sync

L'exemple ci-dessus utilise des disques entiers mais il est possible d'effectuer l'opération sur des partitions.

Seconde méthode :
On recopie les données du disque vers un fichier.

$ dd bs=4k if=/dev/hdx of=/path/to/image conv=noerror,sync

Idem avec des partitions.

Etiquettes: 

Ubuntu 20: périphérique audio bluetooth

Je me suis acheté une enceinte bluetooth JBL Flip 5 et quand je l'ai connecté sur mon portable équipé de Ubuntu 20, grosse déception, aucun son, le néant.

Alors que sur mon smartphone, aucun problème.

Une petite recherche sur internet m'a permis de trouver la solution ci-dessous:

$ sudo apt-get install pavucontrol

Formidable, problème réglé....

Un bastion SSH

Un bastion SSH, qu'est-ce que c'est que ce truc là ?

Dans le cadre d'une grappe de serveurs devant être administrés à distance, un bastion SSH est un serveur sur lequel un serveur SSH (openssh-server par exemple) est installé et par lequel on peut accèder en SSH aux autres serveurs.

Le but est d'utiliser ce serveur comme serveur de rebond afin d'accéder aux différents serveurs.

Dans un premier temps, il est nécessaire de créer un nouveau serveur sur lequel nous installerons un serveur SSH.

Pour l'exemple, je vais créer un nouveau serveur avec OpensSUSE 15.5 et installer openssh-server.

Une machine virtuelle sous Proxmox équipée d'un CPU, de 8 Go de disque et 512 Mo de mémoire vive suffira amplement.

Faire en sorte que ce serveur et seulement ce serveur soit accessible, de l'extérieur, via le port SSH 22 (ou autre si vous souhaitez sécuriser encore plus l'accès au bastion SSH).

# zypper in openssh-server

Pour la configuration du serveur SSH, quelques lignes suffisent.

# cat /etc/ssh/sshd_config
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
AuthorizedKeysFile      .ssh/authorized_keys
PrintMotd no
Subsystem       sftp    /usr/lib/ssh/sftp-server
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
AllowUsers toto

Quelques petites explications:
Ligne 1, on interdit la connexion avec le compte "root"
Ligne 2, on autorise la connexion à l'aide d'une clé SSH
Ligne 3, 4 et 5, on interdit l'authentification par mot de passe
Ligne 6, permet d'indiquer le fichier contenant les clés autorisées à se connecter
Ligne 7, on désactive l'affichage du contenu du fichier /etc/motd
Ligne 8 à 11, divers paramètres
Ligne 12, permet d'indiquer le ou les utilisateurs autorisés à se connecter

Ensuite, il est nécessaire de créer l'utilisateur avec lequel on se connectera au serveur SSH.

L'important est de créer un utiilisateur n'ayant aucun droit de se connecter au serveur SSH servant de bastion.

# useradd -m -s /bin/false toto

Sur le serveur client, ou n'importe quel autre serveur, créer un clé ssh qui sera utilisée pour se connecter au bastion SSH.
Voir un exemple ici https://isbeta.fr/LsUB2 (créer une clé SSH avec ou sans protection par mot de passe, à vous de choisir)

Pour la suite des opérations, je vais partir du principe que la clé ssh permettant de se connecter au bastion SSH se nommera id_rsa.toto et la clé publique id_rsa.toto.pub

Ensuite, il est nécessaire de copier la clé publique (celle créée ci-dessus id_rsa.toto.pub) dans le fichier authorized_keys du bastion SSH.

Créer le dossier .ssh dans le dossier de l'utilisateur toto du bastion SSH.

# mkdir /home/toto/.ssh
# chown toto /home/toto/.ssh
# chmod 0700 /home/toto/.ssh

Créer ensuite le fichier authorized_keys et y copier la clé publique

# touch /home/toto/.ssh/authorized_keys
# vim /home/toto/.ssh/authorized_keys
# cat /home/toto/.ssh/authorized_keys
ssh-rsa xxxx...lgjgdlkffgjdlkg=
# chown toto /home/toto/.ssh/authorized_keys
# chmod 0600 /home/toto/.ssh/authorized_keys

En théorie, la configuration du bastion SSH est terminée.
Le serveur SSH a été paramétré, le user "toto" autorisé à se connecter a été créé et la clé SSH  a été renseignée dans le fichier .ssh/authorized_keys du user "toto".

Maintenant, coté client, il est nécessaire de configurer l'accès au bastion SSH.

Modifier le fichier .ssh/config du client.

# cat .ssh/config
Host monbastion.loc
    Hostname monbastion.loc
    User toto
    Port 22
    TCPKeepAlive yes
    IdentityFile ~/.ssh/id_rsa.toto
Host mon_serveur_nginx
    Hostname 192.168.1.50
    User root
    Port 22
    TCPKeepAlive yes
    ProxyJump monbastion.loc
    IdentityFile ~/.ssh/id_rsa
Host mon_serveur_mariadb
    Hostname 192.168.1.60
    User root
    Port 22
    TCPKeepAlive yes
    ProxyJump monbastion.loc
    IdentityFile ~/.ssh/id_rsa

Quelques explications:

Le premier host correspond au bastion SSH accessible via internet, rien de bien compliqué, excepté peut-être le paramètre IdentityFile faisant référence à la clé SSH id_rsa.toto, l'unique clé autorisée à se connecter au bastion SSH avec le user toto.

Le second host, est celui qui correspond, pour l'exemple, à mon serveur nginx, inaccessible via internet en SSH, seulement en HTTP et HTTPS.
Pour pouvoir y acccéder, j'indique via le paramètre ProxyJump, d'utiliser le host monbastion.loc pour pouvoir y accéder en SSH. Le paramètre IdentityFile permet d'indiquer une autre clé SSH autorisée à se connecter au serveur nginx.

Idem pour le troisième host.

Les adresses IP indiquées pour les hosts 2 et 3 doivent être accessible via le bastion SSH.

Il est possible de renseigner différents serveurs utilisant le bastion SSH commme serveur de rebond.

Et voilà, la configuration est enfin terminée.
Pour se connecter au serveur nginx, par exemple:

# ssh mon_serveur_nginx

C'est tout simple, la connexion via monbastion.loc est complètement transparente.

Unity - Restaurer le dash

Mon dash (tableau de bord) est planté.

Les applications ne sont plus affichées. 

Résultat, je ne peux plus lancer mes applications via le dash. C'est vraiment pas très pratique.

Pour corriger les anomalies du dash, une seule chose à faire. Le restaurer.

Pour cela, il suffit juste de supprimer le fichier ~/.local/share/zeitgeist/activity.sqlite

cp -a ~/.local/share/zeitgeist/activity.sqlite ~/
$ rm ~/.local/share/zeitgeist/activity.sqlite

Bien entendu, avant toute suppression, on le sauvegarde. Juste au cas où.
Prudence est mère de sûreté.

On redémarre la machine et normalement, tout est rentré dans l'ordre.
Enfin, j'espère ;o)

Etiquettes: 

Utiliser le langage SQL sur n'importe quel fichier texte

J'en ai rêvé, Harel Ben-Attia l'a fait.

Utiliser des fichiers texte comme des tables SQL.
C'est ce que propose de faire la commande q.

Ce programme est développé en python.
Pour fonctionner, il utilise donc python 2.4 (minimum) et le module sqlite3.

Il est possible d'utiliser des fichiers texte dont le séparateur de colonne (par défaut) est l'espace.
N'importe quel autre séparateur est utilisable à condition de l'indiquer à l'aide de l'option -d
Si le fichier texte contient une ligne d'en-tête, il est possible de l'indiquer à l'aide de l'option -H et cela permet d'utiliser les en-têtes comme nom de colonne dans les requêtes SQL.
L'option -z permet d'utiliser des fichiers compressés avec gzip.

Le projet est disponible ici.

Un paquet RPM est disponible pour l'installation.
Pour les distributions Debian/Ubuntu, utiliser le programme alien pour convertir le paquet RPM en DEB.

Pour l'installtion, sous Debian/Ubuntu, après conversion du paquet :

$ dpkg --install q_xxx_all.deb

Les différentes utilisations possibles :

Utiliser la commande q derrière un pipe

Par exemple, la commande ls -l retourne le contenu d'un répertoire structuré en colonne.

Par conséquent, du fait que le retour de cette commande soit structuré, il est possible de l'envoyer à la commande q via un pipe et d'y effectuer des petites requêtes SQL.

$ ls -l /var/log
-rw-r--r-- 1 root   root    3554 Mar 12 14:09 alternatives.log
-rw-r--r-- 1 root   root    1208 Feb 21 10:51 alternatives.log.1
-rw-r--r-- 1 root   root     246 Jan 27 18:17 alternatives.log.2.gz
-rw-r--r-- 1 root   root     385 Jan  6 21:10 alternatives.log.3.gz
-rw-r--r-- 1 root   root     539 Nov 22 08:16 alternatives.log.4.gz
-rw-r--r-- 1 root   root     276 Oct 24 16:47 alternatives.log.5.gz
-rw-r--r-- 1 root   root     948 Sep 23 22:35 alternatives.log.6.gz
drwxr-xr-x 2 root   root    4096 Mar  1 05:00 apt
-rw-r----- 1 syslog adm   370529 Mar 12 14:16 auth.log

Le résultat est structuré en 9 colonnes.
 
$ ls -l /var/log | q -b "select * from -"
total      4164                                                          
-rw-r--r-- 1    root   root 3554    Mar  12   14:09 alternatives.log     
-rw-r--r-- 1    root   root 1208    Feb  21   10:51 alternatives.log.1   
-rw-r--r-- 1    root   root 246     Jan  27   18:17 alternatives.log.2.gz
-rw-r--r-- 1    root   root 385     Jan  6    21:10 alternatives.log.3.gz
-rw-r--r-- 1    root   root 539     Nov  22   08:16 alternatives.log.4.gz
-rw-r--r-- 1    root   root 276     Oct  24   16:47 alternatives.log.5.gz
-rw-r--r-- 1    root   root 948     Sep  23   22:35 alternatives.log.6.gz
drwxr-xr-x 2    root   root 4096    Mar  1    05:00 apt                  
-rw-r----- 1    syslog adm  370722  Mar  12   14:39 auth.log
 
Pour lire les données sur l'entrée standard, il faut utiliser le tiret "-" en nom de table.
L'option -b permet d'afficher le résultat aligné en colonne.
 
$ ls -l /var/log | q -b "select c9 from -"
alternatives.log     
alternatives.log.1   
alternatives.log.2.gz
alternatives.log.3.gz
alternatives.log.4.gz
alternatives.log.5.gz
alternatives.log.6.gz
apt                  
auth.log
 
On affiche uniquement le nom des fichiers correspondant donc au contenu de la colonne 9
 
$ ls -l /var/log | q -b "select c9, c5 from -"
alternatives.log      3554   
alternatives.log.1    1208   
alternatives.log.2.gz 246    
alternatives.log.3.gz 385    
alternatives.log.4.gz 539    
alternatives.log.5.gz 276    
alternatives.log.6.gz 948    
apt                   4096   
auth.log              370722
 
On ajoute la taille de chaque fichier.
 
Effectuer des count et des sum etc etc ...
 
$ ls -l /var/log | sed '1d' | q -b "select count(*), sum(c5) from -"
54 4386404
 
Attention, j'ai ajouté la commande sed '1d' car la commande ls -l affiche sur la première ligne la taille totale des fichiers.
 
On peut même effectuer des opérations
 
$ ls -l /var/log | sed '1d' | q -b "select count(*), sum(c5)/1024 from -"
54 4283
 
La clause "order by" existe aussi.
 
$ ls -l /var/log | sed '1d' | q -b "select c9, c5 from - order by c5"
btmp                  0      
btmp.1                0      
init.fifo             0      
vsftpd.log            0      
boot                  1      
dmesg                 1      
kern.log              1      
lpr.log               1      
mail.err              1      
user.log              1
 
En ordre inverse
 
$ ls -l /var/log | sed '1d' | q -b "select c9, c5 from - order by c5 desc"
auth.log.0            1067312
mail.info             923064 
mail.log              923064 
messages              457260 
auth.log              371108 
lastlog               292292 
auth.log.1.gz         70783  
dpkg.log              37630  
daemon.log            29013  
syslog.0              24504
 
Idem avec la clause "where"
 
$ ls -l /var/log | sed '1d' | q -b "select c9, c5 from - where c9 like 'a%' order by c5 desc"
auth.log.0            1067312
auth.log              371108 
auth.log.1.gz         70783  
apt                   4096   
alternatives.log      3554   
alternatives.log.1    1208   
alternatives.log.6.gz 948    
alternatives.log.4.gz 539    
alternatives.log.3.gz 385    
alternatives.log.5.gz 276
 
Et avec des fichiers maintenant
 
Je crée un fichier pour l'exemple.
 
$ ls -l /var/log | sed '1d' > listVarLog
 
Et j'effectue un "select" directement sur le fichier
 
$ q -b "select * from listVarLog"
-rw-r--r-- 1 root   root 3554    Mar 12 14:09 alternatives.log     
-rw-r--r-- 1 root   root 1208    Feb 21 10:51 alternatives.log.1   
-rw-r--r-- 1 root   root 246     Jan 27 18:17 alternatives.log.2.gz
-rw-r--r-- 1 root   root 385     Jan 6  21:10 alternatives.log.3.gz
-rw-r--r-- 1 root   root 539     Nov 22 08:16 alternatives.log.4.gz
-rw-r--r-- 1 root   root 276     Oct 24 16:47 alternatives.log.5.gz
-rw-r--r-- 1 root   root 948     Sep 23 22:35 alternatives.log.6.gz
drwxr-xr-x 2 root   root 4096    Mar 1  05:00 apt                  
-rw-r----- 1 syslog adm  371301  Mar 12 15:16 auth.log             
-rw-r----- 1 syslog adm  1067312 Feb 23 05:00 auth.log.0
 
Si mon fichier n'est pas dans mon répertoire courant...
 
$ q -b "select * from /root/listVarLog"
-rw-r--r-- 1 root   root 3554    Mar 12 14:09 alternatives.log     
-rw-r--r-- 1 root   root 1208    Feb 21 10:51 alternatives.log.1   
-rw-r--r-- 1 root   root 246     Jan 27 18:17 alternatives.log.2.gz
-rw-r--r-- 1 root   root 385     Jan 6  21:10 alternatives.log.3.gz
-rw-r--r-- 1 root   root 539     Nov 22 08:16 alternatives.log.4.gz
-rw-r--r-- 1 root   root 276     Oct 24 16:47 alternatives.log.5.gz
-rw-r--r-- 1 root   root 948     Sep 23 22:35 alternatives.log.6.gz
drwxr-xr-x 2 root   root 4096    Mar 1  05:00 apt                  
-rw-r----- 1 syslog adm  371301  Mar 12 15:16 auth.log             
-rw-r----- 1 syslog adm  1067312 Feb 23 05:00 auth.log.0
 
... J'indique le chemin complet dans ma requête.
 
Avec une ligne d'en-tête.
 
$ head listVarLog 
PERMS INODE USER GROUP SIZE MONTH DAY TIME FILE
-rw-r--r-- 1 root   root    3554 Mar 12 14:09 alternatives.log
-rw-r--r-- 1 root   root    1208 Feb 21 10:51 alternatives.log.1
-rw-r--r-- 1 root   root     246 Jan 27 18:17 alternatives.log.2.gz
-rw-r--r-- 1 root   root     385 Jan  6 21:10 alternatives.log.3.gz
-rw-r--r-- 1 root   root     539 Nov 22 08:16 alternatives.log.4.gz
-rw-r--r-- 1 root   root     276 Oct 24 16:47 alternatives.log.5.gz
-rw-r--r-- 1 root   root     948 Sep 23 22:35 alternatives.log.6.gz
drwxr-xr-x 2 root   root    4096 Mar  1 05:00 apt
-rw-r----- 1 syslog adm   371301 Mar 12 15:16 auth.log
 
$ q -b -H "select * from listVarLog"
-rw-r--r-- 1 root   root 3554    Mar 12 14:09 alternatives.log     
-rw-r--r-- 1 root   root 1208    Feb 21 10:51 alternatives.log.1   
-rw-r--r-- 1 root   root 246     Jan 27 18:17 alternatives.log.2.gz
-rw-r--r-- 1 root   root 385     Jan 6  21:10 alternatives.log.3.gz
-rw-r--r-- 1 root   root 539     Nov 22 08:16 alternatives.log.4.gz
-rw-r--r-- 1 root   root 276     Oct 24 16:47 alternatives.log.5.gz
-rw-r--r-- 1 root   root 948     Sep 23 22:35 alternatives.log.6.gz
drwxr-xr-x 2 root   root 4096    Mar 1  05:00 apt                  
-rw-r----- 1 syslog adm  371301  Mar 12 15:16 auth.log             
-rw-r----- 1 syslog adm  1067312 Feb 23 05:00 auth.log.0
 
Exécuter une requête avec le nom des champs.
 
$ q -b -H "select FILE, SIZE from listVarLog"
alternatives.log      3554   
alternatives.log.1    1208   
alternatives.log.2.gz 246    
alternatives.log.3.gz 385    
alternatives.log.4.gz 539    
alternatives.log.5.gz 276    
alternatives.log.6.gz 948    
apt                   4096   
auth.log              371301 
auth.log.0            1067312
 
$ q -b -H "select FILE, SIZE, MONTH from listVarLog where MONTH = 'Mar'"
alternatives.log 3554   Mar
apt              4096   Mar
auth.log         371301 Mar
btmp             0      Mar
daemon.log       29013  Mar
dpkg.log         37630  Mar
lastlog          292292 Mar
mail.info        923064 Mar
mail.log         923064 Mar
messages         457260 Mar
 
En utilisant un délimiteur de colonne autre que l'espace.
 
Avec le fichier /etc/passwd par exemple, dont le délimiteur est ":"
 
$ q -b -d':' -D'|' "select * from /etc/passwd"
root     |x|0    |0    |root                              |/root             |/bin/bash         
daemon   |x|1    |1    |daemon                            |/usr/sbin         |/bin/sh           
bin      |x|2    |2    |bin                               |/bin              |/bin/sh           
sys      |x|3    |3    |sys                               |/dev              |/bin/sh           
sync     |x|4    |65534|sync                              |/bin              |/bin/sync         
games    |x|5    |60   |games                             |/usr/games        |/bin/sh           
man      |x|6    |12   |man                               |/var/cache/man    |/bin/sh           
lp       |x|7    |7    |lp                                |/var/spool/lpd    |/bin/sh           
mail     |x|8    |8    |mail                              |/var/mail         |/bin/sh           
news     |x|9    |9    |news                              |/var/spool/news   |/bin/sh
 
J'indique le délimiteur du fichier avec l'option -d':' et j'en profite pour modifier le délimiteur de sortie avec l'option -D'|'
 
Et les jointures entre fichier
 
$ q -b -d':' -D'|' "select a.c3, a.c1, a.c4, b.c1 from /etc/passwd a left join /etc/group b on a.c4 = b.c3"
0    |root     |0    |root    
1    |daemon   |1    |daemon  
2    |bin      |2    |bin     
3    |sys      |3    |sys     
4    |sync     |65534|nogroup 
5    |games    |60   |games   
6    |man      |12   |man     
7    |lp       |7    |lp      
8    |mail     |8    |mail    
9    |news     |9    |news
 
Cette requête permet d'afficher l'uid, le user, le gid et le group pour chaque ligne du fichier /etc/passwd et en effectuant une jointure avec le fichier /etc/group et en indiquant les colonnes permettant d'effectuer la jointure.
 
Pour l'instant, seul le "SELECT" peut-être utilisé.
La syntaxe utilisée est celle du SGDB SQLite
Etiquettes: 

Utiliser telnet pour envoyer un mail

Envoyer un mail via la commande telnet permet de tester la configuration d'un serveur SMTP.

En rouge, les informations à saisir.

$ telnet smtp.free.fr 25
Trying 212.27.48.4...
Connected to smtp.free.fr.
Escape character is '^]'.
220 smtp4-g21.free.fr ESMTP Postfix
HELO test.domain.com
250 smtp4-g21.free.fr
MAIL FROM:<test@domain.com>
250 2.1.0 Ok
RCPT TO:<toto@domain.fr>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: test message
This is the body of the message!
.

250 2.0.0 Ok: queued as 2D8FD4C80FF
quit
221 2.0.0 Bye
Connection closed by foreign host.

Explications :

  1. Connexion au serveur SMTP sur le port 25
  2. Le serveur nous renvoie quelques informations
  3. Exécution de la commande HELO avec le nom de notre domaine en paramètre
  4. Le serveur nous répond qu'il est OK
  5. On indique l'émetteur grâce à la commande MAIL FROM
  6. Le serveur nous répond qu'il est OK
  7. On indique le destinataire grâce à la commande RCPT TO
  8. Le serveur nous répond qu'il est OK
  9. On indique le début du message grâce à la commande DATA
  10. Le serveur nous indique qu'il faut saisir un point (.) pour terminer le message
  11. On indique le sujet du message
  12. On saisie le corp du message
  13. On saisi un point (.) pour indiquer la fin du message
  14. Le serveur nous répond qu'il est OK
  15. On ferme la connexion grâce à la commande quit

Si une authentification est nécessaire, après avoir envoyé la commande HELO, il suffit d'envoyer la commande AUTH LOGIN (après le point 4).

Le serveur répond en affichant "334 VXNlcm5hbWU6".
Cette chaine est encodée en base64. 
Traduction en langage humain : "334 Username:"
Il suffit donc de répondre en renseignant son username encodé, bien sûr, en base64.
Le serveur répond ensuite en affichant "334 UGFzc3dvcmQ6".
Cette chaine est également encodée en base64.
Traduction en langage humain : "334 Password:"
Il suffit donc de répondre en renseignant son password encodé, également, en base64.
Si tout se passe bien, le serveur doit répondre "235 Authentication successful"
Reste à continuer la procédure à partir du point 5.

Pour l'encodage en base64, toutes les infos ici.

Etiquettes: 

Utiliser un serveur FTP dans un script

Pour utiliser un serveur FTP dans un script, il faut que l'identification se fasse également via le script.
Par contre, la sécurité du serveur FTP est diminuée du fait d'avoir en clair dans le script les identifiants de connexion.
Cette méthode est donc à utiliser avec des serveurs FTP ne contenant pas de données sensibles.

L'exemple suivant permet de télécharger du serveur ftp "ftp.tutu.fr" le fichier "fichier.txt" en utilisant l'identifiant "toto" et le mot de passe "tata"

#!/bin/bash
loginftp="toto"
motdepassftp="tata"
host_ftp="ftp.tutu.fr"
mon_fichier="fichier.txt"
# On se positionne dans le dossier /tmp
# où sera téléchargé le fichier du serveur FTP
cd /tmp
ftp -in <<EOF
open $host_ftp
user $loginftp $motdepassftp
get $mon_fichier
close
bye
EOF
# Je peux ensuite exécuter d'autres commandes
# sur le fichier téléchargé
exit 0

Il ne reste plus qu'à automatiser le script avec une tâche cron.

Etiquettes: 

Watch - Exécuter une commande périodiquement.

La commande watch permet d'exécuter à intervalle régulière n'importe quelle commande passée en paramètre.

La période de rafraichissement par défaut est de 2 secondes.

L'option -n permet d'indiquer une période de rafraichissement en secondes.
L'option -d permet de mettre en surbrillance les différences survenues entre 2 périodes de rafraichissement.

Surveiller le contenu d'un répertoire :

$ watch -d ls -l /tmp

Cette commande  affiche à l'écran le contenu du répertoire /tmp et raffraichit l'affichage toutes les 2 secondes en mettant les différences en surbrillance (option -d).
Dès qu'un fichier/répertoire est créé/modifié/supprimé l'information apparait à l'écran.

Idem mais toutes les 10 secondes :

$ watch -n 10 -d ls -l /tmp

Surveiller la reconstruction d'un volume RAID :

$ watch cat /proc/mdstat

Surveiller les connexions et l'utilisation d'un serveur :

$ watch -d w
$ watch -d uptime

Windigo

Qu'est ce que Windigo ?

Windigo est un "virus" infectant les serveurs UNIX (entre autres) et qui utilise ces derniers pour envoyer des spams en grosse quantité.

Pour que le virus soit actif, une backdoor OpenSSH dénommée "Ebury" doit être installée manuellement sur les serveurs par les hackers.

Pour vérifier l'infection du système, il est nécessaire d'exécuter la commande suivante :

$ ssh -G 2>&1 | grep -e illegal -e unknown > /dev/null && echo "System clean" || echo "System infected"

Cette commande peut paraitre compliquée alors qu'elle est, au contraire, toute simple.

Que fait-elle exactement ?

Si le système est infecté, la backdoor OpenSSH utilise l'option -G de la commande ssh qui normalement n'existe pas si le système est sain.

Pour faire simple, il suffit donc d'exécuter la commande ssh avec cette fameuse option (-G) pour voir si le système est compromis.

$ ssh -G
ssh: illegal option -- G

Si la commande retourne une erreur, comme celle indiquée ci-dessus, c'est que le système est sain.

A l'inverse, si aucune erreur n'est retournée par la commande, c'est que le système est compromis et qu'il faut donc, rapidement, y remédier.
Pour cela, il est nécessaire de formater le serveur et d'y réinstaller un système complet.
Il faut également changer tous les mots de passe et clés privées.

Etiquettes: 

X11 forwarding en SSH via Putty

Utiliser des applications graphiques d'un serveur (Ubuntu/Debian/CentOS) sur un poste Windows via une connexion SSH.

Pré-requis coté server :

$ sudo apt-get install openssh-server
$ sudo apt-get install xvfb xdm xfonts-base
$ sudo apt-get install twm "xfonts-100dpi*" xterm

Activer X11Forwarding dans la configuration du serveur SSH distant:

# cat /etc/ssh/sshd_config | grep X11
X11Forwarding yes

Pré-requis coté client (Windows par exemple) :

Putty

Xming

Mise en oeuvre :

Ouvrir Xming

Ouvrir Putty

Saisir l'adresse IP du serveur

putty_1

Dans "Connexion" --> "SSH" --> "X11", cocher "Enable X11 forwarding"

Putty_2

Ciquer sur "OPEN"

Dans la console Putty, saisir "Login" et "Password"

Pour ouvrir une console par exemple saisir :

$ xterm &

La variable $DISPLAY permet de vérifier que le X11Forwarding est bien activé:

# echo $DISPLAY
localhost:10.0

La commande xauth list permet d'afficher le magic cookie indispensable au X11Forwarding :

# xauth list
monServeur/unix:10  MIT-MAGIC-COOKIE-1  17e3926a07573d0c51be57158896cf4c

Conserver le X11Forwarding et le magic cookie après avoir changé d'utilisateur:

Dans le cas où vous auriez besoin de lancer une application avec un utilisateur différent de celui utilisé pour la connexion SSH, il faut obligatoirement transmettre au nouvel utilisateur le magic cookie.

Utiliser la commande précédente pour afficher le magic cookie (xauth list):

Changer d'utilisateur et ajouter le magic cookie à l'aide de la commande xauth add et en paramètre, le résultat de la commande xauth list:

# su - user1

$ xauth add monServeur/unix:10  MIT-MAGIC-COOKIE-1  17e3926a07573d0c51be57158896cf4c

 

gocr - La reconnaissance de caractères sous GNU/Linux

gocr est une commande qui permet de faire de la reconnaissance de caractères sous GNU/Linux.

Elle utilise des fichiers au format pnm.

Pré requis :

Pour utiliser gocr, il est nécessaire d'installer le programme djpeg qui permet de convertir un fichier jpeg en pnm.

$ sudo apt-get install gocr libjpeg-progs

Utilisation :

Je vais partir du cas le plus complet c'est à dire effectuer une reconnaissance de caractères à partir d'un fichier PDF (une facture par exemple).

Je dois donc convertir ce fichier PDF en fichier PNM.

Je vais utiliser la commande convert qui permet de convertir un fichier PDF en JPEG, puis la commande djpeg pour convertir mon fichier JPEG en PNM.

Il faut que le fichier JPEG généré à partir du fichier PDF soit d'une excellente qualité ( -density 500).

convert -density 500 exempleFactureMicroRemise.pdf exempleFactureMicroRemise.jpg

Pour finir, conversion du fichier JPEG en PNM avec la commande djpeg puis envoi du résulat à la commande gocr qui va écrire le contenu dans un fichier texte.

djpeg -pnm -gray exempleFactureMicroRemise.jpg | gocr - > exempleFactureMicroRemise.txt

Le résultat obtenu est assez satisfaisant.

Etiquettes: 

jigl - Générer une gallerie photo en HTML

jigl est un programme qui permet de générer une gallerie photo HTML à partir d'images se trouvant dans un répertoire.

Un petit exemple de ce qu'il est possible de faire avec jigl.

Installation :

$ apt-get install jigl

Générer sa première gallerie :

1 - Copier toutes les images dans un répertoire (dans l'exemple suivant les images se trouvent dans le répertoire "testJigl").

2 - Générer la gallerie en exécutant la commande suivante :

$ jigl testJigl

Une fois l'opération terminée, 3 répertoires ont été créés dans le répertoire "testJigl"

testJigl/slides
testJigl/thumbs
testJigl/web

C'est dans le répertoire "web" que se trouvent la gallerie HTML.
2 pages HTML ont été créées par images ainsi qu'un page "index.html".

Il est possible d'ajouter des options afin de personnaliser sa gallerie, comme par exemple :

- Spécifier le titre de la page index.html avec l'option --index-title "Mon Titre"
- Spécifier le nombre de lignes affichées sur la page index.html avec l'option --index-row 5 (permet d'afficher 5 lignes).
- Spécifier le thème à utiliser avec l'option --theme nomDuTheme (black, jovotek, white)
- Spécifier le répertoire web de destination avec l'option --web-dir nomDuRepertoire

Plus d'info avec :

$ jigl --help

Etiquettes: 

pixelize

Pixelize

Pixelize est une application permettant de pixeliser une image en utilisant d'autres images référencées dans sa base de données.

Pré-requis

Installer l'application pixelize

$ apt-get install pixelize

Configuration

Avant d'utiliser pixelize, il est nécessaire de mettre à jour sa base de données. Il s'agit d'un fichier nommé pic_db.dat situé dans le répertoire home (~/pic_db.dat).
Pour le mettre à jour, rien de plus simple. Il suffit d'utiliser l'utilitaire make_db de pixelize.
Pixelize reconnait les formats d'images les plus courants.

Pour mettre à jour la base de données avec le contenu d'un répertoire contenant des images :

$ make_db /mon_repertoire/*.jpg

Ou pour mettre à jour la base de données avec des images se trouvant dans un ensemble de sous-répertoires :

$ find /mon_repertoire -iname "*.jpg" -print | xargs make_db

Plus il y aura d'images référencées dans la base de données, meilleure sera la pixelisation.

Utilisation

Pour utiliser pixelize, il est recommandé de lancer l'application à partir d'un terminal

$ pixelize

De cette manière, en cas d'erreur, celles-ci s'afficheront dans le terminal.

Pour pixeliser une image, utiliser le menu "File => Open" puis sélectionner l'image souhaitée.
Pour lancer la pixelisation cliquer sur "Options => Render"

Le temps de pixelisation est fonction de la taille de la photo à pixeliser et de la quantité d'images référencées dans sa base de données.

Pour sauvegarder l'image pixelisée, cliquer sur "File => Save". Il faut obligatoirement indiquer l'extension du fichier (jpeg, bmp, png ...)

Exemple

Image d'origine

Pixelisation effectuée avec une base de 400 photos

Pixelisation effectuée avec une base de 11 000 photos

Documentation Ubuntu

Site de l'auteur

Etiquettes: 

syslog-ng: Error setting capabilities

Sur les systèmes Debian et dérivés, pour corriger l'erreur
    syslog-ng: Error setting capabilities, capability management disabled; error='Operation not permitted'
il suffit de décommenter la ligne SYSLOGNG_OPTS="--no-caps" dans le fichier /etc/default/syslog-ng.

$ cat /etc/default/syslog-ng
# If a variable is not set here, then the corresponding
# parameter will not be changed.
# If a variables is set, then every invocation of
# syslog-ng's init script will set them using dmesg.

# log level of messages which should go to console
# see syslog(3) for details
#
#CONSOLE_LOG_LEVEL=1
 
# Command line options to syslog-ng
SYSLOGNG_OPTS="--no-caps"

On relance syslog-ng

$ service syslog-ng restart

Etiquettes: 

SUSE

logo suse

Tous mes trucs et astuces pour le système openSUSE et SLES

 

 

Fichier attachéTaille
Image icon suse-logo-640x320.jpg14.63 Ko
Etiquettes: 

SLES: Problème de founisseurs de paquets (vendor) lors d'un dist-upgrade

Lors d'une montée de version d'une distribution SLES 15.5 vers la version 15.6, je me suis retrouvé confronté à ces erreurs avec les paquets perl-*

$ sudo zypper dup
Chargement des données du dépôt...
Lecture des paquets installés...
Avertissement : Vous êtes sur le point d'exécuter une mise à niveau de distribution avec tous les dépôts activés. Assurez-vous que ces dépôts sont compatibles avant de continuer. Reportez-vous à 'man zypper' pour obtenir plus d'informations sur cette commande.
Calcul de la mise à niveau de la distribution...
43 problèmes :
Problème : 1: problème avec l'élément perl-File-ShareDir-1.118-lp155.33.3.noarch installé
Problème : 2: problème avec l'élément perl-File-Which-1.27-lp155.56.3.noarch installé
Problème : 3: problème avec l'élément perl-List-MoreUtils-0.430-lp155.120.2.noarch installé
Problème : 4: problème avec l'élément perl-List-MoreUtils-XS-0.430-lp155.19.3.x86_64 installé
Problème : 5: problème avec l'élément perl-MRO-Compat-0.15-lp155.33.4.noarch installé
Problème : 6: problème avec l'élément perl-Params-Util-1.102-lp155.31.2.x86_64 installé
Problème : 7: problème avec l'élément perl-Package-Stash-0.40-lp155.40.2.noarch installé
Problème : 8: problème avec l'élément perl-Package-Stash-XS-0.30-lp155.24.2.x86_64 installé
Problème : 9: problème avec l'élément perl-Params-ValidationCompiler-0.31-lp155.12.2.noarch installé
Problème : 10: problème avec l'élément perl-Role-Tiny-2.002004-lp155.39.4.noarch installé
Problème : 11: problème avec l'élément perl-Specio-0.48-lp155.21.2.noarch installé
Problème : 12: problème avec l'élément perl-Sub-Quote-2.006008-lp155.15.4.noarch installé
Problème : 13: problème avec l'élément perl-Test-Fatal-0.017-lp155.34.4.noarch installé
Problème : 14: problème avec l'élément perl-Variable-Magic-0.63-lp155.35.4.x86_64 installé
Problème : 15: problème avec l'élément perl-XML-LibXML-2.0210-lp155.107.1.x86_64 installé
Problème : 16: problème avec l'élément perl-XML-SAX-1.02-lp155.49.1.noarch installé
Problème : 17: problème avec l'élément perl-YAML-Tiny-1.74-lp155.31.3.noarch installé
Problème : 18: problème avec l'élément perl-B-Hooks-EndOfScope-0.26-lp155.38.3.noarch installé
Problème : 19: problème avec l'élément perl-namespace-autoclean-0.29-lp155.31.2.noarch installé
Problème : 20: problème avec l'élément perl-Class-Data-Inheritable-0.09-lp155.64.4.noarch installé
Problème : 21: problème avec l'élément perl-Class-Inspector-1.36-lp155.35.4.noarch installé
Problème : 22: problème avec l'élément perl-Class-Singleton-1.6-lp155.28.3.noarch installé
Problème : 23: problème avec l'élément perl-DateTime-1.650.0-lp155.117.1.x86_64 installé
Problème : 24: problème avec l'élément perl-DateTime-Locale-1.400000-lp155.101.1.noarch installé
Problème : 25: problème avec l'élément perl-Devel-StackTrace-2.50.0-lp155.47.1.noarch installé
Problème : 26: problème avec l'élément perl-Exporter-Tiny-1.006002-lp155.27.3.noarch installé
Problème : 27: problème avec l'élément perl-Exception-Class-1.45-lp155.42.4.noarch installé
Problème : 28: problème avec l'élément perl-DateTime-TimeZone-2.620.0-lp155.153.1.noarch installé
Problème : 29: problème avec l'élément perl-Ref-Util-XS-0.117-lp155.3.3.x86_64 installé
Problème : 30: problème avec l'élément perl-Ref-Util-0.204-lp155.3.2.noarch installé
Problème : 31: problème avec l'élément perl-Text-Template-1.61-lp155.67.2.noarch installé
Problème : 32: problème avec l'élément perl-Class-Method-Modifiers-2.15-lp155.35.4.noarch installé
Problème : 33: problème avec l'élément perl-UNIVERSAL-require-0.19-lp155.97.2.noarch installé
Problème : 34: problème avec l'élément perl-Class-XSAccessor-1.19-lp155.22.4.x86_64 installé
Problème : 35: problème avec l'élément perl-Cpanel-JSON-XS-4.37-lp155.80.2.x86_64 installé
Problème : 36: problème avec l'élément perl-XString-0.005-lp155.3.3.x86_64 installé
Problème : 37: problème avec l'élément perl-Devel-Caller-2.07-lp155.16.2.x86_64 installé
Problème : 38: problème avec l'élément perl-Devel-LexAlias-0.05-lp155.19.2.x86_64 installé
Problème : 39: problème avec l'élément perl-Moo-2.005005-lp155.58.4.noarch installé
Problème : 40: problème avec l'élément perl-Net-SSH2-0.73-lp155.29.3.x86_64 installé
Problème : 41: problème avec l'élément perl-PadWalker-2.5-lp155.39.3.x86_64 installé
Problème : 42: problème avec l'élément perl-Parallel-ForkManager-2.02-lp155.3.2.noarch installé
Problème : 43: problème avec l'élément perl-Perl-Tidy-20240202.0.0-lp155.92.1.noarch installé

Problème : 1: problème avec l'élément perl-File-ShareDir-1.118-lp155.33.3.noarch installé
 Solution 1 : installer perl-File-ShareDir-1.104-1.22.noarch du fournisseur SUSE LLC <https://www.suse.com/>
en remplaçant perl-File-ShareDir-1.118-lp155.33.3.noarch du fournisseur obs://build.opensuse.org/devel:languages:perl
 Solution 2 : conserver l'élément perl-File-ShareDir-1.118-lp155.33.3.noarch obsolète

Choisir une des solutions ci-dessus par son numéro ou bien sauter, recommencer ou annuler [1/2/s/r/a/d/?] (a):

A priori, cela signifie que le fournisseur de ces paquets a changé et j'ai donc le choix entre 2 options, changer de fournisseur ou conserver la version actuelle.

Si je souhaite changer de fournisseur, il va falloir que je tape "1" pour chaque paquet, c'est à dire 43 fois.

Il existe beaucoup plus simple, il suffit d'ajouter l'option "--allow-vendor-change" pour que cela se fasse automatiquement.

$ sudo zypper dup --allow-vendor-change
Chargement des données du dépôt...
Lecture des paquets installés...
Avertissement : Vous êtes sur le point d'exécuter une mise à niveau de distribution avec tous les dépôts activés. Assurez-vous que ces dépôts sont compatibles avant de continuer. Reportez-vous à 'man zypper' pour obtenir plus d'informations sur cette commande.
Calcul de la mise à niveau de la distribution...

Les 351 paquets suivants vont être mis à jour :
  apparmor-abstractions apparmor-docs apparmor-parser apparmor-parser-lang apparmor-profiles apparmor-utils apparmor-utils-lang augeas augeas-lenses autoyast2 autoyast2-installation ... yast2-trans-fr
  yast2-update yast2-users yast2-xml yast2-ycp-ui-bindings zstd

Les 3 schémas suivants vont être mis à jour :
  apparmor base minimal_base

Les 4 produits suivants vont être mis à jour :
  SUSE Package Hub 15                                15.5-0 -> 15.6-0
  Containers Module                                  15.5-0 -> 15.6-0
  Module to ship some SLE subpackages by PackageHub  15.5-0 -> 15.6-0
  Python 3 Module                                    15.5-0 -> 15.6-0

Les 45 paquets suivants vont être remis à une version inférieure :
  perl-B-Hooks-EndOfScope perl-Class-Data-Inheritable perl-Class-Inspector perl-Class-Method-Modifiers perl-Class-Singleton perl-Class-XSAccessor perl-Cpanel-JSON-XS perl-DateTime perl-DateTime-Locale
  perl-DateTime-TimeZone perl-Devel-Caller perl-Devel-LexAlias perl-Devel-StackTrace perl-Exception-Class perl-Exporter-Tiny perl-File-ShareDir perl-File-Which perl-List-MoreUtils perl-List-MoreUtils-XS
  perl-Moo perl-MRO-Compat perl-namespace-autoclean perl-Net-SSH2 perl-Package-Stash perl-Package-Stash-XS perl-PadWalker perl-Parallel-ForkManager perl-Params-Util perl-Params-ValidationCompiler
  perl-Ref-Util perl-Ref-Util-XS perl-Role-Tiny perl-Specio perl-Sub-Quote perl-Test-Fatal perl-Text-Template perl-UNIVERSAL-require perl-Variable-Magic perl-XML-LibXML perl-XML-SAX perl-XString
  perl-YAML-Tiny zypper zypper-log zypper-needs-restarting

Le paquet suivant va changer d'architecture :
  perl-XML-SAX  noarch -> x86_64

Les 43 paquets suivants vont changer de fournisseur :
  perl-B-Hooks-EndOfScope         obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Class-Data-Inheritable     obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Class-Inspector            obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Class-Method-Modifiers     obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Class-Singleton            obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Class-XSAccessor           obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Cpanel-JSON-XS             obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-DateTime                   obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-DateTime-Locale            obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-DateTime-TimeZone          obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Devel-Caller               obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Devel-LexAlias             obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Devel-StackTrace           obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Exception-Class            obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Exporter-Tiny              obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-File-ShareDir              obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-File-Which                 obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-List-MoreUtils             obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-List-MoreUtils-XS          obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Moo                        obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-MRO-Compat                 obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-namespace-autoclean        obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Net-SSH2                   obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Package-Stash              obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Package-Stash-XS           obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-PadWalker                  obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Parallel-ForkManager       obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Params-Util                obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Params-ValidationCompiler  obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Perl-Tidy                  obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Ref-Util                   obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Ref-Util-XS                obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Role-Tiny                  obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Specio                     obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Sub-Quote                  obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Test-Fatal                 obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-Text-Template              obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-UNIVERSAL-require          obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-Variable-Magic             obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-XML-LibXML                 obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-XML-SAX                    obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>
  perl-XString                    obs://build.opensuse.org/devel:languages:perl -> openSUSE
  perl-YAML-Tiny                  obs://build.opensuse.org/devel:languages:perl -> SUSE LLC <https://www.suse.com/>

Les 30 NOUVEAUX paquets suivants vont être installés :
  iproute2-bash-completion kernel-default-6.4.0-150600.23.17.1 kernel-firmware-ath12k lftp libbpf1 libcbor0_10 libevent-2_1-7 libfa1 libgpgmepp6 libjson-c5 libLLVM17 libnfsidmap0 libnfsidmap1 libnsl1
  libopenssl3 libpoppler135 libpxbackend-1_0 libsemanage2 libsemanage-conf libsepol2 libsgutils2-1_48-2 libtiff6 liburcu6 man-pages-fr openssl-3 perl-Devel-GlobalDestruction perl-strictures perl-Sub-Name
  systemd-sysvcompat util-linux-tty-tools

Les 13 paquets suivants vont être SUPPRIMÉS :
  libabsl2308_0_0 libcbor0 libpoppler126 libprotobuf-lite20 libsemanage1 nfsidmap openssl-1_1 python3-bind python3-slip python3-slip-dbus sssd-common systemd-sysvinit zypper-docker

Les 270 paquets suivants ne sont pas supportés par leur fournisseur :
  apparmor-abstractions apparmor-docs apparmor-parser apparmor-parser-lang apparmor-profiles apparmor-utils apparmor-utils-lang ... yast2-slp
  yast2-snapper yast2-storage-ng yast2-transfer yast2-trans-fr yast2-update yast2-xml yast2-ycp-ui-bindings zstd

Le paquet suivant requiert un redémarrage du système :
  kernel-default-6.4.0-150600.23.17.1

351 paquets à mettre à jour, 45 à rétrograder, 30 nouveaux, 13 à supprimer, 43 à changer de fournisseur, 1 à changer d'architecture.
Taille de téléchargement totale : 851,0 MiB. Déjà en cache : 0 B. Après l'opération, 471,8 MiB d'espace disque supplémentaire sera utilisé.

    Note : Redémarrage du système requis.

Back-end:  classic_rpmtrans
Continuer ? [o/n/v/...? affiche toutes les options] (o):

Et voilà, plus besoin de répondre à la question pour chaque paquet.
Par contre, le changement de fournisseur implique, comme indiqué, un downgrade des paquets.
A voir si cela est gênant ou pas.

Ubuntu Server

Logo Ubuntu Server

Tous mes trucs et astuces sur Ubuntu Server récoltés ici et là.

Mes problèmes rencontrés et les solutions que j'ai trouvées.

Des exemples de configuration pour avoir un serveur opérationnel.

Etiquettes: 

Téléchargement

Pour télécharger Ubuntu Server, rien de plus simple.

Il suffit de se rendre à l'adresse suivante :

http://www.ubuntu-fr.org/telechargement?variante=server

et de cliquer sur le lien

Télécharger

Etiquettes: 

Installation

Documentation Ubuntu

Pour installer Ubuntu Server, rien de plus simple.

  1. Télécharger la version Ubuntu Server
  2. Graver sur un CD ou un DVD le fichier téléchargé
  3. Démarrer le PC destiné à devenir le serveur avec le CD ou le DVD (faire en sorte que le PC boot sur le CD ou le DVD au démarrage)
  4. Suivre les instructions à l'écran (installer uniquement le système de base)

Pour info, voir la rubrique Trucs & Astuces > Linux > Commandes > Ubuntu Server

Etiquettes: 

Réseau

Paramétrer le serveur en IP fixe

Pour configurer une IP fixe, modifier le fichier /etc/network/interfaces

$ cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
 
# The loopback network interface
auto lo  Interface réseau
iface lo inet loopback
 
# The primary network interface
 
auto eth0
#Indication IP fixe
iface eth0 inet static
#Adresse IP du serveur
address 192.168.1.22
#Masque de sous-réseau
netmask 255.255.255.0
#Adresse de diffusion
broadcast 192.168.1.255
#Adresse du réseau
network 192.168.1.0
#Adresse du routeur / passerelle
gateway 192.168.1.1

Prise en compte des modifications

$ sudo /etc/init.d/networking restart

SSH

Documentation Ubuntu

Pour pouvoir administrer son serveur de n'importe quel PC de la maison, il faut obligatoirement installer le paquet SSH.

Après s'être logué au serveur, saisir la commande suivante

$ sudo apt-get install openssh-server

Le système demande alors le mot de passe du super utilisateur (mot de passe renseigné lors de l'installation) et le paquet SSH est automatiquement installé.

Installer également le client SSH

$ sudo apt-get install openssh-client

Le fichier de configuration SSH

$ sudo nano /etc/ssh/sshd_config

Paramètres à modifier dans le fichier de configuration

Port 22 port d'écoute du serveur SSH

PermitRootLogin no ne pas autoriser l'utilisateur root

PermitEmptyPasswords no ne pas autoriser les mots de passe vide

AllowUsers nom_du_user utilisateur autorisé à se connecter

PasswordAuthentication yes connexion obligatoire avec mot de passe

Pour accéder au serveur de l'extérieur du réseau interne via SSH, créer une règle de routage au niveau du routeur afin de rediriger le port 22 de l'IP externe vers le port 22 de l'IP interne du serveur.

Pour arrêter SSH

$ sudo /etc/init.d/ssh stop

Pour démarrer SSH

$ sudo /etc/init.d/ssh start

Pour redémarrer SSH

$ sudo /etc/init.d/ssh restart

Webmin

Documentation Ubuntu

Webmin est un système permettant de gérer son serveur via une interface web. C'est une bonne alternative pour les personnes n'étant pas habituées à saisir des commandes dans une console.

Le site de Webmin

Pour l'installer, rien de plus simple

  • Récupérer la clé GPG

$ sudo wget http://www.webmin.com/jcameron-key.asc
$ sudo apt-key add jcameron-key.asc

  • Editer le fichier /etc/apt/sources.list et ajouter la ligne :

$ echo "deb http://download.webmin.com/download/repository sarge contrib" >> /etc/apt/sources.list
$ echo "deb http://webmin.mirror.somersettechsolutions.co.uk/repository sarge contrib " >> /etc/apt/sources.list

Installer le paquet Webmin


$ sudo apt-get update
$ sudo apt-get install webmin

Pour accéder à l'interface de Webmin, saisir dans un navigateur :

Pour accéder au serveur de l'extérieur du réseau interne via un navigateur web, créer une règle de routage au niveau du routeur afin de rediriger le port 10000 de l'IP externe vers le port 10000 de l'IP interne du serveur.

Sur un autre ordinateur

  • https://nom_du_serveur:10000/
  • https://adresse_ip_du_serveur:10000/

En local (directement sur le serveur)

Virtualmin

Virtualmin est une sur-couche applicative pour Webmin permettant une gestion complète d'un serveur web.

Il permet une gestion multi-domaine.

Pour fonctionner, Virtualmin a besoin de diverses applications :

  • Bind
  • Postfix
  • Dovecot
  • Apache
  • Webalizer
  • MySQL ou PostgreSQL
  • ProFTPd
  • Un anti SPAM
  • Un anti virus

Si toutes ces applications ne sont pas pré-installées, le script d'installation de Virtualmin les installera automatiquement.

Virtualmin existe en version PRO et GPL.

Installer la version GPL de Virtualmin

Pour installer la version GPL de Virtualmin.

Télécharger le script d'installation :

$ wget http://software.virtualmin.com/gpl/scripts/install.sh

Rendre le script exécutable :

$ sudo chmod +x ./install.sh

Exécuter le script :

$ sudo ./install.sh

Une fois l'installation terminée, l'accès à Virtualmin se fait de la même manière que l'accès à Webmin.

Dans un navigateur, saisir l'adresse https://adresse_ip_du_serveur:10000

Utiliser le thème de Virtualmin

Utilisation du thème de Virtualmin à la place du thème de Webmin.

Dans Webmin/Virtualmin, se rendre dans la section Webmin --> Modification de la langue et du thème.

Dans la section Thème de l'interface, cocher l'option Choix personnel et choisir le thème Virtualmin Framed Theme

Création d'un serveur virtuel

Créer un nouveau serveur virtuel

La création d'un nouveau serveur virtuel entraine automatiquement la création d'un nouvel utilisateur et par conséquent la création d'un répertoire "home".

Cliquer sur "Create Virtual Server"

Dans la section "Détails du nouveau serveur virtuel"

  1. Saisir le nom de domaine concerné
  2. Saisir une description
  3. Saisir un mot de passe

Dans la section "Advanced options"

  1. Saisir une adresse mail de contact différente si besoin

Dans la section "Enabled features"

  1. Décocher les options inutiles (Ne pas décocher l'option "Créer une zone DNS")

Une fois le serveur créer, un user ainsi qu'un répertoire home a été créé.

Par exemple, pour le domaine "mondomaine.fr", un user mondomaine a été créé avec le mot de passe choisi et le répertoire "/home/mondomaineest automatiquement créé.

Si l'option "Créé un site web pour le domaine" a été cochée, un répertoire "public_html" a été créé dans le répertoire home.

C'est dans ce dossier qu'il faut placer tous les fichiers du site web.

Création d'un sous-serveur virtuel

Créer un sous-serveur virtuel

La création d'un sous-serveur est identique à la création d'un serveur mis à part qu'il faut auparavant sélectionner dans la liste des serveurs celui qui va servir de serveur parent puis cliquer sur "Create Virtual Server" et choisir le type "Sub-server".

Les sous-serveurs sont stockés dans le répertoire "/home/nom_du_domaine_parent/domains/"

Un répertoire par sous-serveur y est créé ainsi qu'un répertoire "public_html" dans lequel se trouveront tous les fichiers web du sous-serveur.

Par exemple, pour installer un webmail (RoundCube) afin de consulter ses mails à distance, il est intéressant de se créer un serveur virtuel correspondant à son domaine (monDomaine.fr) puis de créer un sous-serveur (roundcube.monDomaine.fr) permettant d'accéder à RoundCube.

Création d'un serveur proxy Apache

Création d'un serveur (web) proxy

La création d'un serveur virtuel comme serveur proxy est intéressant dans le cas où il est nécessaire d'accéder à un serveur web installé sur un serveur inaccessible de l'extérieur mais faisant partie d'un réseau dont un autre serveur est lui accessible de l'extérieur.

Par exemple :

  • Un serveur web (serveur A) accessible de l'extérieur via les ports 80 et 443.
  • Un serveur mail (serveur B) disposant d'un webmail et accessible uniquement via les ports 25, 110, 143, 465, 993 et 995 (ports utilisés pour l'envoi et la réception des mails)

Dans cette configuration, il est techniquement impossible d'accéder au webmail du serveur B via un navigateur utilisant le port 80 ou 443 pour ses requêtes.

Il existe 2 solutions :

  1. La première consistant à faire écouter le webmail du serveur B sur un port autre que le 80 et/ou 443 et d'autoriser le flux sur ce port via une règle de translation dans le routeur.
  2. La seconde solution consiste à créer sur le serveur A, via Virtualmin, un serveur ou sous-serveur virtuel, d'activer le mode proxy et d'y indiquer l'adresse ip du serveur B ainsi que le port utilisé.

La seconde solution est beaucoup plus simple à gérer et à mettre en oeuvre.

Dans Virtualmin, une fois le serveur virtuel créé, il suffit de cliquer sur "Configuration du serveur", puis sur "Edit proxy Website".
Activer le mode proxy en sélectionnant l'option "Oui" de "Proxy enabled" et d'indiquer dans la zone "Proxy to URL" l'adresse ip et le port du serveur à joindre (http://mon_autre_serveur:80/)

Et voilà, de cette manière on accède au webmail du serveur B via le port 80 en passant par un serveur web virtuel installé sur le serveur A.

Nom de domaine

Documentation Ubuntu

Afin de pouvoir accéder au serveur de n'importe où, que ce soit pour l'accès SSH ou un serveur WEB, le plus simple est de se créer un nom de domaine.

Pour cela, rien de plus simple, il suffit de se rendre sur le site www.dyndns.org, de se créer un nouveau compte afin d'obtenir un nom de domaine.

Une fois le compte créer sur dynns, il faut que le serveur fasse une mise à jour régulière de l'adresse IP fournie par le FAI afin qu'elle corresponde au nom de domaine créé précédement.

Pour cela, il suffit d'installer le paquet ddclient.

$ sudo apt-get install ddclient

Ensuite, il faut configurer ddclient avec les paramètres de connexions de dyndns.

$ sudo dpkg-reconfigure ddclient

Le fichier de configuration se trouve dans /etc/ddclient.conf

Exemple de configuration

# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf
protocol=dyndns2
use=web, web=checkip.dyndns.com, web-skip='IP Address'
server=members.dyndns.org
login=user_compte_dyndns
password='password_compte_dyndns'
nom_de_domaine_dyndns

Ensuite, le processus ddclient se charge de vérifier et de mettre à jour l'adresse IP à intervalle régulière.


Ou alors, dans le cas où l'on dispose d'une adresse IP fixe et que l'on souhaite un nom de domaine un peu plus parlant que ce que propose Dyndns, il y a,  dans ce cas, la société OVH qui propose des noms de domaines en .fr (par exemple) à moins de 10 euros TTC par an.

RAID

Pour utiliser un système RAID, il faut obligatoirement au minimum 2 disques durs.

Les 3 types de systèmes RAID les plus couramment utilisés sont le RAID0, le RAID1 et le RAID5.

Ils ont tous les 3 une manière différente de fonctionner.

Le RAID0 réparti les données sur 2 disques au minimum ce qui permet d'améliorer les performances au niveau de la lecture et de l'écriture.

Le RAID1 duplique les données en temps réel sur 2 disques au minimum ce qui permet de sécuriser les données. Si 1 des disques durs tombe en panne, le suivant prend automatiquement le relais sans aucune perte de données.

Enfin, le RAID5 combine les techniques du RAID0 et du RAID1. Nécessite au minimum 4 disques durs.

Pour installer un système RAID :

$ sudo apt-get install MDADM

Pour l'exemple, je créé un RAID1 (toutes les données seront écrites simultanément sur les 2 disques)

Toutes les opérations se font via WEBMIN

Menu Matériel --> Partition sur Disque Local

  1. Cliquer sur le premier périphérique concerné
  2. Cliquer sur Ajouter une partition primaire
  3. Sélectionner Linux RAID puis cliquer sur créer
  4. Effectuer la même opération pour le second périphérique concerné par le RAID1
  5. Redémarrer le serveur afin qu'il reconnaisse parfaitement les nouvelles partitions

Menu Matériel --> Linux RAID

  1. Sélectionner Miroir (RAID1) puis cliquer sur Créer un dispositif de RAID du niveau
  2. Dans la liste Partitions dans le RAID, sélectionner les 2 partitions crééés précédement puis cliquer sur Créer
  3. Cliquer sur le dispositif RAID créé (dans la liste des RAID existant), par exemple /dev/md0
  4. Créer le système de fichier en sélectionnant New Linux Native (ext4) dans la liste puis cliquer sur le bouton Create Filesystem of type puis sur Créer
  5. Via SSH, créer le répertoire /volume1 (par exemple sudo mkdir /volume1)
  6. Cliquer à nouveau sur le dispositif RAID créé (dans la liste des RAID existant), par exemple /dev/md0
  7. Dans la section Mount RAID on, saisir /volume1 (par exemple) puis cliquer sur Mount RAID on puis sur Créer

L'installation du RAID est terminée...

Pour controler le déroulement de la création du RAID

$ cat /proc/mdstat

Samba

Documentation Ubuntu

Un serveur de fichiers avec Samba

 

Attention !!! Au moment où j'écris ce post, surtout ne pas installer le paquet samba4.

C'est une  version beta et elle fonctionne très mal.

Le serveur n'est pas visible sur le réseau Windows et le partage d'imprimantes est très instable.

 

Installer les paquets suivants

$ sudo apt-get install samba
$ sudo apt-get install samba-common
$ sudo apt-get install samba-common-bin

Le fichier de configuration de Samba

/etc/samba/smb.conf

Paramètres à modifier

[global]

load printers = yes

printing = cups

# le nom du domaine Windows

workgroup = MSHOME

 


[printers]

create mask = 0700

comment = All Printers

printable = yes

path = /var/spool/samba


[print$]

browseable = yes

comment = Printer Drivers

path = /var/lib/samba/printers


Pour partager des dossiers

[nom du partage]

# liste des utilisateurs ayant accès au partage

valid users = user1, user2, user3, user4

# autorise l'écriture (fichiers & dossiers)

writeable = yes

# liste des utilisateurs en lecture

read list = user2, user3, user4

# liste des utilisateurs en écriture

write list = user1

# chemin vers le répertoire à partager

path = /mon_repertoire_a_partager

# activation de la corbeille

vfs object = recycle

# conservation de l'arborescence

recycle:keeptree = yes

# conservation des différentes versions de fichiers

recycle:version = yes

# nom du dossier correspondant à la corbeille

# l'option /%U créera automatiquement un dossier du nom de l'utilisateur dans la corbeille

recycle:repository = #recycle/%U


Pour partager les dossiers personnels de chaque utilisateur

[homes]

writeable = yes

browseable = no


Pour arrêter Samba

$ sudo /etc/init.d/smbd stop

Pour démarrer Samba

$ sudo /etc/init.d/smbd start

Pour redémarrer Samba

$ sudo /etc/init.d/smbd restart


Pour ajouter un utilisateur Samba

Ajouter un utilisateur au système

Voir la rubrique  Trucs & Astuces > Linux > Commandes > Utilisateurs > Ajouter un utilisateur au système

Ajouter le nouvel utilisateur système à Samba

Voir la rubrique Trucs & Astuces > Linux > Commandes > Utilisateurs > Ajouter un utilisateur Samba

Serveur FTP

Documentation Ubuntu

Pour installer sur son serveur Ubuntu un serveur FTP, il suffit d'installer PROFTPD via une console

$ sudo apt-get install proftpd

Installer PROFTPD en mode Démon Autonome

Pour configurer PROFTPD, il suffit d'éditer le fichier /etc/proftpd/proftpd.conf

Pour relancer PROFTPD après une modification de sa configuration

$ sudo /etc/init.d/proftpd restart

Pour voir qui est connecté au serveur FTP

$ ftpwho

Pour voir les statistiques du serveur FTP

$ ftpstats

Pour paramétrer la liste des utilisateurs interdits, éditer le fichier /etc/ftpusers

Pour accéder au serveur de l'extérieur du réseau interne via un client FTP, créer une règle de routage au niveau du routeur afin de rediriger le port 20 et 21 de l'IP externe vers le port 20 et 21 de l'IP interne du serveur.

Seuls les utilisateurs existant sur le système sont autorisés à se connecter au serveur via FTP.

Serveur Web

Documentation Ubuntu

Installer un serveur Web sur son serveur Ubuntu

Il existe 2 méthodes pour procéder à son installation

1 - Installer un serveur LAMP (Linux Apache MySQL PHP)

$ sudo apt-get install lamp-server^

N'oubliez pas le caractère ^ en fin de ligne.

2 - Installer les paquets Apache, MySQL et PHP individuellement

Pour accéder au serveur de l'extérieur du réseau interne via un navigateur web, créer une règle de routage au niveau du routeur afin de rediriger le port 80 et 443 de l'IP externe vers le port 80 et 443 de l'IP interne du serveur.

Apache2

Documentation Ubuntu 

 

Pour installer Apache2

$ sudo apt-get install apache2

Pour gérer Apache2

$ sudo service apache2 start/stop/restart
$ sudo /etc/init.d/apache2 start/stop/restart

Tous les fichiers de configuration de Apache2 sont dans le dossier /etc/apache2

Activer l'URL rewriting

$ sudo a2enmod rewrite

Activer le mode userdir (pour que chaque utilisateur puisse avoir son propre site web perso)

$ sudo a2enmod userdir

les différents sites sont disponibles à l'adresse http://mondomaine.com/~nom_du_user

Comment rediriger certains domaines vers certaines machines

routeur :

  • renvoi du port 80 sur la machine 192.168.0.10

machine 192.168.0.10 :

  • virtualhosts renvoyant vers plusieurs dossiers

  • toto.mondomaine.com va vers /home/toto/www

  • titi.mondomaine.com va vers /home/titi/www

  • etc…

  • et 000-default va vers /var/www

machine 192.168.0.11 :

  • virtualhosts renvoyant vers plusieurs dossiers

  • tata.mondomaine.com va vers /home/tata/www

  • tutu.mondomaine.com va vers /home/tutu/www

  • etc…

  • et 000-default va vers /var/www

Sauf que si on met http://tata.mondomaine.com dans son navigateur on se retrouve avec le site par defaut de 192.168.0.10….

Donc, il faut appeller mod_proxy a la rescousse.

sur 192.168.0.10 : activer le module proxy avec :

$ sudo a2enmod proxy

créer un virtualhost /etc/apache2/sites-available/tata.mondomaine.com qui contient :

<VirtualHost 192.168.0.10:80>
ProxyRequests Off
ProxyPreservehost on
ServerName tata.mondomaine.com
ProxyPass / http://192.168.0.11/
ProxyPassReverse / http://192.168.0.11/
<Proxy>
Order Allow,Deny
Allow from all
</Proxy>
</VirtualHost>

Activer ce fichier :

$ sudo a2ensite tata.mondomaine.com

Pour la désactivation :

$ sudo a2dissite tata.mondomaine.com

Redémarrer apache

$ sudo service apache2 restart
ou
$ sudo /etc/init.d/apache2 restart

MySQL

Documentation Ubuntu

Installer le paquet mysql-server

$ sudo apt-get install mysql-server

Pour gérer MySQL

$ sudo /etc/init.d/mysql start/stop/restart

Pour savoir si MySQL est lancé

$ sudo /etc/init.d/mysql status

Pour recharger la configuration de MySQL

$ sudo /etc/init.d/mysql reload

Pour forcer la prise en compte de la nouvelle configuration

$ sudo /etc/init.d/mysql force-reload

Pour l'utilisation de MySQL, se reporter à la section Trucs & Astuces > MySQL

PHP

Documentation Ubuntu

Liste des différents paquets PHP5 à installer :

$ sudo apt-get install php5
$ sudo apt-get install php5-cgi
$ sudo apt-get install php5-cli
$ sudo apt-get install php5-gd
$ sudo apt-get install php5-mcrypt
$ sudo apt-get install php5-mysql
$ sudo apt-get install php5-sasl

Pour l'utilisation de PHP, voir la rubrique Trucs & Astuces > PHP

PHPMyAdmin

Documentation Ubuntu

phpMyAdmin permet de gérer le serveur MySQL via une interface web écrite en PHP.

Pour l'installer :

$ sudo apt-get install phpmyadmin

Pour se connecter à phpMyAdmin :

  • http://adresse_ip_du_serveur/phpmyadmin

Fail2ban

Documentation Ubuntu

fail2ban est un application qui permet de protéger le serveur contre les intrusions.

fail2ban lit les logs de divers serveurs (SSH, Apache, FTP…) à la recherche d'erreurs d'authentification répétées et ajoute une règle iptables pour bannir l'adresse IP de la source.

Pour l'installer :

$ sudo apt-get install fail2ban

Configuration :

Pour spécifier à fail2ban quels services il doit surveiller, éditez le fichier /etc/fail2ban/jail.conf

# Exemple de configuration

ignoreip = 127.0.0.1 192.168.1.20 192.168.1.21 # liste des adresses ip à ignorer

bantime = 86400 # temps en secondes

maxretry = 3 # nombre de tentative échouées

destemail = toto@serveur

Pour activer un service mettre à true la valeur enabled

Pour rajouter un service, par exemple pour webmin :

[webmin-auth]

enabled  = true
port     = https
filter   = webmin-auth
logpath  = /var/log/auth.log
maxretry = 3

Pour rajouter la règle anti-w00tw00t :

[apache-w00tw00t]
enabled = true
filter = apache-w00tw00t
action = iptables[name=Apache-w00tw00t,port=80,protocol=tcp]
logpath = /var/log/apache2/*access.log
maxretry = 1

Créer ensuite le fichier /etc/fail2ban/filter.d/apache-w00tw00t.conf et y ajouter :

[Definition]
failregex = ^<HOST> -.*"GET \/w00tw00t\.at\.ISC\.SANS\.DFind\:\).*".*
ignoreregex =

A chaque service est associé un fichier de configuration dans le dossier /etc/fail2ban/filter.d

Pour tester un filtre :

Par exemple le filtre anti-w00tw00t

$ fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/apache-w00tw00t.conf

Pour relancer la configuration :

$ sudo fail2ban-client reload

Pour vérifier si les prisons sont correctement lançées :

$ sudo fail2ban-client status

Pour voir la liste des adresses IP bloquées par fail2ban :

$ sudo iptables -L

ou pour un jail en particulier (postfix par exemple):

$ sudo iptables -L fail2ban-postfix

avec les lignes numérotées :

$ sudo iptables -L fail2ban-postfix --line-numbers

Pour supprimer une adresses IP de la liste des adresses bloquées par fail2ban

Par exemple, pour supprimer l'adresse IP 192.168.1.2 bloquée par le service fail2ban-ssh

$ sudo iptables -D fail2ban-ssh -s 192.168.1.2 -j DROP

ou avec son numéro obtenu grâce à la commande iptables -L --line-numbers

$ sudo iptables -D fail2ban-ssh 5

Pour ajouter une adresse IP à la liste des adresses bloquées (pour le jail postfix par exemple)

$ iptables -I fail2ban-postfix 1 -s 1.163.149.165 -j DROP

ou une plage d'adresses

$ iptables -I fail2ban-postfix 1 -s 118.160.0.0/16 -j DROP

Sauvegarde

La sauvegarde est une des parties la plus importante.

Rien n'est plus désastreux que de perdre ses données personnelles et/ou la configuration du serveur, surtout après y avoir passé des heures, voir des jours, voir des semaines ...

Mise en place des sauvegardes

Sauvegarder la liste des sources et des paquets

Restaurer la liste des sources et des paquets

Sauvegarder également les éléments suivants

  • Tous les fichiers de configuration se trouvant dans /etc (/etc/samba/smbd.conf par exemple)
  • Tous les fichiers variables se trouvant dans /var (/var/www /var/mail par exemple)
  • Tout le contenu du /home
  • Tous les répertoires créés spécifiquement (pour Samba par exemple)

2 types de sauvegardes peuvent être mis en place :

  1. La sauvegarde par synchronisation
  2. La sauvegarde différentielle

Créer une tâche cron (root) afin d'exécuter ces scripts de sauvegardes à intervalles régulières.

Pour plus de sécurité, les 2 types de sauvegarde peuvent être mis en place en parallèle

Serveur de messagerie (mail)

Documentation Ubuntu

L'installation et la configuration d'un serveur mail étant, à mon avis, très complexe, je conseil d'installler et d'utiliser un serveur de messagerie comme Zimbra.

Zimbra inclut tous les outils nécessaires au bon fonctionnement d'un serveur de messagerie comme l'anti spam, l'anti virus, serveur POP SMTP IMAP, système de filtrage, webmail etc etc ...

En effet, il faut être très prudent quand on désire installer un serveur de messagerie car s'il est mal configuré et/ou mal protégé, il peut servir de serveur relais pour l'envoi de spam en très grande quantité.

Mon propre serveur de messagerie Zimbra est régulièrement sollicité pour servir de serveur relais
et plus de 20 000 messages sont automatiquement supprimés chaque semaine grâce aux filtres intégrés à Zimbra.

Voir Trucs & Astuces > Zimbra

 

Postfix

Fetchmail

En cours

Procmail

Dovecot

Roundcube

Serveur de messagerie instantanée

Installation du serveur

Documentation Ubuntu

Installation d'une messagerie instantanée avec JABBERD2

 

Installation de jabberd2

$ sudo apt-get update
$ sudo apt-get install jabberd2

Pour l'utilisation de jabberd2, il faut obligatoirement un serveur de base de données (postgre, mysql, oracle ou sqlite) afin de conserver toutes les données utilisateurs.

Installation de la base de données MySQL

Le script d'installation de la base MySQL se trouve dans le répertoire /usr/share/doc/jabberd2

Pour l'installer :

Extraction du fichier

$ gzip -d /usr/share/doc/jabberd2/db-setup.mysql.gz

Connexion à MySQL

$ mysql -u root -p

Une fois connecté à MySQL

$ .
> \. db-setup.mysql
> GRANT select,insert,delete,update ON jabberd2.* to jabberd2@localhost IDENTIFIED by 'secret';

Remplacer secret par le mot de passe désiré

Configuration de jabberd2

Toutes les modifications sont à faire dans les fichiers sm.xml & c2s.xml dans /etc/jabberd2

Nom de domaine du serveur jabberd2

Renseigner le champ id de la section sm du fichier sm.xml

Renseigner le champ id de la section local du fichier c2s.xml

Paramètres de connexions à MySQL

Renseigner le champ dbname,    user et pass de la section mysql du fichier sm.xml

Renseigner le champ dbname, user et pass de la section mysql du fichier c2s.xml

Pour accéder au serveur de l'extérieur du réseau interne via un client de messagerie instantanée, créer une règle de routage au niveau du routeur afin de rediriger le port 5222 de l'IP externe vers le port 5222 de l'IP interne du serveur.

Installation du client

Logo Pidgin

Documentation Ubuntu

PIDGIN, client de messagerie universel

Le site de Pidgin

Installation pour Ubuntu :

$ sudo apt-get update
$ sudo apt-get install pidgin

Infos supplémentaires concernant l'installation de Pidgin sur Ubuntu

Installation pour Windows :

Télécharger Pidgin

Configuration de pidgin :

A l'ouverture de pidgin

ecran 1

Cliquer sur le bouton Ajouter

ecran 2

Dans la liste Protocole, sélectionner XMPP

ecran 3

Remplir les champs :

  1. Utilisateur
  2. Domaine
  3. Mot de passe
  4. Alias local

Cocher les cases :

  1. Mémoriser le mot de passe
  2. Avertir des nouveaux courriers
  3. Utiliser cette icone pour ce compte
  4. Créer ce nouveau compte sur le serveur

ecran 4

Dans l'onglet Avancé :

  1. Décocher Nécessite SSL/TLS
  2. Cocher Autoriser l'authentification en clair pour les flux cryptés
  3. Indiquer l'adresse du serveur de connexion
  4. Enfin, cliquer sur Ajouter

ecran 5

La configuration est terminée

Serveur d'impression

CUPS

Documentation Ubuntu

Un serveur d'impression est très utile pour pouvoir partager des imprimantes sur plusieurs postes clients.

Pour cela rien de plus simple.

Installer les paquets :

$ sudo apt-get install cupsys
$ sudo apt-get install cupsys-client

Pour pouvoir installer de nouvelles imprimantes, votre user doit faire partie du groupe lpadmin

$ sudo adduser $USER lpadmin

Redémarrer le serveur cups

$ sudo /etc/init.d/cupsys restart


Pour installer de nouvelles imprimantes, le serveur CUPS dispose d'une interface web accessible à l'adresse http://localhost:631

CUPS

Pour accéder à cette interface de n'importe quelle machine du réseau, il est indispensable de modifier le fichier /etc/cups/cupsd.conf

Commenter la ligne "Listen localhost:631" et ajouter "Port 631"

Entre les balises <Location /> </Location>, ajouter la ligne

  • Allow 192.168.1.* (pour autoriser toutes les machines ayant une adresse IP commencant par 192.168.1.)

$ nano /etc/cups/cupsd.conf
...
# Only listen for connections from the local machine.
# Listen localhost:631
Port 631

Listen /var/run/cups/cups.sock
...
# Restrict access to the server...
<Location />
  Order allow,deny
  Allow 192.168.1.0/24
</Location>
 
# Restrict access to the admin pages...
<Location /admin>
  Order allow,deny
  Allow 192.168.1.0/24
</Location>
...
^o (enregistrer)
^x (quitter)
$

Installation des imprimantes sur les postes clients

Sur les postes Windows :

  1. Choisir l'option d'installation d'une imprimante réseau
  2. Saisir l'adresse http de l'imprimante sans oublier de préciser le port 631 (http://adress-ip-du-serveur:631/printers/nom-de-l-imprimante)
  3. Valider puis choisir comme pilote d'impression le pilote générique MS Publisher Color Printer (ou le pilote de l'imprimante si disponible)

CUPS-PDF

Documentation Ubuntu

Pré-requis : avoir installé un serveur d'impression CUPS

Pour installer une imprimante virtuelle PDF :

$ sudo apt-get install cups-pdf (installation de l'imprimante PDF virtuelle)
$ sudo /etc/init.d/cups restart (redémarrage du serveur CUPS)
$ sudo aa-complain cupsd (donne les droits à l'imprimante PDF d'écrire les fichiers dans le réperoire PDF de chaque HOME)

Créer dans tous les répertoires "home" un dossier PDF

Pour tester le bon fonctionnement :

Si besoin :

$ sudo apt-get install lpr

Puis :

$ lpr -P PDF /nom_du_document_a_imprimer

Un document au format PDF devrait être généré dans ~/PDF

Ubuntu Server: Upgrade

Pour mettre à jour Ubuntu Server vers une version majeur, 14.04 vers 16.04 par exemple, il suffit d'exécuter les commandes suivantes:

warningNe pas oublier de faire des sauvegardes avant d'exécuter les commandes suivantes en cas d'erreurs pendant la procédure de mise à jour.

# sudo apt-get install update-manager-core
# sudo do-release-upgrade

infoPendant le procédure de mise à jour, bien faire attention aux différents messages.
Avant d'écraser les fichiers de configurations avec les nouveaux fichiers provenant des mises à jour, vérifier les modifications apportées à l'aide de la commande 'd' (difference).
De toute manière, une copie de l'ancien ou du nouveau fichier (en fonction du choix effectué - conserver l'ancien fichier ou l'écraser avec le nouveau), est automatiquement sauvegardée au même emplacement.

Après l'opération d'upgrade, il est peut-être nécessaire d'effectuer les mises à jour du nouvel OS:

# apt-get update
# apt-get -y dist-upgrade
# apt-get -y autoremove
# apt-get autoclean
# apt-get clean

Dans certains cas, il se peut que certaines mises à jour ne se fassent pas à cause d'un paquet corrompu, dans ce cas:

Exemple d'erreur: dpkg: error processing <file.deb> (–unpack):

# dpkg -i --force-overwrite <file.deb>
# apt-get -f install

 

Windows

logo windows

Tous mes trucs et astuces pour utiliser Windows

Seven

Une question ?

Etiquettes: 

Lancer la console en mode admin

  1. Cliquer sur le bouton Démarrer
     
  2. Saisir CMD dans la zone de recherche
     
  3. Valider en appuyant simultanément sur Ctrl, Maj et Entrée

Mot de passe administrateur perdu

Pour réinitialiser le mot de passe d'un compte administrateur de Windows 7 :

  1. Démarrer le PC avec un live CD Ubuntu
     
  2. Dans Ubuntu, ouvrir le poste de travail (Raccourcis --> Poste de travail)
     
  3. Ouvrir le lecteur contenant l'installation de Windows (double clic sur l'icone correspondant)
     
  4. Ouvrir le dossier Windows puis System32
     
  5. Rechercher dans ce dossier le fichier Utilman.exe et le renommer en Utilman.exe.bak
     
  6. Rechercher dans ce dossier le fichier cmd.exe et le renommer en Utilman.exe
     
  7. Redémarrer le PC en suivant la procédure de redémarrage de Ubuntu
     
  8. Une fois le PC redémarré, sur l'écran d'ouverture de session de Windows 7, cliquer sur le bouton en bas à gauche correspondant aux Options d'ergonomie
     
  9. L'invite de commande s'ouvre grâce à la manipulation effectuée ci-dessus
     
  10. Saisir la commande suivante et valider par entrée : net user administrateur /active:yes (Activation du compte super admin)
     
  11. Fermer l'invite de commande et redémarrer le PC
     
  12. Ouvrir une session en cliquant sur l'icone du compte Administrateur
     
  13. Ouvrir le Panneau de configuration puis cliquer sur l'icone Comptes d'utilisateurs
     
  14. Cliquer sur Gérer un autre compte
     
  15. Cliquer sur le compte à modifier puis sur Modifier le mot de passe
     
  16. Saisir le nouveau mot de passe puis cliquer sur Modifier le mot de passe
     
  17. Fermer la session Administrateur puis cliquer à nouveau sur le bouton Options d'ergonomie
     
  18. Dans l'invite de commande, saisir la commande suivante et valider par entrée : net user administrateur /active:no (Désactivation du compte super admin)
     
  19. Répéter les étapes 1 à 7 afin de renommer le fichier Utilman.exe en cmd.exe puis Utilman.exe.bak en Utilman.exe

XP

Une question ?

Créer un service

Pour créer un service Windows, ouvir une console (cmd.exe) et exécuter la commande suivante :

sc create NomDuService binPath= "x:\chemin\de\l\executable.exe"

Attention, il y a un espace à cet endroit binPath=[espace]"x:\chemin\de\l\executable.exe".
Il ne faut surtout pas l'oublier.

Etiquettes: 

Restaurer le MBR

Au secour !!!

J'ai voulu tester Ubuntu 10.04 sans me prendre la tête en l'installant directement sous Windows XP via Wubi.

Installation effectuée sans aucun problème.

Redémarrage du système.

Miracle, au démarrage, un nouveau menu me propose de lancer soit Windows soit Ubuntu.

Je sélectionne Ubuntu.

Ubuntu se lance. Tout fonctionne impeccablement.

Ubuntu me propose des MAJ que j'autorise.

Une fois les MAJ terminées, je redémarre le système et PAN ! voilà que le système est planté. L'ordinateur ne veut plus lancer aucun des 2 systèmes. Ni Windows XP, ni Ubuntu.

Erreur : Grub rescue >

Aie aie aie. Que faire ?

Petite explication :

Quand on installe Ubuntu 10.04 sous Windows via Wubi, l'installation modifie le MBR de Windows en y ajoutant une option afin de lancer Ubuntu. Modification identique à celle effectuée quand on installe 2 systèmes Windows sur une même machine.

Malheureusement, avec Ubuntu 10.04, quand on procède aux MAJ, celle-ci modifie le programme GRUB permettant à Ubuntu de démarrer sauf que dans le cas où Ubuntu 10.04 est installé via Wubi le programme Grub n'est pas installé car c'est le MBR de Windows XP qui gère les démarrages.

Pour régler le problème il faut procéder à la réparation du MBR.

La première solution consiste à effectuer cette réparation via le CD d'installation de Windows XP mais cette solution fonctionne uniquement dans le cas où le disque dur où Windows est installé est au format IDE. Si le disque dur est en S-ATA, les choses se compliquent car le programme de récupération ne reconnait pas ces disques. Il faut installer un pilote supplémentaire.

La solution la plus simple consiste à utiliser un live CD Ubuntu ou n'importe quelle autre distribution Linux.

Procédure de restauration du MBR

  1. Télécharger le fichier mbrxp.img (wget -O mbrxp.img http://www.isbeta.fr/cce9e)
  2. Copier ce fichier sur une clé USB
  3. Démarrer la machine via un live CD
  4. Sous Ubuntu, ouvrir une console et créer sur le bureau un dossier "usb" ($ sudo mkdir ~/Desktop/usb)
  5. Toujours dans la console, afficher la liste des disques durs et clé usb ($ sudo fdisk -l)
  6. Repérer le nom du fichier correspondant à la clé usb. Dans mon cas, la clé usb correspond à /dev/sdb et ma partition à /dev/sdb1 et mon disque dur correspond au fichier /dev/sda
  7. Monter la clé usb dans le dossier créé précédement ($ sudo mount /dev/sdb1 ~/Desktop/usb)
  8. Restaurer le MBR en tapant dans la console : $ sudo dd if=~/Desktop/usb/mbrxp.img of=/dev/sda bs=446 count=1
  9. Vérifier que la partition de Windows XP est bien celle de démarrage avec gparted ($ sudo gparted)
  10. Dans gparted, en haut à droite, sélectionner le disque dur concerné.
  11. Faire un clic droit sur la partition concernée et cliquer sur "Manage Flags".
  12. Dans la liste, cocher "Boot" puis cliquer sur "Close".
  13. Valider le changement en cliquant sur "Apply" (Icone en forme de coche verte dans la barre d'outil)
  14. Redémarrer le système et si tout a bien fonctionné, le menu de démarrage de Windows XP est à nouveau opérationnel.
Etiquettes: 

Restaurer le raccourci "Afficher le bureau"

Pour restaurer le raccourci "Afficher le bureau" exécuter les opérations suivantes :

  1. Cliquer sur Démarrer puis sur Exécuter
  2. Saisir regsvr32 /n /i:U shell32.dll
  3. Valider en cliquant sur Ok
  4. Redémarrer l'ordinateur

Après le redémarrage, l'icone du raccourci devrait réapparaitre dans la barre des taches.

Etiquettes: 

Supprimer un service

Pour supprimer un service Windows sans désinstaller le programme complet :

  1. Ouvrir la fenêtre des services Windows (Démarrer → Exécuter → Saisir services.msc puis OK)
  2. Rechercher dans la liste le service à supprimer et noter son nom exact (Double-clic sur le service puis prendre la valeur de "Nom du service :")
  3. Ouvrir une console Windows (Démarrer → Exécuter → Saisir cmd puis OK)
  4. Dans cette console, saisir :
    C:\>sc delete nom_du_service

 

Toutes versions

Etiquettes: 

Accéder à des partitions Ext2/3/4 sous Windows

Les systèmes de fichiers Ext2, Ext3 et Ext4 sont propres aux systèmes Linux.
Nativement, Windows est incapable d'accéder, ne serait-ce qu'en lecture, à ce genre de système de fichiers.
Toutefois, il est possible d'y accéder en lecture/écriture à partir de Windows à l'aide d'un petit soft complémentaire Ext2IFS.

Disponible en téléchargement ici.

MD5SUM : a16c60a79b2e735dbdbb0dcb115aba99  Ext2IFS_1_11a.exe

Ce soft permet donc à Windows d'accéder aux partitions Ext2/3/4 d'un disque dur externe mais aussi interne.

Très pratique dans le cas d'un disque dur interne dont une partition est commune à Windows et à Linux.

Par exemple dans le cas d'une installation en dualboot :

  • Une partition (NTFS) réservée au système Windows.
  • Une partition (EXT4) réservée au système Linux.
  • Une partition (EXT4) réservée aux documents personnels et partagée entre Windows et Linux afin d'avoir ses documents à disposition sous n'importe quel OS. Cette partition sera montée dans le système de fichier Linux sous le point de montage /home (par exemple). Sous Windows, cette partition sera accessible grâce au soft Ext2IFS. Sous Windows, les liens "Mes documents", "Ma musique", "Mes images" etc etc ... pourront pointer vers les différents répertoires de cette partition.

Connexion Bureau à Distance - erreur de package de sécurité

J'ai cherché pendant un moment la cause de cette erreur en vain jusqu'au jour où, enfin, la solution est apparue.

Lors d'une tentative de connexion bureau à distance à l'aide d'un raccourci enregistré, j'avais cette erreur - erreur de package de sécurité -

Cela concerne uniquement les connexions RDP via un serveur de passerelle bureau à distance.

Pour corriger cette anomalie, il suffit de supprimer les informations d'identification enregistrées au niveau du serveur de passerelle Bureau à distance.

Ouvrir en modification le raccourci RDP

Onglet "Avancé" de la connexion Bureau à Distance

Cliquer sur le bouton "Paramètres"

Dans la section "Paramètres d'ouverture de session", cliquer sur le lien "supprimer"

Confirmer en cliquant sur le bouton "Oui"

Puis valider en cliquant sur le bouton "OK"

Enfin, sur l'onglet "Général"

Cliquer sur le bouton "Enregistrer" puis sur le bouton "Connexion"

Saisir le mot de passe de connexion et surtout, ne pas cocher la case "Mémoriser mes informations"

Et voilà, le problème est définitivement réglé

Par contre, obligation de saisir le mot de passe à chaque connexion.

Etiquettes: 

La commande NETSTAT

La commande netstat permet d'afficher les statistiques de protocole et des connexions réseau TCP/IP actives sur la machine.

Afficher toutes les connexions et les ports d'écoute actifs :

PS C:\> netstat -a

Afficher les fichiers exécutables à l'origine des connexions ou des ports d'écoute :

PS C:\> netstat -b

Couplée avec l'option -v, cela permet d'actualiser la liste automatiquement toutes les n secondes.

PS C:\> netstat -b -v 5

Afficher les statistiques ethernet :

PS C:\> netstat -e

Afficher les adresses et les numéros de ports :

PS C:\> netstat -n

Afficher le PID du processus associé à chaque connexion :

PS C:\> netstat -o

Afficher les connexions par protocole :

PS C:\> netstat -p protocole

protocole = TCP, UDP, TCPv6 ou UDPv6

Afficher la table de routage :

PS C:\> netstat -r

Afficher les statistiques par protocole :

PS C:\> netstat -s

 

Etiquettes: 

PowerShell: Envoyer un mail

Dans PowerShell sous Windows, il existe une commande Send-MailMessage qui permet, comme son nom l'indique, d'envoyer un mail.

Un petit man nous informe sur la manière d'utiliser cette commande:

PS C:\Users\xxx> man Send-MailMessage

NOM
    Send-MailMessage

SYNTAXE
    Send-MailMessage [-To] <string[]> [-Subject] <string> [[-Body] <string>] [[-SmtpServer] <string>] -From <string>
    [-Attachments <string[]>] [-Bcc <string[]>] [-BodyAsHtml] [-Encoding <Encoding>] [-Cc <string[]>]
    [-DeliveryNotificationOption <DeliveryNotificationOptions> {None | OnSuccess | OnFailure | Delay | Never}]
    [-Priority <MailPriority> {Normal | Low | High}] [-Credential <pscredential>] [-UseSsl] [-Port <int>]
    [<CommonParameters>]


ALIAS
    Aucun(e)

Donc, pour tester un serveur SMTP ou planifier un envoi de mail automatique:

PS C:\Users\xxx> Send-MailMessage -to moi@toto.fr -subject test -body coucou -smtpserver smtp.tutu.com -from noreply@tutu.com
PS C:\Users\xxx>

Simple et efficace.

Retrouver les mots de passe des messageries instantanées

MessenPass

MessenPass est un logiciel pour Windows permettant de retrouver les mots de passe de presque tous les logiciels de messageries instantanées.

http://www.nirsoft.net/utils/mspass.html

Tous ces logiciels sont pris en charge :

  • MSN Messenger
  • Windows Messenger (In Windows XP)
  • Windows Live Messenger (In Windows XP/Vista/7)
  • Yahoo Messenger (Versions 5.x and 6.x)
  • Google Talk
  • ICQ Lite 4.x/5.x/2003
  • AOL Instant Messenger v4.6 or below, AIM 6.x, and AIM Pro.
  • Trillian
  • Trillian Astra
  • Miranda
  • GAIM/Pidgin
  • MySpace IM
  • PaltalkScene
  • Digsby

MessenPass

Cliquer ici pour le télécharger

Windows: Connaitre sa version exacte

Pour connaitre la version exacte de Windows, il suffit d'ouvrir une fenêtre PowerShell et de saisir la commande suivante:

PS C:\> systeminfo | findstr Nom
Nom de l'hôte:                              XXX-DFRG01
Nom du système d'exploitation:              Microsoft Windows Server 2008 Standard

Et c'est valable pour toutes les versions

PS C:\> systeminfo | findstr Nom
Nom de l'hôte:                              RFT667
Nom du système d'exploitation:              Microsoft Windows 10 Professionnel