| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| informatique:linux:bash [2021/04/05 16:24] – [Liens/Ressources] pteu | informatique:linux:bash [2023/01/09 10:52] (current) – [-e] pteu |
|---|
| {{:informatique:linux:bash_logo.png?nolink&200 |}} | {{:informatique:linux:bash_logo.png?nolink&200 |}} |
| |
| **Bash** est un interpréteur de commande, un "shell". Il possède beaucoup de caractéristiques et fonctions communes avec ''sh'' ou ''zsh''. | **Bash** est un interpréteur de commande, un "shell". Il possède beaucoup de caractéristiques et fonctions communes avec ''sh'', ''ksh'', ''zsh'' ou plus largement ''*sh''. |
| |
| Pour la partie programmation et les variables, voir la page [[informatique:linux:programmation_shell|Programmation Shell]]. | Cette page se concentrera sur la prise en main de **bash**, sa configuration, ses raccourcis clavier, les enchaînements de commandes et quelques tips ; pour la partie programmation/scripting et variables, voir la page [[informatique:linux:programmation_shell|Programmation Shell]]. |
| |
| |
| * ''^foo^bar'' relancer la dernière commande en remplaçant la première occurrence de "foo" par "bar". Pour supprimer "foo" : ''^foo'' tout court. | * ''^foo^bar'' relancer la dernière commande en remplaçant la première occurrence de "foo" par "bar". Pour supprimer "foo" : ''^foo'' tout court. |
| * ''!!:gs/foo/bar'' relance la dernière commande en remplaçant toutes les occurrences de "foo" par "bar". | * ''!!:gs/foo/bar'' relance la dernière commande en remplaçant toutes les occurrences de "foo" par "bar". |
| | |
| =====Syntaxe===== | =====Syntaxe===== |
| |
| * ''<cmd1>&&<cmd2>'' permet de lancer //<cmd1>//, puis //<cmd2>// uniquement si //<cmd1>// renvoie //true// (se termine correctement) | * ''<cmd1>&&<cmd2>'' permet de lancer //<cmd1>//, puis //<cmd2>// uniquement si //<cmd1>// renvoie //true// (se termine correctement) |
| * ''<cmd1>||<cmd2>'' lancer //<cmd1>//, puis //<cmd2>// uniquement si //<cmd1>// renvoie //false// | * ''<cmd1>||<cmd2>'' lancer //<cmd1>//, puis //<cmd2>// uniquement si //<cmd1>// renvoie //false// |
| * ''<cmd1>|<cmd2>'' redirige la sortie standart de //<cmd1>// vers l'entrée standard de //<cmd2>// | * ''<cmd1>|<cmd2>'' redirige la sortie standard de //<cmd1>// vers l'entrée standard de //<cmd2>// |
| * ''<cmd1>|xargs <cmd2>'' convertit la sortie de //<cmd1>// en paramètres de //<cmd2>// | * ''<cmd1>|xargs <cmd2>'' convertit la sortie de //<cmd1>// en paramètres de //<cmd2>// |
| * ''<cmd> > <fic>'' redirige la sortie (STDOUT) de //<cmd>// vers le fichier //<fic>// (le fichier est vidé (>)) | * ''<cmd> > <fic>'' redirige la sortie (STDOUT) de //<cmd>// vers le fichier //<fic>// (le fichier est vidé (>)) |
| * ''<cmd> >> <fic>'' redirige la sortie de //<cmd>// à la fin du fichier //<fic>// (>>) | * ''<cmd> <nowiki>>></nowiki> <fic>'' redirige la sortie de //<cmd>// à la fin du fichier //<fic>// (<nowiki>>></nowiki>) |
| * ''<cmd> 2><fic>'' redirige la sortie d'erreur (STDERR) vers //<fic>// | * ''<cmd> 2> <fic>'' redirige la sortie d'erreur (STDERR) vers //<fic>// |
| On trouvera par exemple ''<cmd> >/dev/null 2>/dev/null'' pour que //<cmd>// n'affiche rien à l'écran (ni la sortie standart ni la sortie d'erreur). | On trouvera par exemple ''<cmd> >/dev/null 2>/dev/null'' pour que //<cmd>// n'affiche rien à l'écran (ni la sortie standard ni la sortie d'erreur). |
| * ''<cmd> 2>&1'' redirige la sortie d'erreur vers la sortie standart (**&1**) | * ''<cmd> 2> &1'' redirige la sortie d'erreur vers la sortie standard (**&1**) |
| * ''<cmd> < <fic>'' redirige le contenu de //<fic>// sur l'entrée standart de //<cmd>// | * ''<cmd> < <fic>'' redirige le contenu de //<fic>// sur l'entrée standard de //<cmd>// |
| * Les doubles quotes ''"'' n'interprètent que les caractères spéciaux **$**, **\** et **'** (quote) | * Les apostrophes (quotes) permettant de définir une chaîne de caractère contenant des espaces : |
| * Les simples quotes ''''' n'interprètent aucun caractère spécial | * les doubles quotes ''"'' interprètent les caractères spéciaux **$**, **\** et **'** (quote) |
| | * les simples quotes ''''' n'interprètent aucun caractère spécial |
| * Les caractères spéciaux sont : ''& ~ ; \ " ' ` # ( ) [ ] | $ * ! ? < >'' | * Les caractères spéciaux sont : ''& ~ ; \ " ' ` # ( ) [ ] | $ * ! ? < >'' |
| |
| ====-e==== | ====-e==== |
| |
| bash se termine lorsqu'il voit un code de retour en erreur (différent de 0). Pratique pour éviter que des erreurs non prévues dans un script ne provoquent des bêtises. | Avec l'option ''-e'', bash se termine dès qu'une commande se termine avec un code de retour en erreur (différent de 0). Impossible a activer dans un shell, mais pratique pour éviter que des erreurs non prévues dans un script ne provoquent de gros problèmes. |
| Cependant ça ne marche pas correctement avec certaines commandes comme grep, qui retourne 1 s'il ne matche pas (ce qui n'est certainement pas une erreur qui vaut le coup que l'on stoppe le script). Alors, comme indiqué dans [[https://stackoverflow.com/questions/9952177/whats-the-meaning-of-the-parameter-e-for-bash-shell-command-line/9952249|ce thread de stackoverflow]] : | |
| * soit on gère l'erreur avec un ''grep TRUC machin.txt || :'' : on gère l'erreur pour que bash ne la "voit" pas. Cependant ce n'est pas idéal car on masque tous les codes d'erreur (pas que 1). | Cependant cela ne marche pas correctement avec certaines commandes comme grep, qui retourne 1 s'il ne matche pas (ce qui n'est certainement pas une erreur qui vaut le coup que l'on arrête le script). Alors, comme indiqué dans [[https://stackoverflow.com/questions/9952177/whats-the-meaning-of-the-parameter-e-for-bash-shell-command-line/9952249|ce thread de stackoverflow]] : |
| * soit, plus compliqué mais plus pertinent, on créé une fonction "grep" dans notre script pour surcharger la commande système grep, qui va gérer ses codes de retour pour filtrer les erreurs 1 mais pas toutes les autres, qu'elle renverra à bash pour qu'il se termine en affichant le code d'erreur de grep. | * soit on gère l'erreur avec un ''grep TRUC machin.txt || :'' : on gère le retour pour que bash ne la "voit" pas (si aucune action n'est nécessaire : ''grep TRUC machin.txt || true'' suffit ; cependant ce n'est pas idéal, car on masque tous les codes d'erreur (pas que 1). |
| | * soit, plus compliqué mais plus pertinent, on créé une fonction "grep" dans notre script pour surcharger la commande système grep, qui va filtrer les codes de retour = 1 (motif non trouvé) mais pas les autres, que bash pourra interpréter (erreur=2 : fichier inexistant par exemple). |
| <code bash> | <code bash> |
| #!/bin/bash -e | #!/bin/bash -e |
| Rappel : ''command'' permet de lancer la commande qui suit ("grep" ici) parmi les commandes internes et le contenu du $PATH uniquement, afin que notre fonction ne devienne pas récursive ! | Rappel : ''command'' permet de lancer la commande qui suit ("grep" ici) parmi les commandes internes et le contenu du $PATH uniquement, afin que notre fonction ne devienne pas récursive ! |
| |
| =====Debugger un script===== | ====-x==== |
| | |
| Pour vérifier la syntaxe d'un script sans lancer : ''set -n'' (ou ''bash -n mon_script.sh'') | |
| |
| Pour le débugger un script, ajouter dans le script : | Pour le débugger un script, ajouter dans le script : |
| #!/bin/sh -v | #!/bin/sh -v |
| </code> | </code> |
| | |
| | ====-n==== |
| | |
| | Pour vérifier la syntaxe d'un script sans le lancer (//run-dry//) : ''set -n'' (ou ''bash -n mon_script.sh'') |
| | |
| | ====-o pipefail==== |
| | |
| | Par défaut, après une suite de pipe (''|'') bash ne retient que le code de retour de la dernière commande (la plus à droite). En activant l'option pipefail, bash va renvoyer le dernier code de retour __en erreur__ dans la suite de pipes, ou "0" si toutes les commandes se terminent correctement. |
| | |
| | Combiné avec ''-e'', permet de terminer le script courant en cas de code d'erreur non traitée, même celles "cachées" par un pipe. |
| | |
| | Par exemple: |
| | <code bash> |
| | #!/bin/bash |
| | #set -eo pipefail |
| | foo | echo test1 |
| | echo test2 |
| | #test1 |
| | #line 3: foo: command not found |
| | #test2 |
| | </code> |
| | |
| | <code bash> |
| | #!/bin/bash |
| | set -eo pipefail |
| | foo | echo test1 |
| | echo test2 |
| | #test1 |
| | #line 3: foo: command not found |
| | </code> |
| | |
| | ====-u==== |
| | |
| | Avec cette option, bash va générer une erreur et stopper son exécution si une variable non déclarée est utilisée. Pour éviter cela, on utilisera la notation ''${var-default}'' pour s'assurer que si $var n'est pas définie, on lui attribuera la valeur "default" et bash ne génèrera pas d'erreur. |
| |
| =====.bashrc===== | =====.bashrc===== |
| </code> | </code> |
| |
| | ====Désactiver l'alias==== |
| |
| | Pour afficher si une commande est un alias, on peut faire : ''alias <cmd>'', par exemple : |
| | <code bash> |
| | alias ls |
| | alias ls='ls --color=auto' |
| | </code> |
| | |
| | Et si on souhaite la lancer sans l'alias (''ls'' tout court), on peut utiliser la syntaxe '''<cmd><nowiki>'</nowiki>'' ou ''\<cmd>'' : |
| | <code bash> |
| | 'ls' |
| | # ou |
| | \ls |
| | </code> |
| =====Liens/Ressources===== | =====Liens/Ressources===== |
| |
| * [[https://github.com/jlevy/the-art-of-command-line|The Art of Command Line]] (jlevy's GitHub) | * [[https://github.com/jlevy/the-art-of-command-line|The Art of Command Line]] (jlevy's GitHub) |
| | * [[https://betterdev.blog/minimal-safe-bash-script-template/|Minimal safe Bash script template]] (betterdev.blog) |
| | Je pose ledit template ici, mais le lien ci-dessous vaut le détour car il explique chaque best-practice en détail ! |
| | <code bash> |
| | #!/usr/bin/env bash |
| | |
| | set -Eeuo pipefail |
| | trap cleanup SIGINT SIGTERM ERR EXIT |
| | |
| | script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) |
| | |
| | usage() { |
| | cat << EOF # remove the space between << and EOF, this is due to web plugin issue |
| | Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...] |
| | |
| | Script description here. |
| | |
| | Available options: |
| | |
| | -h, --help Print this help and exit |
| | -v, --verbose Print script debug info |
| | -f, --flag Some flag description |
| | -p, --param Some param description |
| | EOF |
| | exit |
| | } |
| | |
| | cleanup() { |
| | trap - SIGINT SIGTERM ERR EXIT |
| | # script cleanup here |
| | } |
| | |
| | setup_colors() { |
| | if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then |
| | NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' |
| | else |
| | NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' |
| | fi |
| | } |
| | |
| | msg() { |
| | echo >&2 -e "${1-}" |
| | } |
| | |
| | die() { |
| | local msg=$1 |
| | local code=${2-1} # default exit status 1 |
| | msg "$msg" |
| | exit "$code" |
| | } |
| | |
| | parse_params() { |
| | # default values of variables set from params |
| | flag=0 |
| | param='' |
| | |
| | while :; do |
| | case "${1-}" in |
| | -h | --help) usage ;; |
| | -v | --verbose) set -x ;; |
| | --no-color) NO_COLOR=1 ;; |
| | -f | --flag) flag=1 ;; # example flag |
| | -p | --param) # example named parameter |
| | param="${2-}" |
| | shift |
| | ;; |
| | -?*) die "Unknown option: $1" ;; |
| | *) break ;; |
| | esac |
| | shift |
| | done |
| | |
| | args=("$@") |
| | |
| | # check required params and arguments |
| | [[ -z "${param-}" ]] && die "Missing required parameter: param" |
| | [[ ${#args[@]} -eq 0 ]] && die "Missing script arguments" |
| | |
| | return 0 |
| | } |
| | |
| | parse_params "$@" |
| | setup_colors |
| | |
| | # script logic here |
| | |
| | msg "${RED}Read parameters:${NOFORMAT}" |
| | msg "- flag: ${flag}" |
| | msg "- param: ${param}" |
| | msg "- arguments: ${args[*]-}" |
| | </code> |