Translation(s): English - Français - Italiano

(!) Discussion


Astuces pour faire des scripts shell

Note : Le wiki Debian n'est peut-être pas le meilleur endroit pour trouver de l'aide dans la création de scripts shell. Si vous rencontrez des problèmes vous devriez vous reporter à la documentation du shell que vous utilisez (bash, zsh, tcsh, dash,...).

Comment voir l'espace disque utilisé ?

df
df -h # affichage lisible par les humains (en mega et giga octets)

Mon système est plein. Comment puis-je voir où se trouvent les plus gros fichiers ?

cd /quelquepart
du -sk *
# Répéter autant que nécessaire

Une autre voie :

find /quelquepart -size +2000k -ls
# Cela montre tout les fichiers plus lourds que 2000 kilo octets dans /quelquepart

Si vous voulez garder vos .deb, vous pouvez filtrer la liste ainsi :

find / -size +2000k -ls | awk ' substr($NF, length($NF) - 3, 4) != ".deb" '
# Cela montre tout les fichiers sur votre système plus lourds que 2000 kilo octets qui ne sont pas des paquets Debian

Si vous voulez voir les gros répertoires au lieu des fichiers, vous pouvez le faire ainsi :

du -x /quelquepart | sort -n | tail -10
# Cela montre les 10 plus gros répertoires dans /quelquepart

Je possède beaucoup de mp3... Comment puis-je tous les renommer en remplaçant les espaces par des tirets bas « _ » ?

rename 's/ /_/g' *.mp3

La commande {rename} n'est pas une commande Unix classique mais elle est incluse dans perl qui est installé par défaut dans Debian bien sûr.

Comment puis-je le faire de manière récursive ?

C'est un peu compliqué. Vous pouvez récupérer la liste des fichiers à passer à {rename} grâce à {find}, mais si vous renommer les répertoire avant les fichiers qu'ils contiennent, {rename} ne réussira pas. Vous devez utiliser {-depth} avec {find} pour être sûr que les fichiers seront renommés avant les répertoires qui les contiennent.

cd /quelquepart
find . -depth -name '* *' -type f -print0 | xargs -r0 rename 's/ /_/g'

Avec la version 4 de bash (et zsh) vous pouvez utiliser les « globing » (exemple pour les mp3) :

rename 's/ /_/g' **/*.mp3

Comment faire pour renommer (récursivement) tout les fichiers en majuscule pour qu'ils soient en minuscule ?

Encore une fois, cela est difficile si les répertoires eux-mêmes ont des lettres majuscules dans leurs noms. Supposons pour le moment qu'ils n'en ont pas. Alors :

cd /quelquepart
find . -name '*[A-Z]*' -type f -print0 | xargs -0 rename 'y/A-Z/a-z/' 

Malheureusement, chaque fois que cette question surgit, ceci n'est souvent pas assez - la personne qui demande ayant généralement aussi des majuscules dans les noms de ses répertoire. Ainsi, il peut être nécessaire de faire quelque chose comme cela :

cd /quelquepart
find . -type d -depth -name '*[A-Z]*' -print |
  while read dir; do dname="$(dirname $dir)"; bname="$(basename $dir)";
  newbname="$(echo $bname | tr [:upper:] [:lower:])"; mv "$dir" "$dname/$newbname"; done
# Cela renomme les répertoires. L'option -depth fait que ceci commence d'abord par les répertoires les plus profonds, donc nous n'avons pas besoin de les renommer un à un.
find . -name '*[A-Z]*' -type f -print0 | xargs -0 rename 'y/A-Z/a-z/' 

Notez que ce script complexe ci-dessus est imparfait : il échouera si jamais un des répertoires a des sauts de lignes dans son nom, et il peut aussi échouer dans certains autres conditions telles que si un répertoire ou un fichier possède des espaces dans son nom. A utiliser à vos risques et périls. (Astuce : si vous voulez au préalable tester sans rien modifier, mettez la commande echo devant la commande mv.)

En fait, cette solution bien plus simple, pourrait mieux fonctionner :

find /quelquepart -depth -name '*[A-Z]*' -print0 | xargs -r0 rename 'y/A-Z/a-z/'

Comment faire pour supprimer un fichier dont le nom commence par « - » ?

unlink -monfichier

Trois autres façons :

rm -- -monfichier
rm ./-monfichier
# Utiliser mc, et appuyer sur F8 lorsque le fichier en question est sélectionné

La deuxième façon fonctionne avec tout les programmes en ligne de commande et pas uniquement {rm}.

Comment faire pour voir le contenu d'un fichier en temps réel ?

Si c'est un fichier de log, on regarde la fin du fichier :

tail -f /var/log/messages

ou

less +F /var/log/messages

Si c'est une commande générale, utilisez {watch} :

watch -n 1 ls -l ~/un/fichier

Comment récupérer le nom du fichier dans un chemin complet ?

Deux solutions :

basename /chemin/vers/un/fichier
var=/chemin/vers/un/fichier ; echo ${var##*/} 

Comment tester si un dossier possède des fichiers ?

if [ "$(ls -A undossier)" ]; then
    echo "Il a des fichiers"
fi

Note : un seul argument dans les crochets (« [] ») est équivalent à [ -n argument ]. Il ne semble y avoir aucun moyen propre pour le faire en utilisant les commandes intégrées du shell. (Nous avons fait une tentative antérieure qui était proche, mais qui a échouée, si il y avait un fichier nommé * dans le répertoire.)

Une autre technique :

if [ "$(ls -A undossier | wc -l)" -gt 0 ]; then
    echo "Fichiers trouvés"
fi

De façon brève : [ $(ls -A somedir) ] && echo "le dossier n'est pas vide"

Note : La double esperluette (&) représente un « et » booléen, alors le « if » n'est pas nécessaire.

Comment lancer un processus en tâche de fond ?

Il suffit d'ajouter une esperluette à la fin :

sleep 100 &

Ceci va afficher une ligne ressemblant à :  [1] 6338 , vous indiquant que ce processus est la tâche numéro 1 que son identifiant processus est le n° 6338.

Pour tuer cette tâche de fond :

kill %1 

ou

kill 6338 

A noter: Si vous avez démarré plusieurs processus en tâches de fond et que vous souhaitez les lister, exécutez pour cela la commande jobs.

Vous pouvez également basculer une tâche de fond au premier plan, la suspendre, puis la redémarrer comme tâche de fond. Démarrons un processus en tâche de fond :

sleep 100 &

Pour la déplacer au premier plan (en supposant qu'elle est la tâche n°1) :

fg %1

Pressez ensuite Ctrl+Z. Ceci suspend le processus et le met en tâche de fond. Maintenant, relancez le processus tout en le laissant en tâche de fond :

bg %1

Pour tuer la tâche :

kill %1

Comment travailler avec des tableaux en bash ?

Initialiser un tableau :

arr=(un deux trois quatre)
myoggs=(*.ogg)

Pour itérer sur un tableau :

for f in "${myoggs[@]}"; do ...; done

Choisir une case au hasard :

mysigs=($HOME/sigs/*)
cat ${mysigs[RANDOM % ${#mysigs[*]}]}

(Dans l'exemple précédent, notez que les crochets servent aussi à forcer l’évaluation numérique, dans celui-ci RANDOM n'a pas besoin de $.)

Vous ne pouvez pas facilement supprimer un élément d'un tableau, mais vous pouvez contourner le problème en utilisant la commande unset sur un élément du tableau :

arr=(zero un deux trois quatre)
unset arr[1]
arr=("${arr[@]}")

Cela permettra aussi laisser tous les éléments vides en place.

Comment utiliser une variable dans une variable (interpolation de variables) en bash ?

Si vous voulez faire quelque chose comme ${$var}, utilisez le format ${!var}.

Ainsi, si vous avez :

FOO=one
BAR=FOO

Alors, ${!BAR} retournera « one »

Cela ne fonctionne que pour le référencement à une variable existante, pas pour l'affectation. Si vous souhaitez affecter à un nom de variable généré dynamiquement, vous devez utiliser à la place eval :

IFACE=eth0
eval IP_${IFACE}=192.168.1.1

ou bien, pour de nombreux cas d'usages, utilisez juste un tableau :

N=17
ROW[N]="This is the 18th row (counting from 0)."

L'astuce de l'emploi d'eval peut également être utilisée pour référencer :

IFACE=eth0
eval echo \$IP_${IFACE}

eval fait alors deux passages à travers le code. Le premier passage substitue $ à \$ et ensuite substitue la valeur de ${IFACE}, laissant « eval echo $IP_eth0 ». Le second passage substitue alors la valeur de la variable ${IP_eth0}, en supposant qu'une telle variable existe.

Comment compter le nombre de ligne dans les fichiers qui ont une certaine extension en bash ?

find $dir -name *.[$extensions] -exec cat \{\} \; | wc -l

Ceci trouve tous les fichiers dans le répertoire $dir qui ont l’extension $extensions et lit ces fichiers en utilisant la commande « cat », puis utilise « wc -l » afin de compter les lignes du texte.

wc -l *.txt
# Obtenir le nombre de lignes de tous les fichiers dont l’extension est txt