--- page corrigée --- //[[rcjcgg@gmail.com|oh!rocks]] 2009/05/09 19:30// =====grep, egrep : recherche sur fichiers===== Pouvoir isoler rapidement un élément dans un fichier de configuration, dans la sortie d'une commande ou dans un fichier texte quelconque est un atout essentiel sur un système GNU-Linux pour lequel "tout est fichier" (citation de [[http://fr.wikipedia.org/wiki/Ken_Thompson|Kenneth Thompson]], père du système UNIX). C'est ce que proposent les commandes **grep** et **egrep**.\\ "egrep" était à l'origine une version étendue et améliorée de "grep". "egrep" supporte les [[http://fr.wikipedia.org/wiki/Expression_rationnelle|expressions régulières]] étendues. Avec "grep" l'option "-E" permet également de supporter les expressions régulières étendues. Une alternative "fgrep" existe, à l'origine plus rapide que "grep" mais ne supportant absolument pas les expressions régulières. "fgrep" ne cherchant des chaînes de caractères que de manière littérale, a donc peu d'intérêt, si ce n'est une plus grande économie de ressources sur des machines très anciennes. L'option "-F" de "grep" donne un comportement similaire, c'est d'ailleurs le comportement par défaut. Depuis 2001 la norme [[http://fr.wikipedia.org/wiki/POSIX|POSIX]] définie les options "-E" et "-F" comme les standards, "egrep" et "fgrep" sont officiellement démodés, mais toujours disponibles et largement utilisés.\\ Un signe $ précède les commandes qui ne nécessitent pas de droits administrateur ; un signe # précède celles qui nécessitent des droits administrateur (ces signes ne font PAS partie des commandes). Les lignes qui ne commencent pas par un signe $ ou # correspondent au résultat de la commande précédente.\\ Les touches utilisées sont indiquées entre crochets, exemple **[ctrl]** pour la touche "contrôle" ====usages simples : recherche dans un fichier texte==== Imaginons que nous voulions savoir si le système de sécurité "selinux" est compilé dans notre noyau. Nous allons __chercher la chaîne de caractère__ "selinux" dans le fichier de configuration du noyau "/boot/config-$(uname -r)" :\\ $ egrep selinux /boot/config-$(uname -r) Oups ! La commande ne renvoie rien alors que l'option "selinux" est forcément présente qu'elle soit activée ou non... Cet échec s'explique facilement : les options de configuration du noyau sont notées en majuscules. Or, __"egrep" et "grep" sont sensibles à la casse__. Pour obtenir le résultat il faudrait écrire "SELINUX" ou mieux, utiliser l'option "**-i**" qui __permet d'ignorer la casse__ (l'expression est cherchée qu'elle soit en minuscules ou majuscules).\\ $ egrep -i selinux /boot/config-$(uname -r) CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 # CONFIG_SECURITY_SELINUX_DISABLE is not set CONFIG_SECURITY_SELINUX_DEVELOP=y CONFIG_SECURITY_SELINUX_AVC_STATS=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX=y CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE=19 C'est mieux ! Mais vu que la configuration du noyau est un document conséquent, il serait intéressant d'avoir les __numéros de lignes des résultats trouvés__... C'est possible avec l'option "**-n**" :\\ $ egrep -in selinux /boot/config-$(uname -r) 3350:CONFIG_SECURITY_SELINUX=y 3351:CONFIG_SECURITY_SELINUX_BOOTPARAM=y 3352:CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 3353:# CONFIG_SECURITY_SELINUX_DISABLE is not set 3354:CONFIG_SECURITY_SELINUX_DEVELOP=y 3355:CONFIG_SECURITY_SELINUX_AVC_STATS=y 3356:CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 3357:CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX=y 3358:CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE=19 Maintenant, __cherchons deux motifs différents__ dans cette même configuration pour savoir si les solutions de virtualisation "xen" et/ou "kvm" sont compilés :\\ $ egrep -in '(kvm|xen)' /boot/config-$(uname -r) 202:# CONFIG_XEN is not set 203:CONFIG_KVM_CLOCK=y 204:CONFIG_KVM_GUEST=y 1334:CONFIG_NETXEN_NIC=m 3465:CONFIG_HAVE_KVM=y 3467:CONFIG_KVM=m 3468:CONFIG_KVM_INTEL=m 3469:# CONFIG_KVM_AMD is not set Les guillemets simples qui entourent l'expression évitent que le shell interprète les caractères spéciaux (les parenthèses et le "|" tube). Si vous voulez ajouter un troisième motif de recherche, ajoutez simplement un "|" (tube) et indiquez le motif :\\ $ egrep -in '(kvm|xen|virtio)' /boot/config-$(uname -r) 202:# CONFIG_XEN is not set 203:CONFIG_KVM_CLOCK=y 204:CONFIG_KVM_GUEST=y 878:CONFIG_VIRTIO_BLK=m 1334:CONFIG_NETXEN_NIC=m 1562:CONFIG_VIRTIO_NET=m 1753:# CONFIG_VIRTIO_CONSOLE is not set 1763:CONFIG_HW_RANDOM_VIRTIO=m 3465:CONFIG_HAVE_KVM=y 3467:CONFIG_KVM=m 3468:CONFIG_KVM_INTEL=m 3469:# CONFIG_KVM_AMD is not set 3470:CONFIG_VIRTIO=m 3471:CONFIG_VIRTIO_RING=m 3472:CONFIG_VIRTIO_PCI=m 3473:CONFIG_VIRTIO_BALLOON=m Simple. Pour obtenir le même résultat avec "grep" il faudrait utiliser la syntaxe :\\ $ grep -i -e kvm -e xen -e virtio /boot/config-$(uname -r) Ce qui est moins élégant.\\ Si maintenant vous voulez __chercher un seul motif mais dans plusieurs fichiers__, il suffit d'en indiquer les chemins à la suite, séparés par un espace :\\ # egrep nobody /etc/group /etc/gshadow /etc/group:lp:x:7:nobody,tux /etc/group:nobody:x:1002: /etc/gshadow:nobody:!:: Si maintenant vous désirez __chercher dans tous les fichiers__ à l'intérieur de /etc (option "-R"), le mot "staff" __de manière stricte__ (pas de mots formés avec "staff", option "-w"), __en excluant le répertoire__ /etc/alternatives __et tout répertoire dont le nom contient__ "dictionarie" (dictionnaire)(options %%--%%exclude-dir="), le tout __avec un résultat en couleurs__ pour en améliorer la lisibilité (option "%%--colorise%%" :\\ # egrep -Rw --color --exclude-dir=alternatives --exclude-dir=*dictionarie* staff /etc /etc/group-:staff:x:50: /etc/gshadow-:staff:*:: /etc/gshadow:staff:*:: /etc/group:staff:x:50: /etc/htdig/english.0:staff/DGMRSZ /etc/group.org:staff:x:50: Pour __chercher les lignes commençant par__ "net" dans le fichier /etc/sysctl.conf, utilisez le motif **^** :\\ $ egrep ^net /etc/sysctl.conf net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.rp_filter=1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.conf.all.secure_redirects = 1 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.log_martians = 1 Et pour __les lignes finissant par__ "1", utilisez le motif **$**:\\ $ egrep 1$ /etc/sysctl.conf net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.rp_filter=1 #net.ipv4.tcp_syncookies=1 #net.ipv4.ip_forward=1 #net.ipv6.conf.all.forwarding=1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.conf.all.secure_redirects = 1 net.ipv4.conf.all.log_martians = 1 kernel.sysrq = 1 Voici un petit mémo des options utiles et des caractères pouvant servir à construire des motifs de recherche :\\ ^ options egrep ^ usage ^^^ | -i | ignorer la casse | | -n | indiquer le numéro de ligne | | -v | chercher les lignes qui n'incluent PAS le motif de recherche | | -R | chercher de manière récursive tous les fichiers du(des) répertoire(s) | | -w | ne chercher le motif QUE comme mot entier | | --exclude-dir= | exclure un répertoire de la recherche (utile en recherche récursive) | | --color | colorie le résultat pour en améliorer la lisibilité | | -B n | donne les "n" lignes du fichier AVANT le résultat trouvé ("egrep -B 5" donnera cinq lignes avant) | | -A n | donne les "n" lignes du fichier APRÈS le résultat trouvé ("egrep -A 5" donnera cinq lignes après) | | -C n | donne les "n" lignes de contexte AVANT ET APRÈS le motif trouvé ; deux tirets "%%--%%" séparent les résultats | ^ motifs spéciaux ^ usage ^^^ | %%^%% | chercher une ligne commençant par (exemple: egrep %%^%%net /etc/sysctl.conf)| | $ | chercher une ligne finissant par (exemple: egrep 1$ /etc/sysctl.conf) | ===="grep" et "egrep" utilisés comme filtres==== Un usage très courant de "grep" et "egrep" est celui de filtre servant à sélectionner une partie du résultat d'une autre commande. Il suffit pour cela d'utiliser une redirection de la commande originale vers "grep". L'opérateur de redirection généralement utilisé est le "tube" ("pipe" en anglais) "**|**". Par exemple, voici une recherche du module uvcvideo (pilote pour webcam) dans le résultat de "lsmod" (liste les modules en fonction) :\\ $ lsmod | grep uvcvideo uvcvideo 55740 0 videodev 40656 1 uvcvideo v4l1_compat 12612 2 uvcvideo,videodev Pour lister uniquement les répertoires sur un chemin donné (ici /etc) :\\ $ ls -l /etc | grep ^d Ou pour les exclure (chercher uniquement les fichiers et liens) :\\ $ ls -l /etc | grep -v ^d Le résultat de "ls -l" commence par la lettre "d" s'il s'agit d'un répertoire ("directory" en anglais) : c'est cette caractéristique qui est utilisée par "grep". On cherche d'abord les lignes commençant par "d" (^d), puis on met une négation à cette condition dans la deuxième commande (on liste les "non-répertoires") avec l'option "-v".\\ Si on veut isoler parmi les messages système ceux concernant l'usb, on fera :\\ $ dmesg | grep -i usb Certaines commandes nécessitent des astuces. Par exemple, pour filtrer le résultat de "ps aux" en recherchant les lignes commençant par "1000" (pid de l'utilisateur courant) et sans que "grep" et "ps" n'apparaissent parmi les processus listés :\\ $ ps aux | egrep -vw '(ps|egrep)' | egrep 1000 On utilise la négation (-v) du motif de recherche (ps|egrep). On utilise l'option "stricte" (-w) pour éviter de masquer des processus qui contiendrait la séquence "ps". Bien entendu, si vous aviez un script qui fonctionne en arrière-plan et qui utilise massivement "egrep", il n'apparaîtrait pas non plus... C'est le revers de la méthode.\\ Une alternative astucieuse est d'utiliser systématiquement une expression régulière, même pour un motif de recherche simple :\\ $ ps aux | egrep [1]000 L'expression "[1]000" est vraie pour le chiffre "1000", mais pas pour les caractères "[1]", donc "egrep" n'apparaîtra pas dans les résultats.\\ __Pour rechercher une expression dans des fichiers OpenOffice__ ".odt" avec "find" et "grep". Cela dépasse le cadre de l'usage de "grep", mais cela peut s'avérer pratique et montre comment combiner la commande de recherche "find" et "grep". On prendra l'exemple d'un répertoire /home/tux/Doc/ contenant de nombreux fichiers ".odt" ("Oasis Document Text", le format de Ooo Writer). On recherchera l'expression "Saint Valentin". Cette commande tire partie du fait que les ".odt" sont en réalité des fichiers zippés (compressés en ".zip") dont le contenu textuel est stocké dans un fichier "content.xml":\\ $ find /home/tux/Doc/ -name '*.odt' -exec sh -c 'unzip -c "{}" content.xml | grep -qi "saint valentin"' ";" -print Attention à ne pas oublier les guillemets simples et doubles qui sont indispensables pour éviter que le shell n'interprète certains caractères spéciaux.\\ Pour adapter à vos besoins, remplacez simplement le chemin /home/tux/Doc par votre répertoire, et "saint valentin" par l'expression que vous recherchez. L'option "-i" permet de s'assurer que l'expression sera trouvée même si la casse est différente (majuscules...). Le nom du fichier dans lequel l'expression a été trouvé vous sera renvoyé en cas de succès.\\ J'espère que vous êtes convaincus de l'utilité de "grep" et "egrep", et surtout plus à l'aise dans leur maniement ! ;-) ==== Liens ==== [[http://www.opengroup.org/onlinepubs/009695399/utilities/grep.html|Définition de "grep" selon la norme POSIX.]]\\ ------------------------------------------------------------------------------------- ==================================================================== [[http://www.linuxpedia.fr/forum/index.php?action=pm;sa=send;u=15|Envoyer un message au mainteneur]] ==================================================================== [[commande:commande:|Retour au sommaire "La ligne de commande"]]