Initiation aux commandes Bash

Utilisation des variables

v="pain"
# Variable statique:
readonly p=22
f=20
echo "Du ${var} à ${p}0,$(($p-$f))0 euros!"
$ Du pain à 220,20 euros!
  • Variable de substitution :

https://tldp.org/LDP/abs/html/parameter-substitution.html

var="${1:-default}"
echo "var = $var"
$ ./script.sh param1
$ var = param1
$ ./script.sh
$ var = default
  • Taille d'une chaine
string="chaine"
echo ${#string}

Concaténation

a="a"
b="b"
x="${a} ${b}"
echo ${x}
$ a b

Utiliser des variables d'environnements et arguments

  • $PATH → Chemin courant
  • $0, $1, $2, … → numéro de l'arguments
  • $# → nb d'arguments
  • $* → tous les arguments

Executer une commande externe

cmd=$(pwd)
echo $cmd
$ /home/utilisateur

Transformer une chaîne en tableau

  • Tableaux dans une variable :
LISTE_FERIER_FR=(
"Lundi de Pâques"
"Jour de l'Armistice"
)
 
Si_ferier() {
 
for i in "${!LISTE_FERIER_FR[@]}" ; do  
 
  gcal --cc-holidays=fr --holiday-list=long |grep "${LISTE_FERIER[$i]}" >/dev/null
  if [[ $? ]]
  then
    return 0
  fi  
 
done
 
}
validate_phpversion() {
    php_version="$1"
    if [[ ! " ${PHP_VERSIONS[*]} " =~ ${php_version} ]]; then
        echo "Wrong PHP version!"
        return 1
    fi
}
  • String to array in bash :
var="a b c d"
var=($var)
echo "Nombre de cases : ${#list[@]}"
Nombre de cases : 4
  • Supprimer une case et lister toutes ces valeurs pour la transformer en chaine :
shift 1
for domain in "$@"; do
    domains="${domains:+${domains} }${domain}"
done
  • Utiliser une boucle
declare -A DATES
 
for i in 1 2 3 4; do
  DATES[$i]=$( date +"%Y%m%d" --date="$i days ago" )
done
 
for i in "${DATES[@]}" ; do echo "$i"; done

Découpage d'une chaine

Ressource : https://tldp.org/LDP/abs/html/string-manipulation.html

⇒ Supprime ce qui est avant le dernier terme rencontré :

var="test.tst.ts.t"
echo ${var##*.}
$ t

⇒ Supprime ce qui est avant le premier therme rencontré :

var="test.tst.ts.t"
echo ${var#*.}
$ tst.ts.t

⇒ Supprime ce qui est après le dernier therme rencontré :

var="test.tst.ts.t"
echo ${var%.*}
$ test.tst.ts

⇒ Supprime ce qui est après le premier therme rencontré :

var="test.tst.ts.t"
echo ${var%%.*}
$ test

⇒ Sélectionner une portion d'une chaine !

var="test.tst.ts.t"
echo ${var:5:3}"
$ tst

Substituer (trouver et remplacer) un mot

var="bonjour le monde!"
echo ${var/bonjour/aurevoir}
$ aurevoir le monde!

Les conditions

En pratique c'est la commande test qui sera appelé lorsque l'on utilise ces caractères “[” et “]”

x=true
if [ $x ] || [ "$x" = "var_inutile" ]; then
        echo true
elif [ "$x" = "autre" ]; then
        echo false
else
        echo "else"
fi
 
if ss -tr |grep microsoft-ds 1>/dev/null ; then
  echo utilisateur connecté
else
  echo utilisateur deconnecté
fi

Sur une seule ligne :

x=true
[ $x ]; echo $?;

Si 0 = vrai. Si 1 = faux (celà signifie généralement qu'il y a une erreur).

  • Si l'on utilise l'opérateur -a (and) :
a=true
b=false
[ $a -a $b ]; echo $?;
$ 0
  • Si l'on utilise l'opérateur -o (or) :
a=true
b=false
[ $a -o $b ]; echo $?;
$ 0
Lors d'une comparaison avec une chaine renseigné dans une variable, il faut faire “$chaine”.

Cas réel (en mode sale sans unité systemd utilisateur):

echo Check si planka est lancé...

pstree -u $USER |grep node >/dev/null
planka_started=$?

[ ${planka_started} -ne 0 ] && {
  cd ~/planka
  REACT_APP_SERVER_BASE_URL=https://todo.autarcie.org NODE_ENV=production node app.js --prod &
}

Opérateurs de comparaisons

un man test permet de voir la liste des comparaison supporté

  • -e fichier → vrai si fichier existe
  • -d fichier → vrai si fichier est répertoire
  • -L fichier → vrai si fichier est un lien symbolique
  • -r fichier → vrai si fichier est lisible (en écriture)
  • -w fichier → vrais si fichier est modifiable (w)
  • -x fichier → vrai si fichier est exécutable -w)
  • -s fichier → vrai si le fichier est non vide
  • file1 -nt file2 → vrai si file1 est plus récent que file2
  • file1 -ot file2 → vrai si file1 est plus ancien que file2

Opération sur les chaines

  • -z “chaine” → vrai si la chaine est vide
  • -n “chaine” → vrai si la chaine est non vide
  • “chaine1” = “chaine2” → vrai si les deux chaine sont égales
  • “chaine1” != “chaine2” → vrai si les deux chaines sont différentes

Opérateur de comparaison numérique

  • $num1 -eq $num2 → égalité
  • $num1 -ne $num2 → inégalité
  • $num1 -lt $num2 → inférieur
  • $num1 -le $num2 → inférieur ou égal
  • $num1 -gt $num2 → supérieur
  • $num1 -ge $num2 → supérieur ou égal

Les boucles

La boucle “for” est rudimentaire. C'est à dire qu'il faut obligatoirement un tableau pour qu'elles puissent fonctionner.

⇒ For :

# Lis un tableau :
 
a=(un deux trois quatre)
b=5
 
for x in ${a[@]}
do
        echo x= $x
done
 
# Simple boucle avec un timer :
 
for sec in $(req 1 5) ; do
  echo $sec
  sleep 1
done

⇒ while :

while [ ! -f "fichier.txt" ]
do
	sleep 1;
done
echo fichier trouvé!

ou ainsi

cat plusieurs-lignes.txt | while red mot ; do echo $mot ; done

Structures de contrôles

  • Switch
case $1 in
        start)
                echo start! ;;
        stop|restart)
                echo stop ou restart! ;;
        *)
                echo autre.. ?
esac
  • Switch
while :; do
    case ${1:-''} in
        -h|-\?|--help)
            show_help
            exit 0
            ;;
        -V|--version)
            show_version
            exit 0
            ;;
        *)
            # Default case: If no more options then break out of the loop.
            break
            ;;
    esac
    shift
done

Les fonctions

fonc() {
	echo mon parametre : $1
}
 
fonc coucou
$ mon parametre : coucou

Les codes couleurs

  • Utiliser la couleur rouge :
echo -e "\033[31m ROUGE \033[0m"

Regex

  • Tester si un mot est inclu dans une chaine :
chaine="bruno est present"
 
if [[ "$chaine" =~ "present" ]]
then
    echo "le mot est bien présent dans la chaine!
fi

Inclure un tableau dans un fichier

Admettons que nous avons ce fichier :

boot.html
#__SCANS_ITEMS__/

Et que nous avons ce script :

items=$(ls -1)
items=($items)
awk -v r="$items" '{gsub(/#__SCANS_ITEMS__/,r)}1' boot.html > boot.html.tmp
mv boot.html.tmp boot.html

En l'exécutant, il va substituer les valeurs inclus dans items par la ligne indiqué. Ici #SCANS_ITEMS/.

Variables d'environnement Bash

On peut les lister avec la commande suivante :

set

Ou ajouter des options pour modifier le comportement de bash

  • -a → Exporte les variables qui ont été modifié ou créé maintenant.
  • -e → Termine le script s'il y a une erreur.
  • -f → Désactive le caractère “*” : https://gist.github.com/codeforkjeff/79dda02f162ee1f350d9
  • -m → Active la gestion des job (dans le cas d'usage des subshell).
  • -n → N'execute pas les commandes.
  • -t → Termine le script après avoir lu et executé une commande.
  • -v → Mode verbose sans les arguments.
  • -x → Mode verbose avec les arguments.
  • - → Désactive les options -x et -v.

e

Quelques commandes supplémentaires

  • Écrire dans un fichier :
cat > /tmp/exemple.txt <<EOF
Le contenu de ce fichier
est sur plusieurs lignes.
:)
EOF
  • dirname fichier = nom de répertoire de fichiers
  • basename fichier = nom du fichier
  • echo -n = évite le retour à la ligne
  • echo -e = execute les méta caractères tel que le retour chariot “\n” ou \e[1em (couleur)
  • reset = idem au paquet clair ?
  • Saisir un mot de passe au clavier :
read -p "Entrez votre mot de passe : " -s pass
  • Lire un texte depuis stdin :
script.sh
#!/bin/bash
cat /dev/stdin |grep maison
$ cat nombreuses-lignes.txt |script.sh
maison
maisonnette

Plus d'infos à ce sujet : http://lipn.univ-paris13.fr/~cerin/SE/S2SE_01_LectureFichiersShell2.html

  • Lancer une commande en tâche de fond grâce au caractère & :
sleep 20 &

Celui-ci peut être mis en pause avec les touches : Ctrl+z

  • Compter le nombres de lignes depuis une sortie (ou depuis un fichier) :
cat /etc/passwd | wc -l
wc -l /etc/passwd
  • Supprimer des lignes en doubles depuis un fichier :
uniq <fichier> -u
  • Décommenter une phrase :
sed 's|# \(.*blue.*\)|\1|' input.txt
  • Remplacer une phrase depuis une ligne précise :
sed -i "307s/.*/\tdisableThirdPartyRequests: true,/" input.txt
  • Capturer une chaine dans une phrase :
zgrep repaired /var/log/mysql/error.log* |sed "s/.*Table '.\(.*\)'.*/\1/" |sort|uniq
  • Faire un filtre sur un mot :
grep -e 'postgres' /etc/postgres       # Retournera que la ligne contenant postgres
grep -v 'postgres' /etc/postgres       # Retournera tout sauf la ligne contenant postgres
  • Récupérer le contenu d'un fichier entre les lignes 15 à 20 :
cat /etc/passwd | head -n 20 | tail -n 5
  • Récupérer le contenu de la 1ère et la dernière collone :
awk -F ":" '{ print $1 $NF }' /etc/passwd
  • Récupérer un contenu spécifique avec des critères de sélections :
awk 'BEGIN { FS = ":"
             printf ("\nUser id\t\tShell\n\n") }
           { printf ("%s\t\t%s\n", $1, $7) }
     END   { printf "\nTotal numer of user ids = %d\n", NR } ' /etc/passwd
  • Nombre total de ligne (comme avec wc -l)
awk 'END { print "Total ligne : " NR }' /etc/passwd
  • Compter des données :
awk ' BEGIN { FS = ","  }
      { 
         if ( NR == 1 || NR == 4 || NR == 5 )
              printf ("%s,%s,%s,%s\n", $1, $2, $3, $4) 
         else {
               sum2 += $2
               sum3 += $3
               sum4 += $4
               printf ("%s,%6d,%6d,%6d\n", $1, $2, $3, $4) }
      }
      END    { printf ("Totals,%6d,%6d,%6d\n", sum2, sum3, sum4) } 
' <<EOF
Expenditure,2012,2013,2014
Advertising,200015,233912,189928
Bank charges, 23029, 26667, 34990
***********,****,****,****
Expenditure,2015,2016,2017
Bank charges, 23029, 26667, 34990
Boarding and Lodging,237899,453326,356625
EOF
  • Gestion des sorties EOF
echo "debut"
 
cat <<EOF >log.txt 
        Status of backup as on $(date)
        Where ?? -> $HOME
EOF
 
echo "fin"

* Jouer avec les couleurs

https://misc.flogisoft.com/bash/tip_colors_and_formatting

  • Fermer le stdin d'un script entier :
exec <&-
  • test d'envoie de mail
mail -s 'test sujet' -a From:contact@domaine.com user@domaine.fr <<< 'test'