Linux:Script en Bash

De Djjnet
Aller à : Navigation, rechercher

Sommaire

Généralités

Commencer par indiquer l'utilisation de bash pour l'exécution de votre script en haut du fichier grâce au "shebang" :

#!/bin/bash

Inclure un fichier dans un script :

. cheminfichier

Les variables

Généralités

Déclaration d'une variable nommé mavariable : mavariable="mon.fichier.sql" echo $mavariable donne mon.fichier.sql pour éviter des erreurs dans les substitutions de variable, il est bon d'utiliser echo ${mavariable}

Sans précision, une variable déclarée dans le script est disponible dans tout ce script, y compris les fonctions.
Attention, elle n'est pas accessible à un sous processus.
Il est possible de préciser devant la variable le terme local par exemple dans une fonction afin que cette variable ne soit valable qu'à l'intérieur de la fonction et sous fonctions.

Nombre de caractères de la variable (longueur de la chaîne) :

${#mavariable}

Affecter une valeur par défaut à une variable nulle ou inexistante :

${mavariable:-defaut}

Dans ce cas, la variable n'est pas modifiée. Pour que la valeur soit affectée à la variable, il faut utiliser :

${mavariable:=defaut}

Attention, il ne faut pas appeler directement ${mavariable:=defaut} sans commande sinon bash effectuera l'affectation si nécessaire mais retrouvera une erreur :

${mavariable:=defaut}
bash: defaut: command not found

Il est possible pour contourner cette erreur d'effectuer :

echo ${mavariable:=defaut} > /dev/null

Mais cela n'est pas très propre, il vaut mieux d'utiliser la commande : de bash lui indiquant d'ignorer l'opération et appeler ainsi :

: ${mavariable:=defaut}

Il est possible de tester si une variable est définie et non vide :

: ${mavariable:?non définie}

Dans le cas où la variable n'est pas définie ou est nulle, bash terminera l'exécution et affichera le message après le ?

Sous-chaînes

${mavariable:debut:longueur} permet d'extraire un certain nombre de caractère d'une chaîne :

${mavariable:2:4} renvoie : n.fi
${mavariable:2} renvoie : n.fichier.sql

Suppression du plus court préfixe correspondant au motif indiqué après le caractère # :

${mavariable#mon.} renvoie : fichier.sql

Suppression du plus long préfixe correspondant au motif indiqué après les caractères ## :

${mavariable##*i} renvoie : er.sql

Suppression du plus court suffixe correspondant au motif indiqué après le caractère % :

${mavariable%.sql} renvoie : mon.fichier

Suppression du plus long suffixe correspondant au motif indiqué après les caractères %% :

${mavariable%%i*} renvoie : mon.f

Environnement et attributs de variable

De base, les variables ne sont pas transmises entre les processus, par exemple lors du lancement d'un script depuis un autre.
S'il est nécessaire de transmettre une variable, il faut la mettre dans l'environnement grâce à la commande export :

export variable

Attention, la variable est "copie" dans le nouveau shell. Si une modification est apportée dans le nouveau shell, elle ne sera pas répercutée dans le shell parent.

Il est possible d'exporter plusieurs variable à la fois :

export variable1 variable 2

La commande declare parmet aussi avec l'option -x d'exporter dans l'environnement la variable :

declare -x variable

On le voit lorsque l'on liste les variables exportées avec :

export -p

Pour annuler l'export :

export -n variable

Un autre attribut intéressant à une variable est d'empêcher la modification :

declare -r variable

variable est alors en lecture seule. Attention, cet attribut n'est pas exporté dans l'environnement d'un nouveau shell.

Variables prédéfinies
  • PID du processus :
$$
  • PID du processus parent :
$PPID
  • PID de la dernière commande lancée en arrière plan :
$!
  • Répertoire personne :
$HOME
  • Répertoire courant :
$PWD
  • Répertoire précédent :
$OLDPWD

Les tableaux

La déclaration d'un tableau n'est pas obligatoire mais il est possible de le faire ainsi :

declare -a tableau

Pour affecter une valeur à un tableau :

tableau[0]=valeur
tableau[1]=valeur2

Pour consulter la valeur des éléments d'un tableau :

${tableau[1]}

Nombre d'éléments dans un tableau :

${#tableau[@]}

Taille d'un élément :

${#tableau[1]}

Les arguments passés à un script ou à une fonction dans un script

Ils sont appelés variables de position.

L'argument null est définit par $0 qui représente le nom du script (tel qu'il a été exécuté).

Le nombre d'arguments passés en paramètre $# n'est pas limité à 9 contrairement à certaines affirmation.

De 1 à 9, il est possible d'utiliser l'appel de variable ainsi $1 à $9. Au dessus de 9, il suffit de bien délimiter le nom de la variable ainsi ${10}, ${11}, etc ...

L'ensemble de la liste des arguments est : $*
Attention, il faut utiliser $* si vous êtes sûr que les arguments n'auront pas d'espaces sinon il est préférable d'utiliser $@ entouré de " ainsi :

#!/bin/bash
#
#Appel d'un sous script en passant les arguments
./sous-script.sh "$@"

L'identifiant du processus est $$

La commande set parmet de redéfinir l'ensemble des arguments à l'intérieur du script :

set 2 3 4
echo $1 $2 $3

-> 2 3 4 S'il y avait plus de trois paramètre avant la redéfinition des arguments avec set 2 3 4 alors ils seront effacés.

La commande shift permet de décaler l'ensemble des valeurs des paramètres (sauf $0) :

./script 1 2 3
shift
echo $1 $2 $3

-> 2 3

Les options de lancement du script

Script d'exemple lisant les options passées au lancement du script

OPTIND=0
while getopts ":abc-:" opt ; do
  case $opt in
      a ) optiona=1 ;;
      b ) optionb=1 ;;
      c ) optionc=1 ;;
      - ) case $OPTARG in
           version ) echo "Version 1.0"
                     exit 0;;
           * ) echo "option illégale -$OPTARG"
               exit 1;;
          esac ;;
      ?) echo "option illégale -$OPTARG"
         exit 1;; 
      esac
done
shift $(($OPTIND - 1))


La fonction getopts renvoie vrai tant qu'il y a des options. En paramètre, nous lui fournissons la liste des options possibles puis le nom de la variable qui contiendra l'option.
Pour les options longues comme --version, il faut indiquer - suivi de : pour indiquer qu'il y aura des caractères après le tiret.
Le premier : sert pour stocker dans la variable OPTARG l'option illégale et mettre un ? dans la variable choisie pour les options (opt dans l'exemple).

Les options sont en réalité des arguments au script comme les autres.
getopts parcours les arguments pour en trouver commençant par un tiret et incrémente OPTIND.
Afin d'appeler les arguments qui ne sont pas des options à partir de l'indice 1, il faut décaler les arguments après traitement des options avec l'appel :

shift $(($OPTIND - 1))

Tests [ ]

Les tests sont réalisés avec la commande interne [ ].
Il doit toujours y avoir des espaces à l'intérieur de [ ] :

[ $i -lt 10 ]

A l'aide des opérateurs && (ET) et || (OU) il est possible d'utiliser les tests pour exécuter des commandes selon des conditions :

[ $i -lt 10 ]&& echo $i
[ -f $fichier ] || echo $fichier n'est pas un fichier


Paramètres pour les tests [ ]

Chaînes :

s1 # Vrai si la chaîne n'est pas vide
s1 = s2 # Vrai si les chaînes sont identiques

Nombres :

n1 -eq n2 # Vrai si les 2 nombres sont identiques
n1 -ne n2 # Vrai si les 2 nombres sont différents
n1 -gt n2 # Vrai si n1 est plus grand que n2
n1 -ge n2 # Vrai si n1 est plus grand ou égal à n2
n1 -lt n2 # Vrai si n1 est plus petit que n2
n1 -le n2 # Vrai si n1 est plus petit ou égal à n2

Fichiers :

-d nomfichier # existe et est un dossier
-e nomfichier # existe
-f nomfichier # existe et est un fichier normal
-r nomfichier # existe et est un lisible
-w nomfichier # existe et est écrivable
-x nomfichier # existe et est exécutable
-s nomfichier # existe et est non vide

! condition # Retour l'opposé de la condition

Opérateurs dans les tests :

-a # AND ou ET
-o # OR ou OU


Les structures de contrôle

  • IF
if [ 1 ]; then
       echo "toto"
else
       echo "titi"
fi 	
if [ 1 ]
then
       echo "toto"
else
       echo "titi"
fi

Il est possible d'ajouter un autre cas au if : elif [ condition ]; then

  • CASE
case $variable in
   1) echo "toto"
         ;;
   2) echo "titi"
         ;;
   *) echo "defaut"
esac
  • FOR
for i in $*
do
   echo $i
done
for i in `ls`
do
   echo $i
done

Attention si vous avez des espaces dans des fichiers que vous souhaitez garder, il faut changer la variable IFS :

OLDIFS=${IFS}
IFS=$(echo -en "\n\b")
for i in $(grep chaine *)
do 
   vos_commandes $i
done
IFS=${OLDIFS}
  • WHILE
i=1
while [ $i -le 3 ]
do
       echo $i
       i=`expr $i + 1`
done

Calculs

La réalisation de calcul s'effectue par l'utilisation de l'opérateur $(( calculs )). Attention, les calculs ne sont possibles ainsi que sur des entiers, sinon, il faut utiliser la commande bc. Exemple pour la somme de deux variable a et b :

resultat=$(( $a + $b))

Appel à une commande

Dans un script, vous pouvez par exemple lancer une commande tar ou date directement comme :

date
tar -zcf fichier.tar.gr dossier/

Parfois, il est nécessaire de mettre le résultat d'une commande dans une variable.
Il n'est pas possible d'écrire :

variable=date

Il s'agit là d'une affectation de la chaîne date à la variable nommée variable. Une écriture couramment employée est :

variable=`date`

Elle fonctionne mais n'est pas la syntaxe normale pour bash, il faut utiliser :

variable=$(date)

Il est parfois utile de préparer une expression à l'avance et de l'évaluer ensuite :

aevaluer="echo \$variable"
eval $aevaluer


Les fonctions

Déclarer une fonction

function nomfonction(){
    ...
}

Appel de la fonction :

nomfonction parametre1 parametre2 ... parametre9

Export dans l'environnement :

export -f nomfonction

Redirection d'entrées/sorties

Les entrées et sorties standard par défaut sont :

  • STDIN : entrée standard de numéro 0
  • STDOUT : sortie standard de numéro 1
  • STDERR : sortie standard des erreurs de numéro 2
0 ---> COMMANDE | ----> 1
                | ----> 2

Les redirections sont effectuées avec les opérateurs >, >>, < et <<

  • > redirige par défaut la sortie standard 1 vers un fichier
  • >> redirige par défaut la sortie standard 1 vers un fichier en ajoutant à la fin du fichier.
  • < redirige l'entrée standard 0 depuis un fichier
  • << redirige l'entrée standard 0 depuis le texte suivant l'opérateur, un séparateur de fin est alors nécessaire.

Dans le cas de > et >>, il est possible de préciser le numéro de la sortie à rediriger : 2> ou 1>.
Si les deux sorties sont à rediriger, il est possible d'utiliser 2>&1 > /dev/null ou le raccourci &> /dev/null

Exemples pour > et >> :

echo texte > /home/utilisateur/fichier
echo $(date) >> /home/utilisateur/fichier
find . -name test.txt 2> /dev/null

Exemples pour < et << :
Contenu de mail.txt :

helo tortuegeniale.dj-j.net
mail from: email@domaine.net
rcpt to: destinataire@domaine.fr
data
Subject:Bonjour
test
.

Utilisation de < :

telnet localhost 25 < mail.txt

Même chose sans les affichages à l'ecran :

telnet localhost 25 < mail.txt &> /dev/null

Utilisation de << :

telnet localhost 25 << FIN
helo tortuegeniale.dj-j.net
mail from: email@domaine.net
rcpt to: destinataire@domaine.fr
data
Subject:Bonjour
test
.
FIN

Particularité :
L'utilisation de <<- permet de supprimer les tabulations (pas les espaces) :

telnet localhost 25 <<- FIN
    helo tortuegeniale.dj-j.net
    mail from: email@domaine.net
    rcpt to: destinataire@domaine.fr
    data
    Subject:Bonjour
    test
    .
FIN

Fonctionnalités avancés de bash

  • configuration du shell avec la commande set

Par exemple, il est possible de provoquer une erreur lors de la lecture d'une variable inexistante :

set -u
  • désactivation d'une commande interne

Par exemple, la commande echo existe en interne à bash et en version système, pour désactiver la version bash :

enable -n echo
  • utilisation d'une commande redéfinie par une fonction

Par exemple, vous redéfinissez la fonction echo mais vous souhaitez l'utiliser dans la nouvelle fonction, il faut alors utiliser builtin :

function echo()
{
 builtin echo "Nouvelle fonction echo"
 builtin echo "$@"
}

Astuces et commandes utiles

Changer la casse

minuscules=$(echo ${variable}|tr A-Z a-z)

La date

  • Date courante en secondes depuis 1970...
date +"%s"

-> 1272622472

  • Date courante selon un format :
date +"%Y%m%d-%H:%M:%S"

-> 20100430-12:14:03

  • Date d'hier :
date -d'1 day ago' +"%Y%m%d"

-> 20151108

  • Conversion de date :
date -d'2010-04-30 11:34:16' +%s

-> 1272620056

Générer une suite de nombre

seq 10#renvoie 1 2 3 4 5 6 7 8 9 10
seq 10 15#renvoie 10 11 12 13 14 15

Code d'erreur et pipe

De base avec le pipe, vous ne pouvez avoir que le code d'erreur de la dernière commande. Pour pallier çà, il faut utiliser le tableau spécial PIPESTATUS :

mysqldump...|gzip> fichier.sql.gz
_exitcodes=${PIPESTATUS[@]}
_mysql_exitcode=$(echo ${_exitcodes}|cut -f1 -d ' ')
_compress_exitcode=$(echo ${_exitcodes}|cut -f2 -d ' ')

Tri de fichiers rapide

Création du dossier de la date :

ls -ln --full-time|awk '{ print $6 }'|sort -u|xargs mkdir

Déplacement des fichiers :

ls -ln --full-time DSC*|awk '{ print "mv "$9" "$6"/" }'|bash

Options utiles pour ssh et scp

-o ConnectTimeout=5 
-o StrictHostKeyChecking=no

Quelques exemples

Générer un csv comparant des variables dans plusieurs fichiers de configuration.

#!/bin/bash
DIFF=/tmp/diff.csv
echo -n "var;">$DIFF
for i in $(ls /tmp/pgconf_*)
do
echo -n ${i#/tmp/pgconf_}";" >> $DIFF
done
echo "" >> $DIFF
for var in $(awk '{ print $1 }' /tmp/pgconf_1001)
do
  echo -n $var";" >> $DIFF
  for i in $(ls /tmp/pgconf_*)
  do
  echo -n $(grep "^$var" $i|awk '{ print $3 }') >> $DIFF
  echo -n ";" >> $DIFF
  done
echo "" >> $DIFF
done