Introduction aux scripts shell

De Ensiwiki
Aller à : navigation, rechercher

Cette page traite des scripts shell. Elle a pour objectif de donner, de manière concise, des outils permettant de faire beaucoup de tâches courantes dans un environnement UNIX. Le seul prérequis est le stage UNIX de première année suivi d'une certaine pratique du shell, cette page s'adresse à des utilisateurs UNIX niveau débutant à intermédiaire.

Fichier script

Description

Un script shell est un ensemble de commandes shell rassemblées dans un fichier. L'extension de ce fichier est généralement en '.sh' . Les commandes peuvent se suivre comme si on les tapait successivement dans le terminal, il est également possible de faire des fonctions afin de rendre les scripts plus évolués. Les scripts shell sont souvent utilisés pour automatiser des manipulations sur des fichiers et dossiers : édition, suppression, modifications. On peut par exemple écrire des scripts qui génèrent des pages HTML de manière automatique.

En tête

Par convention (et je vous recommande très vivement de la respecter), on définit le langage shell utilisé sur la première ligne du script. Ainsi, un script en Bourne shell commencera par :

#!/bin/sh

En pratique, on pourra rencontrer des scripts commençant par

#!/bin/bash 
#!/bin/ksh
etc.

pour d'autres shell UNIX. Dans la suite, nous utiliserons le Bourne shell.

Commentaires

Le script est un langage de programmation à part entière et certaines lignes d'instructions font parfois beaucoup de choses. Les commentaires sont alors d'autant plus importants pour pouvoir se relire et être relu. Dans un script Shell les commentaires sur une ligne commencent par le caractère '#'

# ceci est un commentaire sur une ligne

Execution

Pour pouvoir rendre un script exécutable, c'est à dire pouvoir le lancer en tapant dans un terminal la commande

./script.sh 

il faut le rendre exécutable.

Pour cela, il faut modifier les droits de ce fichier de la manière suivante :

chmox +x script.sh

Les variables

Création

Le shell donne la possibilité de créer des variables, comme dans tous les langages de programmation. Contrairement aux langages compilés comme l'Ada, le C/C++, Java, etc. ... les variables n'ont pas besoin d'être déclarées avant leur utilisation et ne sont pas typées. En fait les variables en shell sont toutes de type 'chaine de caractère'. En général dans un script shell, il est d'usage d'écrire les variables en majuscules afin de faciliter la lecture

Remarque : les variables Shell sont globales et donc restent accessible partout dans un script

MESSAGE="hello world"

Ceci est la variable MESSAGE dans laquelle on a stocké la chaine de caractère hello world. Notez qu'il n'y a pas d'espaces des deux côtés du signe égal, cela ne fonctionnerait pas s'il y en avait. Le shell est assez pointilleux sur ce point contrairement à la plupart des langages compilés cités plus haut.

Utilisation

Pour récupérer accéder à la valeur d'une variable, il faut faire précéder son nom du caractère $, le tout entoure de guillemets. Par exemple, pour afficher le contenu de la variable MESSAGE en console (en utilisant la commande echo)

echo "$MESSAGE"

Remarque : Les guillemets ne sont pas toujours obligatoires dans la mesure où si la variable ne contient pas d'espaces, ça marchera sans : par exemple si on est absolument certain que la variable que l'on utilise représente un nombre. Avec les guillemets, cela marchera tout le temps, même si cela alourdit le code.

Par exemple, un cas ou les guillemets sont obligatoires : supposons que l'on parse une page HTML ligne a ligne et que l'on veut détecter des lignes de tableau commençant donc par : <TR><TD><A

Dans la variable DEBUT on met le debut de la ligne de code HTML.

On fait par exemple le test suivant:

if [ $DEBUT = "<TR><TD><A" ]
then 
       #code
fi

Si celle ci commence par <img src=" ce la ne fonctionnera pas à cause du caractère <.

Par contre le code suivant marchera

if [ "$DEBUT" = "<TR><TD><A" ]
then 
       #code
fi

La présence de caractères spéciaux pour le Shell dans les variables (comme les espaces) implique l'utilisation des guillemets. De manière générale, il est conseillé de les mettre, même si cela détériore la lisibilité du fichier

Concaténations

Les variables étant toutes du type chaine de caractère, il est possible de les concaténer, ou de les compléter simplement en les écrivant côte à côte. Par exemple :

PARTIE_1="hello"
PARTIE_2="world"
MESSAGE="$PARTIE_1""$PARTIE_2"
# ou encore 
MESSAGE="${PARTIE_1}${PARTIE_2}"
# les accolades servent à délimiter le nom des 
# variables de manière à ce qu'il n'y ait pas d'ambiguités 
# c'est surtout utile dans le cas suivant
MESSAGE="${PARTIE_1}world"
# qui produit exactement le même résultat que la commande précédente

Affectation du résultat d'une commande

Les résultats de commande : par exemple ls sont des chaines de caractères et il est donc possible de les stocker dans une variable.

De manière générale, cela se fait de la manière suivante:

VARIABLE=$(commande)
# ou 
VARIABLE=`commande`

Par exemple :

CONTENU=$(ls)
# ou
CONTENU=`ls`

Fonctions

Il est possible de faire des fonctions dans les scripts Shells. Ces dernières présentent les mêmes intérêts que dans le cas de la programmation classique en langages compilés : éviter les répétitions de code. Les fonctions peuvent prendre des paramètres mais a l'inverse des langages compilés cela se fait de manière invisible sans même que l'on ait a déclarer quoi que ce soit, ce qui peut se révéler déroutant au premier abord. Voici comment déclarer une fonction :

ma_fonction()
{

    # ecrire du code ici

}

Le code peut utiliser les variables $1, $2, ... , $9 qui sont les paramètres passés a la fonction. Quel que soit le nombre de paramètres utilisés, l'en-tête de la fonction reste inchangé. Pour appeler une fonction on écrit son nom suivi de la suite des paramètres qu'on lui passe, séparés par des espaces

ma_fonction PARAM_1 PARAM_2 PARAM_3 PARAM_4 PARAM_5 PARAM_6 PARAM_7 PARAM_8 PARAM_9

Par exemple, la fonction suivante

dire_bonjour()
{
     echo "Bonjour"
     echo "comment allez vous ?"
}

Aura pour effet d'écrire dans la console :

>Bonjour
>comment allez vous ?

c'est à dire remplacer l'écriture des deux echo. Cette fonction est sans paramètres et est appelée comme ceci

dire_bonjour

Un exemple de fonction avec 2 paramètres

# paramètres
# <PREMIERE_CHOSE_A_DIRE>................$1
# <DEUXIEME_CHOSE_A_DIRE>................$2
# utilisation :
#              dire "$PREMIERE_CHOSE_A_DIRE" "$DEUXIEME_CHOSE_A_DIRE"
# où PREMIERE_CHOSE_A_DIRE et DEUXIEME_CHOSE_A_DIRE sont des variables
# ou des chaines de caractères
dire()
{
     echo "Bonjour, j'ai ceci a vous dire :"
     echo "premièrement: $1"
     echo "et deuxièmement: $2"
     echo "merci de votre attention"
}

Comme les paramètres ne sont jamais indiqués dans le prototype de la fonction, il est conseillé de les mettre en commentaire avant la fonction, comme ci dessus et ce, afin de rendre compréhension, relecture et débuggage possibles.

Utilisation :

PREM="hello"
DEUX="world"
dire "$PREM" "$DEUX"
# ou encore pour le même résultat :
dire "$PREM" "world"

Commandes de base

Les pipes

Le pipe est un outil qui permet d'aligner des commandes Shell. Il est donc assez difficile de bien les expliquer sans connaitre de commandes. Cependant, dans la liste de commandes suivantes, il était très intéressant parfois de montrer quelques combinaisons assez pratiques. Donc si vous ne connaissez pas les commandes utilisées dans cette section, il est conseillé de poursuivre en ignorant les partie/lignes contenant le caractère |.

Le pipe donc permet de faire correspondre la sortie d'une commande à l'entrée d'une autre. Une commande Shell peut être constituée d'autant de pipes que l'on veut.

Un pipe, avec deux commandes au total (ce qui est le minimum), s'écrit de la manière suivante

commande1 | commande2

Au fur et a mesure que la commande 1 produit son résultat, celui ci est interprété par la commande 2. Bon c'est un détail mais il fait que l'exécution est rapide.

L'exemple le plus utilisé est de faire un pipe avec grep en récepteur (piper avec grep dira t-on ...)

Par exemple :

ls | grep "un_bout_de_nom_de_fichier"

grep ira chercher dans le résultat de ls une chaine de caractères avec un_bout_de_nom_de_fichier dedans

Les possibilités offertes par les pipes sont grandes :

par exemple :

ls -l | cut -b2
ls -l | sed -e s/"user"/"hello user"/
...

La première commande renvoie la liste du deuxième caractère du résultat de la commande ls -l et la seconde ligne de commande remplace pour chaque ligne du résultat de la commande ls -l la chaîne de caractère user par la chaîne hello user Par contre, on peut imaginer la commande suivante pour supprimer tous les fichiers dont le nom contient truc

ls | grep "truc" | rm

En fait cela ne marchera pas, car rm n'est pas fait pour lire un flux de données.

Pour que cela marche, il faut dire a rm que le résultat de la commande qui le précède passe dans ses paramètres.

Le mot clé pour cela est xargs

ainsi

ls | grep "truc" | xargs rm

fonctionnera.

De même, imaginons que l'on veuille chercher une chaine de caractères précise dans tous les fichier d'un répertoire :

en supposant qu'il n'y a que des fichiers dans le répertoire courant, la commande

ls | grep "chaine de caracteres a chercher"

ne fera pas ce que l'on veut : elle va chercher la chaine de caractères dans le résultat de la commande ls

Ici, c'est xargs qui résoud le problème :

ls | xargs grep "chaine de caracteres a chercher"

Cela n'est certes pas facile à comprendre du premier coup, (ni à expliquer clairement d'ailleurs). Il faut essayer et tâtonner un petit peu, après cela devient quasiment naturel de savoir si l'on met xargs ou pas.

En pratique, si la commande cible du pipe (ie celle qui est a droite) est capable de lire un fichier : cut, sed, grep ... , ce n'est pas la peine de mettre xargs mais il y a des exceptions (cf l'exemple précédent)

Redirections

Il est possible de rediriger les sorties des commandes vers des fichiers :

Un simple chevron

>

pour mettre la sortie de la commande dans un fichier

Ex:

ls -l > contenu.txt

Doubles chevrons pour ne pas écraser le fichier si il existe déjà et mettre le résultat de la commande a la suite de ce qu'il y a déjà dans le fichier.

Les erreurs éventuellement levées par les commandes ne sont pas considérées comme une sortie standard. Les simple et doubles chevrons ne les affectent donc pas. Pour rediriger la sortie d'erreur, l'opérateur est

2>

Il est possible de combiner > et 2> pour mettre la sortie d'erreur dans un autre fichier que la sortie standard, avec une commande ou un script Shell:

commande > output.txt 2> err.txt
# peut se faire avec des scripts (qui sont aussi des commandes)
script.sh > output.txt 2> err.txt

Avec le shell bash, on peut mettre les deux sorties dans le même fichier avec l'opérateur

&>

(attention, cette syntaxe n'est pas POSIX et ne marchera pas avec tous les shells, typiquement pas avec le /bin/sh de Debian et Ubuntu)

cat

Plusieurs utilisations : affichage, édition et concaténation de fichiers

Sert a afficher le contenu d'un fichier appelé ainsi

cat fichier.txt

assez utile, cat (en version GNU) permet de numéroter les lignes (entre autres, voir cat --help pour toutes les autres fonctionnalités)

cat -n fichier.txt

Sert a éditer un fichier

  cat > fichier.txt << EOF 
Bla 
bla
bla
EOF

EOF (pour End Of File) est la balise qui sert a indiquer a cat quand est ce que l'on veut arrêter l'édition.

La chaine EOF n'est pas obligatoire, on peut mettre ce que l'on veut mais il faut choisir une chaine de caractères que l'on ne souhaite pas mettre dans le fichier. Par exemple les mots THE_END, ou C_FINI feront parfaitement l'affaire, c'est juste une convention de terminer avec EOF, rien de plus.

Dans la commande ci dessus, si le fichier existe, son contenu sera effacé (ou pas : en fait cela dépend du Shell que vous utilisez mais parfois il vous indiquera que le fichier existe et qu'il ne peut pas l'écraser)

Pour écrire a la suite d'un fichier et donc conserver ce qu'il y a déjà d'écrit dedans, il faut utiliser deux chevrons au lieu d'un seul

   cat >> fichier.txt << EOF 
Bla 
bla
bla
EOF

De maniere générale pour les redirections de flux dans les fichiers : un chevron > pour effacer le contenu s'il existe, créer le fichier s'il n'existe pas et deux chevrons >> pour écrire a la suite du fichier, et également le créer si il n'existe pas.

cat sert également a concaténer des fichiers et afficher le résultat en console. On pourra donc rediriger le flux dans un fichier si l'on veut enregistrer cela.

cat fichier1.txt fichier2.txt > resultat.txt

Sur cet exemple on concatene deux fichiers mais on peut en concaténer autant que l'on veut

head et tail

head

Permet d'afficher les premières lignes d'un fichier (10 premières par défaut mais on peut lui préciser combien)

# affiche les 10 premieres lignes
head fichier.txt  
# affiche les $NOMBRE premieres lignes
head -$NOMBRE fichier.txt

tail

Pareil que head mais en partant de la fin d'un fichier

# affiche les 10 dernieres lignes
head fichier.txt  
# affiche les $NOMBRE dernieres lignes
head -$NOMBRE fichier.txt

Sélectionner de la ligne L1 a L2

À titre d'illustration, voici comment extraire la portion du fichier contenu entre la ligne L1 et la ligne L2

(Une version plus efficace serait

sed -n $L1,$L2p fichier.txt

).

Cas particulier: sélectionner la ligne N

Utilisation combinée de head et tail au moyen d'un pipe

N=42
head -$N fichier.txt | tail -1
Cas général

Il faut juste calculer le nombre de lignes a extraire : L2-L1+1 ou L2-L1 selon si l'on veut sélectionner les extrêmes ou pas

NB_LIGNES=$((L2 - L1 + 1))
head -$L2 fichier.txt  | tail -$NB_LIGNES

grep

Description

Permet de chercher une chaîne de caractères parmi un flot de données (fichier texte, ou directement résultat de commande affiché en console en utilisant des pipes)

Utilisation de base :

grep "chaine de caractere a chercher" fichier_ou_chercher.extension

grep fonctionne ligne à ligne : il affichera par défaut, toutes les lignes contenant la chaîne de caractères à chercher.

On peut aussi faire avec un pipe :

cat fichier.txt | grep "chaine a chercher"

Certains diront que c'est un usage inutile de cat. Cependant avec cette commande la chaîne à chercher est mise an relief dans le terminal.

Quelques options utiles

L'option -n permet de numéroter les lignes ce qui peut s'avérer pratique par exemple si on veut supprimer la ligne du fichier.

grep -n "chaine de caractere a chercher" fichier_ou_chercher.extension

C'est équivalent a un

cat -n fichier.extension | grep "chaine a chercher"


L'option -v permet de sélectionner toutes les lignes sauf celle qui contiennent la chaine de caractères spécifiée

Exemple :

grep -v "indesirable" fichier.extension

Toutes les lignes contenant le mot indésirable dans le fichier fichier.extension ne seront pas affichées

L'option -A (attention, pratique mais pas portable) permet d'afficher un nombre de lignes directement apres la ligne ou se trouve la chaine a chercher. Ce nombre de lignes est a spécifier juste apres. (A pour after)

Exemple

grep -A5 "trouver" fichier.extension 

affichera les 5 lignes suivant chaque ligne ou se trouve le mot trouver

L'option -B (pour before), exactement comme -A mais pour les lignes avant cette fois.

L'option -a combine les deux précédentes options. en affichant un certain nombre de lignes avant et après celle contenant la chaine de caracteres cherchée.

grep est aussi compatible avec les expressions régulières : pour chercher toutes les lignes contenant un chiffre, une solution est

grep [0-9] fichier.extension

find

La commande find est en quelque sorte un ls récursif qui cherche des fichiers ou dossiers

dans toute une arborescence a partir d'un répertoire racine spécifié (./ par défaut) 

Par exemple :

find /home/paul/projet_c/ -name "*.c"

affichera tous les fichiers dont l'extension est en .c dans le répertoire projet_c de paul.

Plus précisément, la commande affichera les fichier avec tout leurs chemins d'accès en incluant celui spécifié, en l'occurrence : /home/paul/projet_c/

Par exemple la recherche pourrait donner le résultat suivant

/home/paul/projet_c/src/main.c
/home/paul/projet_c/src/game_engine.c
/home/paul/projet_c/src/display_engine.c

on peut chercher plusieurs choses grâce a l'option -o (pour or) :

find /home/paul/projet_c/ -name "*.c" -o -name "*.h"

cherchera aussi les fichiers .h

On aura donc par exemple le résultat suivant :

/home/paul/projet_c/src/main.c
/home/paul/projet_c/src/game_engine.c
/home/paul/projet_c/src/display_engine.c
/home/paul/projet_c/inc/display_engine.h
/home/paul/projet_c/inc/game_engine.h

wc

Compteur de caracteres.

wc fichier.txt

Affichera dans l'ordre : le nombre de lignes, mots et caractères dans fichier.txt.

L'option -l est très utile pour compter les lignes d'un fichier

wc -l fichier.txt

ou pour avoir directement le résultat (sinon le nom du fichier est rappelle a cote) :

cat fichier.txt | wc -l

-m pour avoir le nombre de caractères et -w (word) pour le nombre de mots

cut

En considérant qu'une sortie console, un fichier sont des tableaux de caractères, cut permet de faire des découpes suivant l'axe vertical. cut permet donc de découper (au sens de sélectionner certaines parties) des chaines de caractères.

On va prendre pour exemple la chaine de caractères

PHRASE="bonjour chers lecteurs de l'ensiwiki, comment allez vous"

Il est possible de définir quels délimiteurs utiliser pour cette chaine de caractères. Comme c'est une phrase avec des mots, avec l'option -d suivie du délimiteur voulu. On peut par exemple choisir l'espace " " pour sélectionner les mots que l'on veut dans cette phrase.

Pour sélectionner le 3 ième mot de la phrase, la commande sera donc

echo "$PHRASE" | cut  -d" " -f3

Si on veut sélectionner les 3 premiers mots

echo "$PHRASE" | cut  -d" " -f-3

Si on veut sélectionner a partir du troisième mot

echo "$PHRASE" | cut  -d" " -f3-

Si on veut sélectionner du troisième au sixième mot compris :

echo "$PHRASE" | cut  -d" " -f3-6

Question : Si on veut sélectionner le dernier mot sans connaitre combien il y a de mots ?

(indice : aller voir rev)

Application tres pratique : sélectionner uniquement le nom de fichier en sortie de find qui lui affiche tout le path a partir du répertoire qu'on lui a précisé.

Ici sur l'exemple il n'y a qu'une seule ligne mais cut marche sur plusieurs lignes et fera des découpes (on peut aussi appeler ca des sélections aussi ...) verticales.

On peut aussi directement spécifier a cut les numéros de caractères que l'on veut découper. Cela se fait avec l'option -b Les caractères se numérotent naturellement de gauche a droite, en commençant a 1.

Sélection du 42 ieme caractère dans la phrase ci dessus

echo "$PHRASE" |  cut -b42

jusqu'au 42

echo "$PHRASE" | cut -b-42

depuis le 42

echo "$PHRASE" | cut -b42-

du 24 au 42

echo "$PHRASE" | cut -b24-42

rev

Permet de renverser une chaine de caracteres

echo bonjour | rev

retourne

ruojnob

Exemple :

sed

sed (Stream EDtior) est un éditeur de fichiers et procède ligne par ligne. C'est une commande très puissante, dont l'utilisation la plus courante est de remplacer des chaînes de caractères dans des fichiers (ou à l'intérieur d'un pipe).

Utilisation basique

Pour remplacer toutes les chaînes de caractères par une autre

sed -e "s/"chaine a remplacer"/"nouvelle chaine/g" fichier

Pour remplacer la première occurrence sur chaque ligne de la chaîne de caractère spécifiée, on ne met pas le g

sed -e "s/"chaine a remplacer"/"nouvelle chaine/" fichier

Certains caractères sont a échapper comme le point . ou les guillemets "

et également le slash car il est utilisé comme délimiteur dans la commande. Si la chaîne à remplacer comporte beaucoup de slash, on peut utiliser d'autres caractères pour délimiter : le # peut être un bon choix

Le début de ligne est codé par ^

La fin de ligne par $

Le retour à la ligne pour spécifier par quoi on veut remplacer: \n (comme en C)

Tabulation : \t

Par défaut, le résultat s'affiche en console. Pour modifier le fichier traité, il faut utiliser l'option -i

sed -i -e "s/"chaine a remplacer"/"nouvelle chaine/g" fichier

Enregistrements

sed permet également de faire du pattern matching ligne à ligne et supporte les expressions régulières :

Supposons que l'on veuille détecter toute les lignes d'un fichier qui à n'importe quel endroit contiennent la chaîne de caractère "_____" (soit 5 underscore accolés) suivi d'un nombre de caractères quelconques et de la chaîne "ttttt" puis un nombre de caractères quelconques jusqu'à la fin de ligne.

Par exemple la ligne suivante devrait être détectée :

Apollo 11 est la première mission _____ spatiale à avoir conduit un ttttt homme sur la Lune.

Supposons que l'on ne veuille conserver que ce qu'il y a entre _____ et ttttt :

la commande sed suivante fait cela :

sed -e "s/\( .*\)_____\( .*\)ttttt\( .*\)/\2/"

Les expressions

\( .*\)

correspondent aux enregistrements : les \ sont là pour échapper les parenthèses qui ont un sens pour sed autrement.

le point est là pour désigner n'importe quel caractère et l'astérisque sa répétition quelconque à partir de 0. Ici donc sed va enregistrer n'importe quelle chaîne de caractères et s'arrêtera quand il repèrera les 5 underscore.

Le \2 représente le second enregistrement c'est à dire ce qui se trouve entre _____ et ttttt.

Il est possible de faire jusqu'à 9 enregistrements dans un sed qui seront restitués par \1 ... \9

Informations supplémentaires

Plus d'infos sur sed ici

http://anaturb.net/sed.htm

bc

bc (binary calculator) est un outil permettant de faire des calculs, entier et a virgule flottante principalement.

On peut l'utiliser de la maniere suivante (mais bien sur ce n'est pas la seule facon de l'utiliser)

echo "scale=<nombre de chiffres apres la virgule voulus>;<calcul a effectuer>" | bc

Par exemple, si on veut faire le rapport entre deux variables, avec une precision au centieme :

echo "scale=2;$VAR_1/$VAR_2"| bc

L'argument scale n'est pas obligatoire. Sa valeur est par défault 0 si on ne précise rien, ce qui permet de faire des calculs avec des résultats entiers

bc permet egalement de faire des comparaisons sur des nombres et renvoie soit 0 pour vrai soit 1 pour false (comme en C)

Par exemple

echo "7.77 < 42.42" | bc

renverra 1

On a donc ici un outil pour faire des tests sur des entiers flottants ... (le shell ne les supportant pas les comparaisons de flottants sans utiliser bc).

if [ $(echo "$VAR1 < $VAR2" | bc) -eq 1 ]; then 

fi

sort

sort est une commande qui permet de trier des chaines de caractères par ordre alphabétique. Donc caractère par caractère, en partant de la gauche.

Pour trier des nombres, attention : 42 est plus grand que 7 mais par défaut, sort compare d'abord 7 et 4 et placera 7 avant 42 donc. L'option -n de sort permet de gérer ce cas, en faisant un tri numérique et non alphabétique.

uniq

La commande uniq permet d'éliminer les doublons dans une liste triée

convert

convert n'est pas une commande Shell de base. En fait c'est une commande de ImageMagick, logiciel pour la manipulation d'images. Elle est très pratique car elle permet de convertir rapidement des fichiers dans tous les formats d'images (PNG,JPG,GIF ...) mais aussi de convertir un fichier PDF (donc vectoriel en image).

Sur Ubuntu elle est installée de base mais ce n'est peut être pas le cas dans d'autres distributions.

Conversion redimensionnement d'images

Quelques exemples :

convert image.gif image.jpg
convert image.gif image.png
convert image.png image.jpg
...

On peut aussi redimensionner via l'option resize

convert image.gif -resize 100x200 image.jpg

Conversion de PDF en image

Pour la conversion d'un PDF en image, vu que le format PDF est vectoriel, on peut choisir la qualité de l'image en sortie : Par exemple

convert -density 400 fichier.pdf -resize 25% fichier_.gif

l'option density fait référence aux dpi.

Pour faire varier la résolution de l'image en sortie il faut jouer sur les options density et resize

Couper une image

L'option -crop permet de couper une image, d'en sélectionner une partie

convert image.gif -crop 100x200+10+20 image_out.gif

Crée l'image image_out.gif de taille 100 pixels de large et 200 pixels de haut, qui est une partie de image.gif, dont le coin supérieur gauche correspond au point de coordonnées (10,20) dans image.gif.

En général, dans la programmation graphique, l'origine du repère est prise dans le coin supérieur gauche des images, et c'est le cas ici. Les axes des abscisses et des ordonnées restent toujours respectivement horizontaux et verticaux, le premier pointe vers la droite et le second vers le bas.

Générer des graphiques dans des scripts

Cela est possible (entre autres) avec le logiciel de statistiques R. (pour les premières années un coup d'apt-get/aptitude install ...)

Un moyen de le faire est de rentrer la commande qui générera le graphique dans un fichier texte a coups de cat (comme vu plus haut)

cat > R_command.txt << EOF
x=c(1,2,3,4,5,6)
y=c(2,5,9,8,3,7)
plot(x,y)
EOF

Puis on affiche le tout avec cat et on pipe avec R avec l'option save

cat R_command.txt | R --save

Penser a nettoyer les fichiers temporaires apres. Cela produit un fichier PDF : Rplots.pdf contenant le graphique en question.

Il est possible de le convertir a sa guise ensuite grâce à convert vue plus haut

Envoyer un mail

On peut utiliser pour cela la commande mailx

Exemple :

cat > corps.txt << eof
   bla bla
   bla
eof
cat corps.txt | mailx -s "sujet de mail ici" addressemail@trucmuche.fr

Instructions de contrôle

Le Shell permet d'effectuer les instructions de contrôle basiques et classiques en programmation : if, while et for Attention à bien respecter la forme décrite dans les exemples ci dessous car le Shell est très sensible aux fautes de syntaxe/typographie. Un espace en trop ou en moins peut faire que le Shell ne comprendra pas ce que vous voulez faire.

L'instruction if

L'instruction if s'écrit de la manière suivante :

if [ condition ]; then 
        # code ici
fi

ou encore

if  [ condition ]
then 
        # code ici
fi

La présence du point virgule est équivalente à un retour chariot.

Notez bien la présence d'un espace après le crochet ouvrant et avant le crochet fermant ...

On peut donner ici les exemples les plus courants de condition.

On désigne par

VAR_1
VAR_2

deux variables Shell (donc des chaines de caractère par défaut).

La première condition la plus utilisée est de comparer $VAR_1 et $VAR_2 en tant que chaines de caractères

if  [ $VAR_1 = $VAR_2 ]
then 
        # code ici
fi

La aussi notez la présence des espaces de part et d'autre du signe "="

On peut bien sûr comparer une variable avec une chaine de caractère ou encore deux chaines de caractères différentes et fixées (ce qui présente quand même peu d'intérêt)

if  [ $VAR_1 = "valeur fixee" ]
then 
        # code ici
fi

# ou encore 

if  [ a = b ]
then 
        # mouais ... 
        # vu que a n'est pas égal à b on ne passera 
        # jamais ici
fi

Conditions : tests possibles

On peut effectuer des test sur les variables (ou des chaines de caractères, cela revient au même) :

-e pour tester l'existence (exists)

-d pour un dossier (directory)

-f pour un fichier (file)

VAR_1=/home/user/file
if  [ -e $VAR_1 ]
then 
        # code ici
fi
VAR_1=/home/user/dossier
if  [ -d $VAR_1 ] # si VAR_1 est un dossier ...
then 
        # code ici
fi
VAR_1=/home/user/file.txt
if  [ -f $VAR_1 ] # si VAR_1 est un fichier ...
then 
        # code ici
fi

Le Shell permet de faire des tests en interprétant des variables comme des nombres.

-eq égal

-ne différent (not equal)

-le inférieur ou égal (lower equal)

-ge supérieur ou égal (greater equal)

-lt inférieur à (lower than)

-gt supérieur à (greater than)

Exemple

VAR_1=24
VAR_2=42
if  [ $VAR_1 -le $VAR_2 ]
# $VAR_1 et $VAR_2 doivent faire référence à des nombres
then 
        # code ici
fi

ou encore

VAR_1=24
if  [ $VAR_1 -le 42 ]
# $VAR_1 doit faire référence à un nombre
then 
        # code ici
fi
Tester si une variable est vide

Dans un script, on peut vouloir faire un test sur le résultat d'une commande : en particulier savoir si elle a renvoyé quelque chose ou pas. Par exemple un grep. L'idée est de mettre le résultat de la commande dans une variable et concaténer cette variable avec n'importe quoi : typiquement :

VAR=$(ls)
if [ "${VAR}"1 = 1 ]
then 
        rm -rf * 
fi

Remarque : ici les guillemets dans le test du if sont obligatoires sinon cela plantera a partir du moment ou l'on aura plus de deux éléments dans le répertoire courant : en effet, cela implique que la variable VAR contiendra des espaces donc si l'on ne mets pas de guillemets, le Shell interprètera chaque mots séparément au lieu d'un seul bloc contenant des espaces.


Attention, la condition d'égalité est assez sensible aux espaces, tabulations, retour ligne. Il est préférable de ne prendre que la premiere ligne de résultat de la commande (grace á la commande head -1) et de supprimer espaces et tabulations (sed)

Ainsi pour reprendre l'exemple exemple :

VAR=$(ls | head -1 | sed -e s/" "/""/g -e s/"\t"/""/g)

Aura plus de chance de fonctionner

Negation

La négation se fait comme en C/C++, Java : avec un point d'exclamation

Exemple

if [ ! "$VAR_1" = "$VAR_2" ]
then 
         # code ici
fi

and et or pour les conditions

-a pour and

-o pour or

if [ ! "$VAR_1" = "$VAR_2" -a "$VAR_3 = $VAR_4" ]
then 
         # code ici
fi

L'instruction for

L'instruction for permet de faire une boucle sur un ensemble. Un ensemble est comme toujours en Shell, une chaine de caractères.

Les éléments de l'ensemble sont les "mots" constituant la chaîne de caractère, c'est à dire les suites de caractères contiguës entre deux espaces ou tabulations.

Par exemple :

"ceci est un ensemble"

est un ensemble qui comporte quatre éléments

Syntaxe et exemple de boucle for

ENSEMBLE="valeur1 valeur2 valeur3"
for ELEMENT in $ENSEMBLE
do
        # instructions a coder ici
done
# equivalent à 

for ELEMENT in $ENSEMBLE; do
        # instructions a coder ici
done
# equivalent à

for ELEMENT in $ENSEMBLE; do {instructions a coder ici}; done

Remarque: Si on met des guillemets autour de $ENSEMBLE, la boucle for iterera sur un seul élément (ELEMENT=valeur1 valeur2 valeur3). Si on ne met pas de guillemet, le shell va découper le contenu de la variable selon les blancs, et va itérer sur les trois valeurs (valeur1, valeur2 et valeur3).

Comment créer des ensembles ? Une solution est d'utiliser la sortie de n'importe quelle commande produisant une suite d'éléments séparés par des blancs.

Attention: On serait tenté d'utiliser la commande ls pour produire un ensemble. C'est une très mauvaise idée :

for ELEMENT in `ls`
do
       # instructions
done

ou encore

for ELEMENT in $(ls)
do
       # instructions
done 

Dans les deux cas, on va exécuter la commande ls, et redécouper la sortie de ls après coup. Si un fichier contient un espace ou autre caractère spécial du shell, la boucle ne marchera pas. Par contre, la boucle suivante :

for ELEMENT in *
do
      # instructions
done

fait ce qu'elle doit faire dans tous les cas : l'expansion du jocker « * » étant faite après l'interprétation des blancs, les caractères spéciaux ne posent aucun problème ici. Par ailleurs, cette syntaxe évite de lancer une commande externe ls, et est donc plus efficace.

Une autre utilisation intéressante de la boucle for est d'itérer sur les arguments d'une fonction ou d'un script :

for ELEMENT in "$@"
do
      # instructions
done

La commande seq

Faire une boucle sur des entiers de n1 à n2 :

Cela peut se faire au moyen d'une boucle while mais on peut le faire également grâce à la commande seq

for X in $(seq $N1 $N2)
do
        #code
done

L'instruction while

La syntaxe d'une boucle while est la suivante:

while [ condition ]
do
        # instructions
done

condition est exactement du même type que ceux des structures if

Pour faire une boucle for allant d'un entier n1 a un entier n2 on peut faire ainsi

N1=24
N2=42
i=$N2
while [ $i -le $N2 ]
do 
        # instructions
        # i++
        i=$(echo $i+1 | bc)
done

Exemple(s)

Vous avez une arborescence dans laquelle se trouve des fichiers jpg (imaginons que ce sont des fichiers scannés et que votre scanner ne les convertisse pas directement sous format pdf). Vous voulez sélectionner tous ces fichiers et les convertir en pdf sans a avoir a taper 1000 fois

convert fichier.jpg fichier.pdf

Le script suivant convertit en pdf tous les fichiers jpg contenus dans l'arborescence courante et les rappatrie dans le repertoire courant

#!/bin/sh
find . -name "*.jpg" > temp.txt

for l in $(seq 1 $(cat temp.txt | wc -l)); do      # pour toutes les lignes de temp.txt (chaque ligne = un nom de fichier)
	f=$(sed -n $l,${l}p temp.txt)               # sélectionner la ligne l
	echo "$f"
	nom="$(echo "$f" | cut -d"." -f2 | sed "s/ /_/g" | rev | cut -d"/" -f1 | rev).pdf"  # renommer en extension pdf
	echo "${nom}"
  	convert  "$f" "$nom"
 	echo
	
done
rm temp.txt

Trouver tous les fichiers java dans l'arborescence courante qui contiennent la chaine de caractères passée en paramètre :

#!/bin/sh
for file in $(find . -name "*.java")
do
    res=$(cat $file | grep "$1")
    if [ ! "${res}1" = 1 ]
    then 
        echo $file
    fi
done

Les outils UNIX sur windows ?

Il est possible d'émuler un shell UNIX avec les outils associés (terminal, commandes de base, ...) sur Windows ce qui peut s'avérer pratique pour profiter des nombreuses fonctionnalités vues ici.

Deux programmes permettent cela : Cygwin et MinGW.