Aller au contenu principal

Les 15 commandes shell qui reviennent tous les jours en développement

Un terminal ouvert toute la journée, cinq commandes utilisées en boucle et le reste fait à la main dans l'éditeur. Cet article couvre les quinze situations de développement qui se règlent en une ligne de shell : chercher, filtrer, compter, remplacer, diagnostiquer un port occupé, lire un log de 3 Go. Avec les enchaînements qui servent chaque semaine et les pièges qui coûtent des données.

Guides & tutoriels ·
Adel LATIBI
Adel LATIBI

Le Briefing Dev - les ressources et actus de la semaine, droit dans ta boîte chaque vendredi gratuitement.

En vous inscrivant, vous acceptez de recevoir notre newsletter. Désinscription possible à tout moment.

Le terminal reste ouvert du matin au soir, et il sert à lancer trois commandes toujours identiques. La recherche dans les fichiers se fait dans l'éditeur, le renommage en masse à la main, la lecture des logs par copier-coller dans un onglet de navigateur.

La curiosité n'a rien à voir là-dedans. Ce qui manque, c'est le déclencheur. Personne n'apprend xargs par plaisir. On l'apprend le jour où il faut supprimer 4 000 fichiers temporaires répartis dans trente dossiers, et où l'interface graphique se fige au bout de deux minutes.

Les quinze entrées qui suivent couvrent les situations qui reviennent le plus souvent en développement. Elles regroupent une vingtaine d'outils, parce que certains ne servent qu'en couple.

Les exemples sont écrits pour Bash 5 et les outils GNU (coreutils, findutils, grep) tels qu'ils arrivent sur une distribution Linux récente, ce qui correspond aussi à WSL sous Windows. Sur macOS, plusieurs de ces outils viennent de BSD et se comportent différemment : les écarts sont signalés commande par commande. Aucune installation n'est nécessaire, à une exception indiquée en fin de liste.

Le problème : chaque outil graphique réapprend ce que le shell sait déjà faire

Rechercher dans un projet, filtrer, trier, compter, remplacer. Ton éditeur sait faire tout ça, avec une interface, dans son propre périmètre. Le jour où il faut appliquer la même opération sur un serveur distant, sur un fichier de 2 Go, ou dans un script qui tournera toutes les nuits, l'interface disparaît et il ne reste que la ligne de commande.

En formation, l'exercice qui marque le plus les groupes tient en une question posée devant un fichier de log de plusieurs centaines de milliers de lignes : quelle erreur revient le plus souvent. La réponse par l'éditeur de texte prend un quart d'heure et donne un ordre de grandeur approximatif. La réponse par le shell tient en une ligne et donne un décompte exact. Cet écart déclenche plus de vocations que n'importe quel chapitre théorique sur le système de fichiers.

Le déficit se paie surtout au moment du diagnostic. Une application ne répond plus en production, la question « quel processus occupe le port 8080 » demande une réponse en dix secondes, pas en dix minutes de recherche web. C'est le même réflexe que celui décrit dans apprendre à débugger : une procédure connue à la place de la panique.

Le principe : des petits outils qu'on branche les uns aux autres

Chaque commande Unix fait une chose, lit du texte en entrée et écrit du texte en sortie. Le tube, la barre verticale |, envoie la sortie de la première dans l'entrée de la suivante. Toute la puissance du shell tient dans cette règle, implémentée par Ken Thompson à partir d'une idée de Doug McIlroy et datée du 15 janvier 1973 dans les archives de Bell Labs, apparue dans le manuel de la version 3 d'Unix le mois suivant. Rien ne l'a remplacée depuis.

Une question posee en une ligne : quelles adresses IP frappent le plus le serveur ? access.log 2 M lignes | awk garde l'IP | sort regroupe | uniq -c compte | sort -rn classe | head les 10 premieres Chaque etape lit du texte, en ecrit, et ignore tout du reste de la chaine.
Cinq outils simples branchés en série répondent à une question qu'aucun d'eux ne sait traiter seul.

La ligne correspondante traite deux millions d'enregistrements en quelques secondes :

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

Cette logique de briques assemblées est celle qu'on retrouve ensuite dans un Dockerfile ou dans un job de CI, ce que détaille la roadmap DevOps.

Les quinze entrées, par situation

1. grep : retrouver du texte dans un projet

grep -rn "API_KEY" . --exclude-dir=node_modules
grep -ril "todo" src/

L'option -r descend dans les dossiers, -n affiche le numéro de ligne, -i ignore la casse et -l ne liste que les noms de fichiers. La première commande sert aussi d'audit rapide avant un commit, pour vérifier qu'aucune clé n'a atterri dans le code.

2. find : retrouver des fichiers par nom, taille ou date

find . -name "*.log" -size +100M
find . -type f -mtime -1

La seconde ligne liste tout ce qui a été modifié dans les dernières 24 heures, ce qui aide à comprendre ce qu'un script a touché pendant la nuit. Le find de macOS exige un chemin de départ explicite, d'où le point de find . qui n'est pas facultatif là-bas.

3. xargs : appliquer une commande à une liste

find . -name "*.tmp" -print0 | xargs -0 -r rm
git diff --name-only | xargs -r npx prettier --write

Le couple -print0 et -0 gère les noms de fichiers contenant des espaces, situation qui casse silencieusement la moitié des scripts trouvés en ligne. L'option -r, propre à la version GNU, évite de lancer la commande quand la liste est vide.

4. sed : remplacer dans un ou mille fichiers

sed -i 's/http:/https:/g' config.yml
grep -rlZ "ancienNom" src/ | xargs -0 -r sed -i 's/ancienNom/nouveauNom/g'

Le -Z de grep sépare les noms par un octet nul, comme le fait -print0 côté find. Sans lui, un fichier nommé « mon fichier.txt » devient deux arguments et sed échoue sur les deux. Sur macOS, la version BSD réclame un argument après -i : écris sed -i '' 's/.../.../'. Lance toujours la commande sans -i d'abord pour voir le résultat à l'écran.

5. awk : travailler par colonnes

awk -F',' '$3 > 100 {print $1, $3}' ventes.csv
awk '{somme += $2} END {print somme}' data.txt

Un fichier CSV de 500 Mo s'analyse ainsi sans ouvrir de tableur ni écrire de script Python, tant que les valeurs ne contiennent pas de virgule échappée entre guillemets, cas où awk se trompe de colonne et où un vrai parseur devient nécessaire. La variable $1 désigne la première colonne, -F définit le séparateur. Mon avis, après plusieurs sessions passées à voir des groupes se noyer dedans : apprendre awk comme un langage de programmation ne sert à rien pour un développeur web. Ces deux formes couvrent la quasi-totalité des besoins qui se présentent.

6. sort et uniq : trier et compter

grep "ERROR" app.log | sort | uniq -c | sort -rn | head -20

Cette ligne donne les vingt erreurs les plus fréquentes d'un fichier de log. Elle répond en trente secondes à une question qu'on repousse souvent pendant des semaines. À retenir : uniq ne regroupe que des lignes adjacentes, d'où le premier sort. Si les messages contiennent un horodatage, ils sont tous différents et le comptage ne donne rien : il faut d'abord isoler la partie stable du message avec cut ou awk.

7. cut : extraire un champ

cut -d':' -f1 /etc/passwd
cut -d',' -f2,5 export.csv

Plus limité qu'awk, plus rapide à taper quand le besoin se résume à récupérer une ou deux colonnes. Attention avec un séparateur espace : cut traite chaque espace comme un séparateur, donc deux espaces consécutifs créent un champ vide, là où awk les regroupe.

8. tail : suivre un fichier qui grossit

tail -f var/log/dev.log
tail -f app.log | grep --line-buffered "ERROR"

La seconde ligne affiche en direct uniquement les erreurs qui arrivent. L'option --line-buffered évite que grep retienne les résultats dans son tampon, sinon rien ne s'affiche pendant plusieurs minutes. Si le fichier est recréé par une rotation de logs, tail -F avec un F majuscule reprend le suivi sur le nouveau fichier.

9. less : lire un gros fichier sans le charger

less enorme.log
less +F enorme.log

Ouvrir un fichier de 3 Go dans un éditeur graphique fige la machine, less l'affiche instantanément parce qu'il ne lit que ce qu'il montre. La touche / lance une recherche, n passe à l'occurrence suivante, G saute à la fin et q quitte. La seconde forme, +F, démarre en mode suivi comme tail -f, et Ctrl+C rend la main pour naviguer dans ce qui est déjà écrit.

10. lsof et ss : savoir qui occupe un port

lsof -i :3000
ss -tulpn | grep 8080

Le message « address already in use » se résout en une commande au lieu d'un redémarrage de machine. Sur les distributions Linux récentes, ss, livré avec iproute2, remplace l'ancien netstat du paquet net-tools, qui n'est plus installé par défaut. Sur macOS, ss n'existe pas, lsof fait le travail. Afficher le nom du processus propriétaire demande parfois les droits administrateur.

11. kill et pkill : arrêter un processus

kill 4821
pkill -f "node server.js"

Commence toujours par un kill sans option : il envoie le signal TERM, qui demande au programme de se terminer et lui laisse le temps de fermer ses fichiers et ses connexions. Le -9 envoie KILL, que le processus ne peut pas intercepter, et peut laisser des données à moitié écrites. Avec pkill -f, vérifie d'abord la cible avec pgrep -af et le même motif, sinon tu tues plus large que prévu.

12. du et df : trouver ce qui remplit le disque

df -h
du -sh * | sort -rh | head

La seconde ligne classe le contenu du dossier courant du plus lourd au plus léger. Sur un serveur dont la partition est pleine, elle donne le coupable en une seule commande, souvent un dossier de logs jamais purgé ou un cache Docker. Le motif * ignore les fichiers cachés, donc pense à relancer avec du -sh .[!.]* * si le total ne colle pas.

13. tar : archiver et restaurer

tar -czf sauvegarde.tar.gz dossier/
tar -tzf sauvegarde.tar.gz | head
tar -xzf sauvegarde.tar.gz

Mnémotechnique qui a sauvé des générations : c pour créer, x pour extraire, t pour lister, z pour compresser, f pour nommer le fichier. La ligne du milieu vaut le détour avant toute extraction, parce qu'une archive mal faite déverse cinquante fichiers directement dans le dossier courant.

14. rsync et scp : copier vers un serveur

scp dump.sql utilisateur@serveur:/tmp/
rsync -avzn --delete build/ utilisateur@serveur:/var/www/site/

rsync ne transfère que les différences, ce qui rend une synchronisation répétée quasiment instantanée. Le n ajouté dans l'exemple active la simulation : la commande affiche ce qu'elle ferait sans rien modifier. Retire-le une fois la liste vérifiée. Attention à --delete, qui supprime côté serveur ce qui n'existe plus côté local, et à la barre oblique finale : avec build/ tu copies le contenu, avec build tu copies le dossier lui-même.

15. curl : tester une API sans quitter le terminal

curl -i https://api.exemple.fr/produits/12
 
curl -X POST https://api.exemple.fr/produits \
  -H "Content-Type: application/json" \
  -d '{"nom":"Clavier","prix":79}'

L'option -i affiche les en-têtes de réponse, indispensables pour comprendre un problème de cache ou de CORS, sujet détaillé dans comprendre HTTP en profondeur. Une commande curl se colle dans un ticket, se rejoue à l'identique et se glisse dans un script de test, ce qu'aucune capture d'écran d'outil graphique ne permet.

Quatre enchaînements qui servent chaque semaine

Les commandes prennent leur valeur quand on les branche entre elles. Voici quatre combinaisons qui reviennent dans une journée de développement ordinaire.

Repérer les fichiers les plus gros d'un projet, sans compter les dépendances installées :

find src -name "*.php" -print0 | xargs -0 wc -l | sort -n | tail -20

La dernière ligne affichée est un total calculé par wc, pas un fichier. Sur un projet à plusieurs milliers de fichiers, xargs découpe l'appel en plusieurs lots et plusieurs totaux apparaissent : c'est normal, ils s'ignorent.

Repérer les fichiers les plus modifiés du dépôt, souvent ceux qui concentrent la dette :

git log --name-only --pretty=format: | grep -v '^$' | sort | uniq -c | sort -rn | head

Le grep -v '^$' est indispensable : git insère une ligne vide entre chaque commit, et sans ce filtre le premier résultat du classement est le décompte de ces lignes vides.

Sortir la répartition des codes de réponse HTTP d'un serveur web, pour vérifier après une mise en production que le taux d'erreurs n'a pas bougé :

awk '{print $9}' access.log | sort | uniq -c | sort -rn

La position 9 correspond au code de statut dans le format combined d'Apache et de Nginx, celui livré par défaut. Si le format a été personnalisé, compte les champs sur une ligne du fichier avant de reprendre le numéro.

Mesurer le temps de réponse d'une API avant et après une optimisation, sans installer d'outil de mesure :

curl -o /dev/null -s -w "%{time_total}\n" https://api.exemple.fr/produits

Une seule mesure ne dit pas grand-chose, la première requête paie souvent l'ouverture de connexion et la mise en cache. Lance la ligne cinq fois de suite et compare les valeurs stabilisées.

Aucune de ces lignes ne demande de connaître un langage de script. Elles réutilisent les mêmes briques dans un ordre différent, ce qui explique pourquoi apprendre quinze commandes rapporte davantage que d'en apprendre cinquante : la valeur vient des combinaisons, pas du catalogue.

Les pièges

  • rm -rf avec une variable. Un rm -rf "$DOSSIER/" dont la variable est vide vise la racine. Vérifie toujours le contenu avec echo avant d'exécuter la suppression, et ajoute set -u en tête de tes scripts pour qu'une variable non définie arrête l'exécution.
  • Coller une commande trouvée en ligne sans la lire. Une ligne qui commence par curl ... | sudo bash exécute du code distant avec les droits administrateur, sans que personne n'ait vu ce code. Cette pratique est à l'origine de compromissions décrites dans application non sécurisée : les risques réels.
  • chmod 777 en réflexe. Donner tous les droits à tout le monde règle le symptôme et ouvre une porte. Le bon geste consiste à corriger le propriétaire du fichier avec chown, dans la logique du principe de moindre privilège.
  • Oublier les guillemets. Un nom de fichier avec un espace se transforme en deux arguments. La règle simple : entoure systématiquement tes variables de guillemets doubles.
  • Lancer une commande destructrice sans mode simulation. rsync a -n, find a -print avant -delete, sed s'exécute sans -i. Ces trois réflexes coûtent dix secondes et évitent des restaurations de sauvegarde.
  • Supposer que macOS et Linux se comportent pareil. sed, find, date et readlink diffèrent entre les versions BSD et GNU. Un script écrit sur un Mac peut échouer sur le serveur de production, ce que l'article « ça marche sur ma machine » détaille sous l'angle de la portabilité.

Ces quinze entrées forment aussi la base de tout le reste de la chaîne de déploiement. Un fichier Dockerfile, un job de CI, un script de sauvegarde ne contiennent rien d'autre que du shell. C'est ce qui fait le lien entre la formation Linux et ligne de commande pour développeurs et les sujets qui suivent, Docker puis CI/CD avec GitHub Actions. Le shell est la matière première du DevOps, et il continue de servir longtemps après la première mise en production réussie.

Trois suffisent pour commencer, choisies parmi les situations décrites plus haut. Pas quinze.

Questions fréquentes

Ces commandes fonctionnent-elles sous Windows ?

Pas dans l'invite de commandes classique ni dans PowerShell, qui utilisent une autre syntaxe. Installe WSL, le sous-système Linux intégré à Windows : tu obtiens un shell Linux complet, avec accès à tes fichiers Windows, et l'ensemble des exemples de cet article fonctionne tel quel. Un point de vigilance : les fichiers montés depuis le disque Windows sont nettement plus lents à parcourir que ceux stockés dans le système de fichiers Linux de WSL, ce qui se sent sur un find récursif dans un gros projet.

Bash ou Zsh, quelle différence ?

Zsh est le shell par défaut sur macOS depuis Catalina en 2019, Bash reste celui de la plupart des serveurs Linux. Les commandes présentées ici sont des programmes externes, elles se comportent de la même façon dans les deux. Les différences portent sur l'autocomplétion, l'affichage de l'invite et quelques subtilités de syntaxe dans les scripts, notamment l'indexation des tableaux qui commence à 1 en Zsh et à 0 en Bash.

Faut-il apprendre le shell quand une IA écrit les commandes ?

Un assistant produit rapidement une ligne plausible, et le shell l'exécute sans confirmation ni annulation possible. Savoir relire une commande avant de la lancer, repérer un rm -rf mal placé, un chemin absolu douteux ou un --delete qui ne devrait pas être là, devient plus utile que de mémoriser les options. La lecture compte davantage que l'écriture, principe développé dans l'article sur le vibe coding sans perdre le contrôle.

Comment retenir les options de chaque commande ?

Personne ne les retient toutes. Les développeurs expérimentés utilisent man commande, l'option --help, et surtout Ctrl+R pour retrouver une commande déjà tapée. Créer des alias dans ton fichier de configuration de shell pour les trois ou quatre lignes que tu réécris chaque semaine élimine le problème pour de bon.

Faut-il installer ripgrep, fd ou bat à la place des commandes classiques ?

Ces outils sont plus rapides et plus agréables sur un poste de travail, ripgrep ignore par exemple le contenu de .gitignore sans configuration. Ils ne sont pas installés sur le serveur que tu vas dépanner à 22h, ni dans l'image Docker minimaliste où tourne ton application. Apprends grep, find et less d'abord, ajoute les alternatives ensuite sur ta machine, en sachant reprendre les originales quand elles manquent.

Quand écrire un script plutôt qu'une ligne de commande ?

Dès la troisième fois que tu tapes la même séquence, ou dès qu'elle dépasse deux tubes enchaînés. Un fichier .sh versionné dans le dépôt du projet documente l'opération pour l'équipe et se relit six mois plus tard, ce qu'une ligne perdue dans un historique ne permet pas. Passé une centaine de lignes, ou dès qu'il faut manipuler du JSON, des dates ou des appels HTTP structurés, bascule sur Python : c'est l'objet de la formation Automatisation et scripting avec Python.

Vous êtes expert ?

Partagez votre expertise sur notre blog

Tutoriel, retour d'expérience, analyse - publiez un article invité et gagnez en visibilité.

Écrire pour nous