Bash: l'antisèche ultime


Soyez conscients qu'il ne s'agit pas d'une liste exhaustive.


Faites un bash --version si une ou plusieurs des astuces ne fonctionnent pas chez vous. Certaines pourraient nécessiter bash 4 mmême si la grande majorité ont été testées avec bash 3.

Permalien pour la section Simple, basique Simple, basique

Il faut bien commencer quelque part..

Permalien pour la section Bash est un acronyme Bash est un acronyme

Bash veut dire “Bourne Again Shell”, ce qui fait référence à “born again shell”. Bash est la version plus récente du vieux Bourne shell sortie en 1979.

Permalien pour la section Bash est un language de scripting “Unix-versel” Bash est un language de scripting “Unix-versel”

Bash est tellement pratique pour automatiser des tâches que ce soit pour des projets persos, de la maintenace de serveurs ou encore des actions type devOps. Cela peut consister à écrire une série de commandes dans un fichier texte.

Vous pouvez tout à fait taper ces commandes dans le terminal et utiliser grep, sed, find ou d’autres commandes issues de librairies tierces installées sur la machine.

Cependant, l’intérêt est souvent de grouper toutes ces instructions dans des fichiers afin de pouvoir les versionner dans Git et les transférer d’une machine à l’autre ou encore des les utiliser sur de multiples serveurs. En général, les scripts Bash sont des fichiers sans extensions qu’on exécute de cette façon :

bash myscript

On peut aussi modifier les permissions du fichier pour le rendre exécutable et le lancer directement:

chmod +x myscript && ./myscript

Permalien pour la section Bash n’est pas sh! Bash n’est pas sh!

Bash et sh ont des syntaxes très similaires mais sh ne supporte pas toutes les commandes Bash. Les lignes qui suivent peuvent très bien marcher mais c’est assez confusant et sujet aux erreurs:

bash mysh.sh
sh myscript

Permalien pour la section L’importance du Shebang L’importance du Shebang

C’est le fameux #! au début des fichiers. “Bang” fait référence au point d’exclamation “!”:

#!/usr/bin/env bash

Ce n’est pas du tout pour faire joli ! Cela indique le chemin absolu vers l’interpréteur Bash de la machine ou du serveur. /usr/bin/env est un choix assez malin en général car il va chercher l’exécutable dans la variable utilisateur $PATH.

Permalien pour la section Commenter le code Commenter le code

Utilisez#:

# comment

Permalien pour la section Variables Variables

Les variables sont très simples à manipuler avec Bash.

Permalien pour la section Variables natives (liste incomplète) Variables natives (liste incomplète)

echo "$1 $2" # positional arguments passed one by one, e.g., bash myscript arg1 arg2
echo "$#" # number of arguments passed
echo "$@" # all arguments passed
echo "You are here: $PWD" # $PWD is the current path

Permalien pour la section Assigner de nouvelles variables Assigner de nouvelles variables

Utilisez le signe = sign mais surtout aucun espace autour:

MyVar="My string"
MyArray=(all in one)

Permalien pour la section Utiliser les variables Utiliser les variables

Écrivez le signe $:

echo $Variable
echo "This is my var: $Variable" # double quotes are required for interpolation
echo "${MyArray[@]:0:3}" # "all in one", because it prints 3 elements starting from the first (0)

# Be careful the following does not display a value
echo ${#MyArray[2]} # "3" as there are 3 chars in the third element of the array, which is the word "one"

Permalien pour la section Modifier les variables Modifier les variables

Utiliser les accollades {}:

MyVar="My variable"
# Length
echo ${#MyVar}

# Substitution
echo ${MyVar//a/O} # "My vOriOble"

# Expansion
echo ${!BASH@} # all variable names than begin with "BASH"
echo ${!BASH*} # all variable names than begin with "BASH"

# Removals
MyFile="list.txt"
echo ${MyFile%.*} # the filename without extension
echo ${MyFile##*.} # the file's extension

# Default value
echo ${AnotherOne:-"Another one"} # displays "Another one" even if AnotherOne is not defined

Permalien pour la section Dictionnaires Dictionnaires

declare -A Movies

Movies[title]="The Dark Knight Rises"
Movies[bestActress]="Anne Hathaway"
Movies[director]="Christopher Nolan"

Permalien pour la section printf printf

printf fonctionne un peu comme dans C pour afficher du texte préformaté. Il y a bien plus de possibilités qu’avec echo. On définit des formats et on passe des paramètres, la fonctionne affiche ensuite le texte formaté.

Exemples :

printf "%s\n" "Learn it" "Zip it" "Bash it" # it prints 3 lines
printf "%.*f\n" 2 3.1415926 # prints 3.14

Permalien pour la section Boucle, boucle, boucle Boucle, boucle, boucle

Permalien pour la section For For

for i in "${MyArray[@]}"; do
    echo "$i"
done

Avec des chaînes de caractère:

MyString="First Second Third"
for n in $MyString
do
   echo "$n line"
done

Permalien pour la section While While

while [ true ]
do
    echo "We loop"
    break
done

Permalien pour la section Ranges Ranges

echo {1..7} # 1 2 3 4 5 6 7
echo {a..g} # a b c d e f g

Permalien pour la section Until Until

until [ $counter == 111 ]
do
    echo "Number is: $((counter++))"
done

Permalien pour la section Inputs utilisateur Inputs utilisateur

echo "6x7?"
read answer
echo answer # hopefully 42 but can be anything the user wants

Permalien pour la section Conditions Conditions

if [[ CONDITION1 || CONDITION2 ]]
then
    # code
elif [[ CONDITION3 && CONDITION4 ]]
then
    # code
else
    # code
fi

On peut aussi utiliser un case statement:

case $COLOR in

  Red)
    echo -n "red"
    ;;

  Blue | Green)
    echo -n "blue or green"
    ;;

  Yellow | "Sunny" | "Gold" | "Fire")
    echo -n "Yellow"
    ;;

  *)
    echo -n "What?"
    ;;
esac

Permalien pour la section Erreurs & stratégies d’exit Erreurs & stratégies d’exit

Il vaut mieux élever un peu votre jeu et renvoyer des erreurs pour éviter toute mauvaise utilisation de vos scripts.

Permalien pour la section Exit si une commande échoue Exit si une commande échoue

#!/usr/bin/env bash
set -e

Permalien pour la section Exit N Exit N

Exit avec un code spécifique:

#!/usr/bin/env bash
if [ CONDITION ]; then
    exit 0 # 0 1 2 3 ... N
fi

0 indique le succès et {1,2,3, N} sont réservés aux erreurs par convention.

Permalien pour la section Tester les erreurs Tester les erreurs

# $? is the exit status of last command
if [[ $? -ne 0 ]] ; then
	echo "You failed!"
    exit 1
else
    echo "You are the best!"
fi

Permalien pour la section Débug Débug

#!/usr/bin/env bash
set -x # set -n can also be used to check syntax errors
ARGS=("$@") # store all script args in a var

On peut aussi passer l’option -x dans le terminal:

bash -x myscript

Permalien pour la section Système de fichiers & répertoires Système de fichiers & répertoires

Toutes les commandes habituelles typecp, rm, ls, mv ou encore mkdir fonctionnent.

Par exemple, vous pouvez vous assurer que le fichier est exécutable avant de faire quoi que ce soit :

if [ ! -x "$PWD/myscript" ]; then
    echo "File is not executable!"
    exit 1
fi

Utilisez -f pour les fichiers, -d pour les répertoires, -e pour tester l’existence, etc. Il y a des tonnes d’options possibles, vous pouvez même tester des liens symboliques avec -L ou enore comparer des fichiers avec les options -nt (plus récent que) and -ot (plus vieux que).

Permalien pour la section Usages un peu plus avancés Usages un peu plus avancés

Au-delà du basique.

Permalien pour la section Profitez de la souplesse de la syntaxe Profitez de la souplesse de la syntaxe

Les brace expansions permettent de construire rapidement des affichages relativement complexes :

echo {1..10}{0,5}h # bash 3
echo {10..120..10}km # requires bash 4

Permalien pour la section Définir des fonctions Définir des fonctions

function test ()
{
    echo "$1 $2" # displays positional arguments passed to the function ("All" "In")
    return 0 # if you return a value, it !must be numerical!
}
test "All" "In"

Permalien pour la section Les scopes Les scopes

On n’a pas besoin de déclarer une variable pour l’utiliser dans Bash. Par défaut, aucune erreur n’est renvoyée quand une variable n’est pas définie, seulement une valeur vide à la place

Permalien pour la section Variables d’environnement Variables d’environnement

Les variables d’env sont les plus disponibles, elles existent partout au niveau système. Utilisez printenv pour les lister ou inspecter l’une d’entre elles en particulier :

printenv USER # if you don't specify a variable, all variables will be displayed

Permalien pour la section Variables shell Variables shell

Comme suit :

TEST="My test"

Elles s’utilisent partout dans un même script, y compris dans les fonctions. Cependant, elles ne sont pas passées aux processus enfant à moins d’utiliser explicitement export pour transmettre l’info.

Permalien pour la section Le mot-clé local Le mot-clé local

S’utilise uniquement dans une fonction :

function test ()
{
    local MyVar="My string" # local variable
}
test

On ne peut pas exporter des variables locales.

Permalien pour la section Bonnes pratiques Bonnes pratiques

Il vaut mieux limiter le nombre de variables shell et utiliser des variables locales dans des fonctions pour éviter les écrasements involontaires et les confusions parfois à l’origine d’erreurs importantes.

Permalien pour la section Boucler sur le résultat d’une commande Boucler sur le résultat d’une commande

for r in $(ls $PWD)
do
	# task using the result of the command => r
	echo "$r"
done

Permalien pour la section Utiliser l’affichage d’une fonction Utiliser l’affichage d’une fonction

function hello() {
	echo "Hello Ethan"
}

echo "This is the result of the function : $(hello)"

Permalien pour la section Stocker le résultat d’une commande dans une variable Stocker le résultat d’une commande dans une variable

Users=$(cat users.txt)

Permalien pour la section Capturer les inputs yes/no Capturer les inputs yes/no

read -p "Continue? [y/n]: " -n 1 -r
echo # extra line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    echo "ok"
elif [[ $REPLY =~ ^[Nn]$ ]]
then
	echo "Too bad!"
else
	echo "Sorry, I did not understand your answer :("
fi

Permalien pour la section Capturer la sélection d’un utilisateur Capturer la sélection d’un utilisateur

Ce code affiche une sélection à l’utilisateur et capture son choix :

select w in "zsh" "bash" "powershell" 
do
  echo "You prefer $w"
  break  
done

Permalien pour la section Alias et écrasements Alias et écrasements

On peut créer des alias facilement :

alias lz='ls -alh'

On peut aussi tout simplement redéfinir des commandes.

Permalien pour la section Chaîner les commandes Chaîner les commandes

wget -O /tmp/logo.jpg 'https://dev-to-uploads.s3.amazonaws.com/uploads/logos/resized_logo_UQww2soKuUsjaOGNB38o.png' && echo "echo only if the first hand is true"
wget -O /tmp/logo2.jpg 'https//example.com/logo.jpg' || echo "echo only if the first hand is wrong"
wget -O tmp/logo3.jpg 'https//example.com/logo.jpg' ; echo "echo whatever happens"

Permalien pour la section Passer le jeu en mode difficle Passer le jeu en mode difficle

Ces concepts sont beaucoup plus avancés que les précédents.

Permalien pour la section Exécuter à nouveau la dernière commande exécutée Exécuter à nouveau la dernière commande exécutée

sudo !!

Permalien pour la section Redirections, stderr, stdout Redirections, stderr, stdout

COMMAND > out.txt   # write in out.txt 
COMMAND >> out.txt  # append in out.txt 
COMMAND 2> test.log # stderr to test.log
COMMAND 2>&1        # stderr to stdout
COMMAND &>/dev/null # stdout and stderr to (null)

Remplacez COMMAND avec votre commande.

Permalien pour la section La commande Trap La commande Trap

Permet une gestion avancée des erreurs mais à ne pas confondre avec les exceptions qu’on peut retrouver dans d’autres langages. L’idée est d’exécuter du code dans des conditions très spécifiques :

#!/usr/bin/env bash

trap 'catch' EXIT

catch() {
  echo "Exit game!"
}

echo "ok"
echo "---"

exit 0

“Exit game!” s’affiche quand exit 0 est déclenché.

Dans la vraie vie, on voit souvent ce genre de code :

#!/usr/bin/env bash

trap "rm /tmp/scan.txt" EXIT

Permalien pour la section Exécuter des scripts dans un script Bash Exécuter des scripts dans un script Bash

Utiliser la commande source :

#!/usr/bin/env bash

source ./otherScript

Permalien pour la section Subshells Subshells

A subshell is a separate instance of the command processor. A shell script can itself launch subprocesses. These subshells let the script do parallel processing, in effect executing multiple subtasks simultaneously.

Source: tldp.org

En gros, toutes les commandes qui sont mises entre parenthèses, ce qui justifie l’utilisation de export pour passer les variables utiles aux sous-processus.

On retrouve parfois le terme de “forks”. C’est la même idée.

Permalien pour la section pipefail pipefail

On rentre dans une gestion des erreurs plus avancées et pro :

#!/usr/bin/env bash

set -eu # u is to exit if using undefined variables
set -o pipefail

Non seulement on active le “mode erreurs” mais aucune erreur dans les pipelines ne sera ignorée, ce qui est le comportement par défaut sans l’option -o pipefail.

Permalien pour la section Mes astuces favorites pour le terminal Mes astuces favorites pour le terminal

C’est plus lié aux système Unix mais pas complètement hors-sujet ici non plus🤞🏻.

Permalien pour la section bash bash

À entrer dans le terminal pour passer en mode Bash quelque soit le système en place (ex : zsh) :

bash

Pressez ensuite entrée et vous pouvez ensuite taper man bash pour explorer l’aide.

Permalien pour la section \ \

\ améliore la lisibilité et évite la perte de temps en découpant les lignes de commande trop longues en plusieurs lignes moins larges.

Permalien pour la section & &

nohup bash script &

On peut exécuter notre bash en arrière-plan tout en capturant le signal hangup.

Permalien pour la section Divers Divers

Des astuces de la vie courante sous Bash :

Permalien pour la section Lister les sous-répertoires dans un répertoire Lister les sous-répertoires dans un répertoire

SUBDIRS=$(ls -d */)
for sub in $SUBDIRS
do
    echo $sub
done

Permalien pour la section Renommer un fichier rapidement Renommer un fichier rapidement

mv /project/file.txt{,.bak} # rename file.txt.bak

Permalien pour la section Exécuter un script sur un serveur distant Exécuter un script sur un serveur distant

ssh REMOTE_HOST 'bash -s' < myScript

Permalien pour la section Trouver rapidement les fichiers volumineux (exemple ici avec plus de 10 MB) Trouver rapidement les fichiers volumineux (exemple ici avec plus de 10 MB)

find . -size +10M -print

Permalien pour la section Créer de multiples fichiers rapidement Créer de multiples fichiers rapidement

Vous pouvez toujours faire ça :

touch {script1,script2,script3,script4,script5,script6,script7}

Mais cela reste très pénible à écrire donc faites plus ça :

touch script{1..7}

Permalien pour la section Bash pour les hackers Bash pour les hackers

Les hackers aiment bien le Bash en général. Même si Python est pratique pour le pen-testing, bash permet d’automatiser beaucoup de tâches. Les professionnels s’en servent pour accélérer les phases d’analyse notamment.

Permalien pour la section Hello world: scanning Hello world: scanning

Il vaut mieux grouper des instructions finalement assez courantes dans des fichiers réutilisables plutôt que de répéter sans arrêt les mêmes lignes dans le terminal.

Vous pouvez passer par read pour rendent le tout plus agréable ou simplement passer des arguments. Par exemple, écrivons un binaire rapidement, on nommera le fichier scanning:

#!/usr/bin/env bash

echo "Welcome aboard"
echo "What's the targeted IP?"
read TargetIp

if [[ ! $TargetIp ]]
    then echo "Missing targeted IP!"
    exit 1
fi

echo "What port do you want to scan?"
read PortNumber

if [[ ! $PortNumber ]]
    then echo "Missing port number!"
    exit 1
fi

nmap $TargetIp -p $PortNumber >> scan.txt # append results in scan.txt

Ensuite, il nous reste simplement à lancer bash scanning. Bon, j’admets que ce n’est pas très réaliste car il existe déjà bon nombre de solutions pour automatiser et enrichire nmap et l’exemple ici est très incomplet mais vous voyez l’idée j’espère.

Permalien pour la section Alias Alias

Les alias peuvent faire gagner un temps fou :

alias whatsMyIp="echo $(ifconfig -a | grep broadcast | awk '{print $2}')"
whatsMyIp # IP local

J’admets que les distributions de Pen-testing ont des auto-complétions plus performantes qui renderaient cet exemple inutile mais on n’est pas toujours sur ce genre d’environnements sur-optimisés donc ça reste judicieux.

Permalien pour la section Hacker le Bash Hacker le Bash

Tout est piratable ou presque, en premier lieu les langages informatique. Lisez le post de hacktricks (en).

Permalien pour la section Aller plus loin Aller plus loin

Votre voyage peut débuter ici

Cette page est aussi disponible dans d'autres langues :