meta données pour cette page
Initiation aux commandes Bash
Bash acronyme de Bourne Again Shell est un interpréteur de commandes développé en 1989 par Brian Fox. Au début de sa création, ce petit logiciel permettait de rendre exécutable les programes GNU installé sur un système. Aujourd'hui il comporte de nombreuses extensions et il existe des alternatives comme CSH, KSH, ZSH, ou même Oh My ZSH.
Guide avancé :
Voir les directives chargé par le terminal :
stty -a env
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
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
⇒ 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'