moi

Orange / IT&Labs

développeur Java / architecte logiciel

admin système Linux

15 ans Linux

pourquoi utiliser un shell sur son poste ?

alors qu'on a une souris !

  • plus efficace que le clic !
  • seule solution pour interagir avec un serveur Linux distant (dans le cloud)
  • un peu dur de plonger dedans...
    • une distrib linux contient plusieurs milliers de programmes !
    • bash$ ls -1 /bin /usr/bin | wc -l
      3280
      
    • syntaxe hétérogène entre les programmes

BASH

the Bourne-Again Shell

  • le shell du projet GNU => utilisé par defaut dans les distribs Linux
  • un shell parmis d'autres (sh, zsh, ksh, ...)
  • un programme comme les autres (pas de privilèges particuliers)
  • exécute des commandes
    • tapées au clavier depuis un terminal
    • issues d'un script
  • très configurable (~/.bashrc, ~/.inputrc, ...)

fonctionnalités interactives

éditeur de la ligne courante

  • éditeur de texte mono-ligne
  • préfixé par un prompt pour avoir des informations contextuelles
  • raccourcis de navigation en mode par défaut (emacs)
    • CTRL-GAUCHE / CTRL-DROITE : mot précédent / mot suivant
    • CTRL-A / CTRL-E : va au début / à la fin de la ligne
    • CTRL-W : supprime le mot précédent
    • CTRL-U / CTRL-K : supprime la ligne jusqu'au début / jusqu'à la fin
  • contrôle d'exécution
    • CTRL-C : arrête la commande en cours
    • CTRL-Z : gèle la commande en cours
    • CTRL-D : quitte le shell
  • astuce : mettre en commentaires la ligne en cours permet de se la garder sous le coude pour plus tard (séquence CTRL-A, #, ENTRÉE)

la complétion

  • touche TAB complète la ligne en cours avec soit :
    • nom de commande
    • nom de fichier ou répertoire
    • nom de variable
  • completion contextuelle à la commande avec bash_completion
      $ git <TAB>
      add                   commit                instaweb              rm 
      am                    config                l                     s 
      ...
      
    • implémenté par des fonctions BASH (/etc/bash_completion.d/)

la complétion

configuration améliorée

dans le fichier ~/.inputrc

$include /etc/inputrc

# ignore la case
set completion-ignore-case on

# treats hyphens ('-') and underscores ('_') as equivalent
set completion-map-case on

# ignorer les fichiers cachés
set match-hidden-files off

# affiche directement les completions possibles (évite de taper TAB 2 fois)
set show-all-if-ambiguous on

historique des commande

  • garde en 'mémoire' les commandes précédemment exécutées
    • stocké dans ~/.bash_history
    • 500 lignes conservées par défaut
  • touche HAUT / BAS : commande précédente / suivante
    • peu efficace avec un gros historique
  • CTRL-R : recherche mot-clé dans l'historique
  • ALT-. : répère le dernier argument de la commande précédente

historique des commande

configuration améliorée

  • recherche incrémentale avec HAUT / BAS (dans ~/.inputrc)
  • "\e[A": history-search-backward
    "\e[B": history-search-forward
    
  • un gros historique (dans ~/.bashrc)
  • HISTSIZE=20000
    HISTFILESIZE=20000
    

le prompt

  • ce qu'affiche le shell avant la zone d'édition
  • yoann@computer:~/breizhcamp-bashetmoi $
    
  • assez basique par défaut mais fortement personnalisable
  • variable PS1, évaluée à chaque affichage
    • peut inclure des informations contextuelles
    • peut inclure des appels de fonctions
  • configuration à mettre dans ~/.bashrc

un prompt clé en main : liquidprompt

  • affiche élégamment des informations utiles uniquement quand le contexte le demande
  • https://github.com/nojhan/liquidprompt
mon terminal

ok, mais qu'est qu'on tape ?

la ligne de commande

petits rappels...

  • on tape des commandes !
  • navigation et manipulation de répertoires (cd, mkdir, rmdir, ...)
  • [yoann:~]$ mkdir test
    [yoann:~]$ cd test
    [yoann:~/test]$ ls
    [yoann:~/test]$
    
  • gestion de fichiers (ls, cp, mv, rm, ..)
  • [yoann:~]$ ls
    template.html
    [yoann:~]$ cp template.html template.html.old
    [yoann:~]$ ls
    template.html  template.html.old
    [yoann:~]$ rm template.html
    

la ligne de commande

petits rappels...

  • l'évaluation est plus complexe qu'un simple lancement de programme
  • applique des substitutions (variables, caractères spéciaux, ...)
  • [yoann:~]$ echo ${PWD}
    /home/yoann
    [yoann:~]$ echo *
    template.html.old
    
  • interprète les constructions du langage et exécute les programmes
  • [yoann:~]$ for i in {1..5}; do echo -n "$i "; done
    1 2 3 4 5
    
  • c'est un vrai langage de programmation !

commandes de base pour survivre

  • exécutables systèmes :
    • cp, mv, rm, mkdir, rmdir, chown, chmod
    • ls, find, stat
    • grep, wc, tr, sort, uniq, head, cut, cat, echo, tail(f), more, less, join, date
    • xargs, sed, awk
  • internes bash :
    • cd, pwd
    • export, set, env
    • if, then, else, fi, for, while, do, done
    • |, ||, &, &&

à l'aide !!!

  • utiliser le man
  • bash$ man ls
    
  • lancer une commande avec -h ou --help
  • bash$ ls --help
    Utilisation : ls [OPTION]... [FILE]...
    Afficher des renseignements sur les FILEs (du répertoire actuel par défaut).
    Trier les entrées alphabétiquement si aucune des options -cftuvSUX ou --sort
    ne sont utilisées.
    
    Les arguments obligatoires pour les options longues le sont aussi pour les
    options courtes.
      -a, --all                  ne pas ignorer les entrées débutant par .
      -A, --almost-all           ne pas inclure . ou .. dans la liste
    

exemples de programmes

lignes de commande...

exemple 1 : mise en bouche

les 2 commandes que j'utilise le plus

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
   3682 cd
   2967 ls

exemple 1 : mise en bouche

pipeline de commandes

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
          ^                  ^      ^         ^              ^
  • opérateur pipe : command A | command B
    • connecte la sortie standard de A vers l'entrée de B
    • les 2 processus s'exécutent en même temps

exemple 1 : mise en bouche

les commandes utilisées

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
  • history : affiche l'historique
  • $ history
      497  ls
      498  vi .inputrc 
      499  cat /etc/inputrc 
      500  ls
      501  history
    
  • awk : traitement de chaines (recherches, remplacements, transformations)
  • $ history | awk '{print $2}'
    ls
    vi
    cat
    ...
    

exemple 1 : mise en bouche

les commandes utilisées

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
  • sort : tri le texte entrée
  • $ history | awk '{print $2}' | sort
    cat
    history
    ls
    ls
    vi
    

exemple 1 : mise en bouche

les commandes utilisées

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
  • uniq : supprime les doublons consécutifs
    • -c : préfixe avec le compte
  • $ history | awk '{print $2}' | sort | uniq -c
     1 cat
     1 history
     2 ls
     1 vi
    

exemple 1 : mise en bouche

les commandes utilisées

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr | head -2
 2 ls
 1 cat
  • sort : tri le texte entrée
    • -k1,1 : clé de tri => 1ère colonne
    • -n : tri numérique
    • -r : inverse le tri
  • head : affiche le début

exemple 1 : mise en bouche

redirection dans un fichier

$ history | awk '{print $2}' | sort | uniq -c | sort -k1,1nr > hist-$(date +%Y%m).txt
$ head hist-201405.txt
   3682 cd
   2967 ls
   2364 git
   ...
  • opérateur > : redirige la sortie standard dans un fichier
  • opérateur $(command) : substitue l'argument par la sortie standard de l'exécution de command

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

$ find * -name ".git" | while read d ; do d=${d%.git} ; ( cd "$d" && git branch
  --list | grep -q mybranch && echo "$d" ) ; done

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

$ find * -name ".git" | while read d ; do d=${d%.git} ; ( cd "$d" && git branch
  --list | grep -q mybranch && echo "$d" ) ; done
dépot-3/
dépot-5/
dépot-8/

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

$ find * -name ".git" | while read d ; do d=${d%.git} ; ( cd "$d" && git branch
  --list | grep -q mybranch && echo "$d" ) ; done
dépot-3/
dépot-5/
dépot-8/

reformattons !

$ find * -name ".git" | \
  while read d ; do \
    d=${d%.git} ; \
    ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
  done

le caractère \ permet d'échapper le retour chariot

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

-> $ find * -name ".git" | \
     while read d ; do \
       d=${d%.git} ; \
       ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
     done
  • métacaractère * remplacé par la liste des fichiers dans le répertoire courant
  • commande find : recherche récursive de fichiers par critères
  • $ find * -name ".git"
    dépot-1/.git
    dépot-2/.git
    dir-1/dépot-Z/.git
    ...
    

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

   $ find * -name ".git" | \
->   while read d ; do \
       ...
->   done
  • commande while x; do command ; done
    • boucle exécutant command tant que la commande x réussi
  • ; sépare une liste de commandes exécutées séquentiellement
  • commande read var
    • assigne à var une ligne lue depuis l'entrée standard
    • succès tant que l'entrée standard n'est pas vide (code retour 0)

note : construction fonctionnant avec des noms de fichiers ayant des espaces

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

   $ find * -name ".git" | \
     while read d ; do \
->     d=${d%.git} ; \
       ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
     done
  • assignation varname = value
  • Shell Parameter Expansion
    • ${variable%word} enlève word à la fin de variable
    • ${variable#word} enlève word du début de variable

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

   $ find * -name ".git" | \
     while read d ; do \
       d=${d%.git} ; \
->     ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
     done

sous shell : ( command )

  • exécute command dans un nouveau process bash
  • permet de ne pas polluer le process principal
$ ps | grep bash | wc -l
1
$ ( ps | grep bash | wc -l )
2

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

   $ find * -name ".git" | \
     while read d ; do \
       d=${d%.git} ; \
->     ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
     done

liste de commandes

  • command A && command B : exécute B si A a réussie (ET logique)
  • command A || command B : exécute B si A a échouée (OU logique)
  • command A ; command B : exécute A puis B

exemple 2 : on en veut plus

liste des dépôts git ayant une branche nommée mybranch

   $ find * -name ".git" | \
     while read d ; do \
->     d=${d%.git} ; \
       ( cd "$d" && git branch --list | grep -q mybranch && echo "$d" ) ; \
     done
  • command grep : recherche de pattern dans un fichier ou l'entrée standard
    • -q : mode silencieux (quiet)
    • code retour 0 si pattern trouvé
  • command echo : affiche les arguments passés

exemple 3 : super combo !

  • génère un changelog unifié à partir de changelog Debian d'un ensemble de sous-modules
$ git diff --submodule=log $COMMIT_START $COMMIT_END | \
  sed -n -r 's|^Submodule ([^ ]+) ([0-9a-f]{7}\.?\.\.[0-9a-f]{7})[: ].*|\1 \2|p' | \
  while read sm vv ; do \
    v1=${vv%%..*} ; v2=${vv##*..} ; \
    echo ">>> ${sm} (${v1}..${v2})" >&2 ; \
    if [[ ${v1} = 0000000 ]] ; then \
      cat "${sm}/debian/changelog" ; \
    else \
      git --git-dir="${sm}/.git" diff "${v1}..${v2}" -- debian/changelog | \
        sed -n '/^+++/!s:^+::p' ;\
    fi ;\
  done

du one-liner au script

  • fichier texte avec droits d'exécution : chmod +x myscript.sh
  • entête spécial avec le shebang : #!/bin/bash
  • possibilité de définir des fonctions pour organiser son code
  • #!/bin/bash
    function usage { 
      echo 'Salut le BreizhCamp'; exit 1
    }
    [[ $# == 0 ]] && usage
    echo "$@"
    

quelques tips

  • expansion de crochet : {mot1,mot2}
  • $ echo mv realllylongname.cpp{,-old}
    mv realllylongname.cpp realllylongname.cpp-old
    
  • revenir au répertoire précédant
  • $ cd -
    
  • renommage massif de fichiers
  • $ rename 's/old/new/' *.java
    
  • opérateur <(command) : expose le flux de sortie de command comme un fichier
  • $ diff <(sort -u file1) <(sort -u file2)
    
    • évite de créer des fichiers temporaires dans des cas complexes

quelques tips

  • sed (stream editor) : outil de manipulation de texte permettant d'inserer, de supprimer, de chercher et replacer du texte
    • substitution simple dans un fichier
    • $ sed -e 's/foo/bar/' myfile.txt
      
    • liste des suffixes de recherche DNS
    • $ sed -n 's/^search //p' /etc/resolv.conf
      
  • jq (JSON processor) : équivalent de sed pour des données JSON
  • sponge : l'éponge à fichier (paquet moreutils)
    • bash$ sort test.csv | sponge test.csv
      

les alias

raccourcis vers une commande ou pré-configuration

alias l='ls -CF'
alias ..='cd ..'
alias ...='cd ../..'
# accès rapide au répertoire du projet foo
alias cdf='cd ~/Projets/foo'

généralement définis dans ~/.bash_aliases

Autour de BASH

tmux : un multiplexeur de terminal

tmux : un multiplexeur de terminal

  • plusieurs terminaux virtuels dans une même fenêtre (multiplexe le terminal)
  • détachement / rattachement d'une session
  • visualisation de plusieurs processus en même temps
  • multiplexage du clavier possible
    • permet d'exécuter une commande sur plusieurs serveurs en même temps
  • alternative à GNU screen

vu de la fenêtre

  • Chocolatey
    • outil d'installation automatique de paquets logiciels pour Windows
    • équivalent de apt-get ou yum
    • https://chocolatey.org
  • ConsoleZ : une vrai console (copier / coller, redimensionnement, ...)
  • c:\> cinst ConsoleZ
    
  • BASH existe sous Windows !
    • MinGW : Minimalistic GNU for Windows
    • c:\> cinst mingw
      
    • Cygwin : couche de compatibilité Linux pour Windows
    • c:\> cinst Cygwin
      

liens

questions ?

Votez +1 ou bashez moi :)