Grep et coupure d'une variable

Bonjour,

Bonjour,

Dans un fichier a.txt :

--------------
%SystemRoot%\abcd?.exe
%SystemRoot%\abcd.exe
%SystemRoot%\abc?.exe
%SystemRoot%\abcd1.exe
%SystemRoot%\abcdkz.exe
%SystemRoot%\abcd???.exe
---------------

comment faire pour trouver toutes les lignes qui répondent exactement à ces 3 critères :
- commencent par %SystemRoot%\abc,
- suivi d'un unique caractère quelconque,
- et enfin de l'expression ".exe" ?

ceci pour arriver à afficher simplement :

-----------
%SystemRoot%\abcd.exe
%SystemRoot%\abc?.exe
----------

Bien sur j'ai essayé un grand nombre de combinaisons avec grep -n, egrep -n et fgrep (il me faut les numéros de ligne), mais cela ne donne rien de convaincant. De plus je suis sur un pb bien plus complexe mais je 'aperçois déjà que ça coince à ce niveau

Merci pour votre aide

PS : je signale par respect pour les intervenants que j'avais posté le même sujet ici https://www.developpez.net/forums/d518055/systemes/linux/shell-commandes-gnu/grep-expression-reguliere/#post3148329 mais que malheureusement depuis j'ai pas avancé d'un chouilla
Configuration: Linux Suse
Firefox 2.0.0.14

17 réponses

  • Votes
  • Date
  • Date décroissante
  1. Modérateur
    Salut,
    grep -En '^%SystemRoot%\\abc.?\.exe'
    ou
    egrep -n '^%SystemRoot%\\abc.?\.exe'
    ;-))
    1. Contributeur
      J'ai pourtant l'impression que pour ce problème là particulièrement, tu avais réussi à avoir la bonne commande :

      grep -n '^%SystemRoot%\\abc.\.exe' a.txt

      Non ?

      1. merci pour vos réponses. Mais le problème réel est que %SystemRoot%\abc n'est pas connu, il est récupéré comme variable $i par une boucle for. Ce fichier n'est qu'un échantillon

        --------------
        for i in $(grep '\.exe$' a.txt)
        do echo `expr "$i" : "\(.*\)......$"` >> Exe.txt
        done
        -------------

        donne le fichier Exe.txt :

        ---------------
        %SystemRoot%\abcd
        %SystemRoot%\abc
        %SystemRoot%\abc
        %SystemRoot%\abcd
        %SystemRoot%\abcdk
        %SystemRoot%\abcd??
        -------------

        puis

        --------------
        for i in $(uniq -d Exe.txt); do echo "Suggestion : "$i"?.exe" >> Doublons.txt; fgrep -n "$i" a.txt >> Doublons.txt; echo >> Doublons.txt; done
        ----------------

        donne le fichier Doublons.txt

        --------------
        Suggestion : %SystemRoot%\abc?.exe
        1:%SystemRoot%\abcd?.exe
        2:%SystemRoot%\abcd.exe
        3:%SystemRoot%\abc?.exe
        4:%SystemRoot%\abcd1.exe
        5:%SystemRoot%\abcdkz.exe
        6:%SystemRoot%\abcd???.exe
        --------------

        et ce que je souhaite :

        ----------------
        Suggestion : %SystemRoot%\abc?.exe
        1:%SystemRoot%\abc?.exe
        2:%SystemRoot%\abcd.exe
        ----------------

        Désolé, j'aurais du donner le lien direct de la problématique majeure, https://www.developpez.net/forums/d518055/systemes/linux/shell-commandes-gnu/grep-expression-reguliere/#post3148049

        le but est de voir si en retranchant les 4 derniers caractères de chaque ligne du fichier a.txt, on n'obtient pas de doublons. Il faut arriver à matcher toutes ces lignes exéctement.
        1. Modérateur
          Essaye ça :
          grep -n "$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" a.txt
          ;-))
          1. ça ne semble pas marcher, ai-je bien placé ta suggestion :

            -----------------
            for i in $(uniq -d Exe.txt); do echo "Suggestion : "$i"?.exe" >> Doublons.txt; grep -n "$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" a.txt ; done
            -----------------

            résultat :

            -----------------
            1:%SystemRoot%\abcd?.exe
            2:%SystemRoot%\abcd.exe
            3:%SystemRoot%\abc?.exe
            4:%SystemRoot%\abcd1.exe
            5:%SystemRoot%\abcdkz.exe
            6:%SystemRoot%\abcd???.exe
            ---------------------

            résultat attendu :

            ---------------------
            2:%SystemRoot%\abcd.exe
            3:%SystemRoot%\abc?.exe
            ---------------------
            1. Modérateur
              [jp@MDK-2008 trash]$ cat plop 
              %SystemRoot%\abcd?.exe
              %SystemRoot%\abcd.exe
              %SystemRoot%\abc?.exe
              %SystemRoot%\abcd1.exe
              %SystemRoot%\abcdkz.exe
              %SystemRoot%\abcd???.exe
               
              [jp@MDK-2008 trash]$ var="%SystemRoot%\abc?.exe"
              [jp@MDK-2008 trash]$ grep -n "$(echo $var | sed 's/\\/\\\\/;s/.\./\.\\./')" plop 
              2:%SystemRoot%\abcd.exe
              3:%SystemRoot%\abc?.exe
              [jp@MDK-2008 trash]$
              ;-))
              1. Ok, mais il y a deux éléments qui ne vont pas dans le contexte :

                1. la "suggestion" n'est plus bonne :

                ---------------------
                for i in $(uniq -d Exe.txt); do echo "Suggestion : "$i"?.exe" ; grep -n "$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" a.txt ; done
                ---------------------

                ---------------------
                Suggestion : %SystemRoot%\ab?.exe
                ---------------------

                il devrait écrire :

                ---------------------
                Suggestion : %SystemRoot%\abc?.exe
                ---------------------

                2. Je n'obtiens pas la liste des lignes correspondant exactement aux critères du problème :

                ---------------------
                1:%SystemRoot%\abcd?.exe
                2:%SystemRoot%\abcd.exe
                3:%SystemRoot%\abc?.exe
                4:%SystemRoot%\abcd1.exe
                5:%SystemRoot%\abcdkz.exe
                6:%SystemRoot%\abcd???.exe
                ---------------------

                est-ce que tu arrives à obtenir ce résultat dans une boucle for ?
                1. Modérateur
                  Ok, je crois que j'ai cerné le problème, en fait le "grep" doit se faire sur le fichier "Doublons.txt" :
                  [jp@MDK-2008 trash]$ cat Doublons.txt 
                  Suggestion : %SystemRoot%\abc?.exe
                  1:%SystemRoot%\abcd?.exe
                  2:%SystemRoot%\abcd.exe
                  3:%SystemRoot%\abc?.exe
                  4:%SystemRoot%\abcd1.exe
                  5:%SystemRoot%\abcdkz.exe
                  6:%SystemRoot%\abcd???.exe
                  
                  [jp@MDK-2008 trash]$ grep "$(echo $var | sed 's/\\/\\\\/;s/.\./\.\\./')" Doublons.txt 
                  Suggestion : %SystemRoot%\abc?.exe
                  2:%SystemRoot%\abcd.exe
                  3:%SystemRoot%\abc?.exe
                  [jp@MDK-2008 trash]$ 
                  ;-))
                  1. je ne suis pas sur d'être très clair dans mes explications, probablement à cause du fait que je tourne en rond depuis des semaines sur ce problème. Désolé, je reprends tout, n'hésitez pas s'il y a des points obscurs dans la formulation :

                    1. Nous avons un fichier a.txt dont chaque ligne représente le chemin d'un fichier/dossier windows, en voici un échantillon :

                    --------------
                    %SystemRoot%\abcd?.exe
                    %SystemRoot%\abcd.exe
                    %SystemRoot%\abc?.exe
                    %SystemRoot%\abcd1.exe
                    %SystemRoot%\abcdkz.exe
                    %SystemRoot%\abcd???.exe
                    %SystemDrivet%\abcd???.dll
                    --------------

                    Le problème à résoudre est le suivant : recherche des doublons .exe en amputant un caractère précédant l'expression .exe, et proposer une suggestion de remplacement

                    a) dans un premier temps, on isole tous les fichiers exe, càd toutes les lignes se terminant par l'expression .exe, on leur ampute l'expression ".exe" et un caractère supplémentaire, puis on redirige le résultat dans le fichier Exe.txt :

                    --------------
                    for i in $(grep '\.exe$' a.txt)
                    do echo `expr "$i" : "\(.*\).....$"` >> Exe.txt
                    done
                    --------------

                    b) une fois ce fichier Exe.txt obtenu :

                    --------------
                    %SystemRoot%\abcd
                    %SystemRoot%\abc
                    %SystemRoot%\abc
                    %SystemRoot%\abcd
                    %SystemRoot%\abcdk
                    %SystemRoot%\abcd??
                    --------------

                    => on regarde s'il y a des doublons pour chaque ligne dans Exe.txt : si c'est bien le cas, alors il s'agit de rechercher dans le fichier d'origine a.txt les lignes où apparaissent exactement ces doublons, sachant qu'ils ne devront être précédés que d'un caractère quelconque, suivi de l'expression .exe, et en affichant la suggestion de substitution juste au-dessus :

                    par exemple, dans le fichier Exe.txt, les deux lignes

                    --------------
                    %SystemRoot%\abc
                    %SystemRoot%\abc
                    --------------

                    constiutent un doublon, il faut donc rechercher l'expression %SystemRoot%\abc + un caractère quelconque + l'expression .exe dans a.txt, et afficher les n° de ligne correspondants.

                    --------------
                    for i in $(uniq -d Exe.txt); do echo "Suggestion : "$i"?.exe" ; grep -n "$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" a.txt ; done
                    --------------

                    Le résultat souhaité serait donc, avec notre échantillon :

                    --------------
                    Suggestion :%SystemRoot%\abc?.exe
                    2: %SystemRoot%\abcd.exe
                    3: %SystemRoot%\abc?.exe
                    --------------

                    c'est important de rechercher ces doublons dans le fichier d'origine, car c'est dans ce dernier que vont s'opérer les substitutions (c'est aussi pour cela que j'utilise grep, pour avoir les n° de ligne avec l'option -n)
                    1. Bon, je pense avoir trouvé un début de solution, grâce à ta pise ; il était en effet nécessaire de substituer les \ par des \\ pour qu'ils passent correctement dans grep

                      _____________________
                      for i in $(grep '\.exe.$' a.txt)
                      do echo `expr "$i" : "\(.*\)......$"` >> Exe.txt
                      done

                      Ext=\.exe

                      for i in $(uniq -d Exe.txt)
                      do echo "=> Suggestion : "$i"?.exe" >> Doublons.txt
                      a=$(echo $i | sed 's/\\/\\\\/g')
                      egrep -n ^"$a.$Ext".$ a.txt >> Doublons.txt
                      done
                      ____________________

                      ça a l'air de fonctionner. Propre ou pas propre, selon toi ?
                      En jouant sur les . de `expr "$i" : "\(.*\)......$"` il sera possible de jouer sur la profondeur de recherche afin de déterminer s'il n'y a pas encore des doublons en amputant n caractères en deça de l'expression .exe

                      Juste un dernier truc que je pige pas, si je fais :

                      egrep -n ^"$a.$Ext"$ a.txt >> Doublons.txt

                      ça ne marche pas. En ajoutant un . ou même .$ ça marche. Pourtant, après l'expression .exe, il n'y a rien, c'est la fin de la ligne et il n'y a pas d'espace ou de tabulation en fin de ligne. Comment expliquer ça ?

                      Merci
                      1. Contributeur
                        Salut,

                        je suis sur un problème à peu près similaire, qui consiste, dans un fichier a.txt à :

                        1. isoler les dossiers (càd grossièrement les lignes qui ne contiennent pas de .)
                        2. pour chaque ligne du fichier a.txt, supprimer les lignes qui commencent par ces chemins de dossiers, mais qui incluent des sous-éléments

                        Voici le fichier :

                        cat a.txt
                        %AppData%\addon.dat
                        %AppData%\addon.dat
                        %AppData%\inside.exe
                        %AppData%\Microsoft\Windows\fkoym.exe
                        %AppData%\Microsoft\Windows\qganni.exe
                        %AppData%\Microsoft\Windows\vvpmyvaw.exe
                        %AppData%\SpeedRunner
                        %AppData%\SpeedRunner\config.cfg
                        %AppData%\SpeedRunner\SRUninstall.exe
                        %AppData%\WinTouch
                        %AppData%\WinTouch\wintouch.cfg
                        %AppData%\WinTouch\WinTouch.exe
                        %AppData%\WinTouch\WTUninstaller.exe


                        le but est donc d'éliminer ces lignes puisqu'elles représentent des sous-éléments de dossiers (%AppData%\SpeedRunner et %AppData%\WinTouch) :

                        %AppData%\SpeedRunner\config.cfg
                        %AppData%\SpeedRunner\SRUninstall.exe
                        %AppData%\WinTouch\wintouch.cfg
                        %AppData%\WinTouch\WinTouch.exe
                        %AppData%\WinTouch\WTUninstaller.exe


                        Voici l'une de mes centaines de tentatives :

                        for i in "$(egrep -v "\." a.txt)" ; do a="$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" ; cat a.txt | sed -e 's/$a.*$//g' ; done


                        évidemment ça ne marche pas, est-ce que quelqu'un aurait une piste supplémentaire. Attention, c'est encore un échantillon et les lignes peuvent contenir des espaces

                        Merci bien
                        1. Modérateur
                          Salut,
                          [jp@MDK-2008 trash]$ cat a.txt
                          %AppData%\addon.dat
                          %AppData%\addon.dat
                          %AppData%\inside.exe
                          %AppData%\Microsoft\Windows\fkoym.exe
                          %AppData%\Microsoft\Windows\qganni.exe
                          %AppData%\Microsoft\Windows\vvpmyvaw.exe
                          %AppData%\SpeedRunner
                          %AppData%\SpeedRunner\config.cfg
                          %AppData%\SpeedRunner\SRUninstall.exe
                          %AppData%\WinTouch
                          %AppData%\WinTouch\wintouch.cfg
                          %AppData%\WinTouch\WinTouch.exe
                          %AppData%\WinTouch\WTUninstaller.exe
                          
                          [jp@MDK-2008 trash]$ cat foo.sh
                          
                          #! /bin/bash
                          
                          for line in $(grep -v '\.' a.txt)
                          do
                                  sed -i -r '/'"${line/\\/\\\\}"'.+/d' a.txt
                          done
                          
                          [jp@MDK-2008 trash]$ ./foo.sh
                          [jp@MDK-2008 trash]$ cat a.txt
                          %AppData%\addon.dat
                          %AppData%\addon.dat
                          %AppData%\inside.exe
                          %AppData%\Microsoft\Windows\fkoym.exe
                          %AppData%\Microsoft\Windows\qganni.exe
                          %AppData%\Microsoft\Windows\vvpmyvaw.exe
                          %AppData%\SpeedRunner
                          %AppData%\WinTouch
                          
                          [jp@MDK-2008 trash]$
                          ;-))
                          1. Contributeur
                            Ok merci pour cette approche différente. Par contre pour conserver tel quel a.txt et rediriger le résultat dans un fichier u.txt comment faire?
                            1. Modérateur
                              C’est là que le bât blesse ;-(

                              Les modifications du fichier doivent être dynamique. Donc seule solution faire une copie du fichier original avant et travailler sur la copie...
                          2. Contributeur
                            Bon, d'accord.

                            Par contre et par curiosité, qu'est-ce que c'est qui n'allait pas avec la méthode :

                            for i in "$(egrep -v "\." a.txt)" ; do a="$(echo $i | sed 's/\\/\\\\/;s/.\./\.\\./')" ; cat a.txt | sed -e 's/$a.*$//g' ; done
                            1. Modérateur
                              Les guillemets autour de l'expression qui rassemblent tout dans une seule et même variable (à enlever donc) :
                              [jp@MDK-2008 trash]$ for i in "$(egrep -v "\." a.txt)" ; do echo $i;done
                              %AppData%\SpeedRunner %AppData%\WinTouch
                              
                              [jp@MDK-2008 trash]$ for i in $(egrep -v "\." a.txt) ; do echo $i;done
                              %AppData%\SpeedRunner
                              %AppData%\WinTouch

                              Puis les quotes simples dans l'expression avec "sed" qui empêchent l'évaluation de la variable :
                              sed -e 's/$a.*$//g'

                              à remplacer par des guillemets pour que la variable "$a" soit évaluée :
                              sed -e "s/$a.*$//g"


                              Ton script aurait du ressembler à ça :
                              for i in $(egrep -v "\." a.txt); do a=$(echo "$i" | sed 's/\\/\\\\/;s/.\./\.\\./'); cat a.txt | sed -e "s/$a.*$//g"; done

                              Mais même comme ça on retombe dans ce que j'ai essayé de t'expliquer au-dessus, il faut que "sed" agisse dynamiquement sur le fichier sinon on a un double affichage (un par variable traitée) :
                              $ for i in $(egrep -v "\." a.txt); do a=$(echo "$i" | sed 's/\\/\\\\/;s/.\./\.\\./'); cat a.txt | sed -e "s/$a.*$//g"; done
                              %AppData%\addon.dat
                              %AppData%\addon.dat
                              %AppData%\inside.exe
                              %AppData%\Microsoft\Windows\fkoym.exe
                              %AppData%\Microsoft\Windows\qganni.exe
                              %AppData%\Microsoft\Windows\vvpmyvaw.exe
                              
                              %AppData%\WinTouch
                              %AppData%\addon.dat
                              %AppData%\addon.dat
                              %AppData%\inside.exe
                              %AppData%\Microsoft\Windows\fkoym.exe
                              %AppData%\Microsoft\Windows\qganni.exe
                              %AppData%\Microsoft\Windows\vvpmyvaw.exe
                              %AppData%\SpeedRunner
                              
                          3. Contributeur
                            bon, en testant la même commande sur le fichier entier - qui contient des chemins de fichiers/dossiers comme précisé plus haut - j'obtiens deux erreurs actuellement

                            1. la première, c'est :

                            sed: -e expression n°1, caractère 55: Référence arrière invalide


                            conséquence : rien n'est supprimé

                            2. La deuxième, en ajoutant simplement ces deux chemins au fichier échantillon a.txt de tout à l'heure:

                            %ProgramFiles%\Microsoft Update
                            %ProgramFiles%\Microsoft Update\abc.txt


                            la commande efface les deux lignes, et non pas uniquement la deuxième

                            D'où peut venir le pb ?
                            1. Modérateur
                              j'obtiens deux erreurs actuellement
                              1. la première, c'est :
                              sed: -e expression n°1, caractère 55: Référence arrière invalide


                              Il me faut le fichier de référence ainsi que la syntaxe que tu emploies, sans quoi je pédale dans la choucroute ;-((

                              Concernant le 2nd point, rajoute ça dans le script :
                              #! /bin/bash
                              
                              IFS=$'\n'
                              for line in $(grep -v '\.' a.txt)
                              do
                                      sed -i -r '/'"${line/\\/\\\\}"'.+/d' a.txt
                              done
                              et ça devrait rouler, du moins chez moi ça le fait ;-))
                          4. Contributeur
                            Ok, tu as raison IFS=$'\n' règle bien le problème n°2

                            Concernant le 1, désolé effectivement sans le fichier c'est pas bien, par contre il fait plus de 3000 lignes, je te mets un lien si tu es ok http://www.alt-shift-return.org/Info/Fichiers/Temp/Coucou.txt.zip
                            La commande utilisée est celle que tu as suggéré, dans un terminal

                            for line in $(grep -v '\.' a.txt)
                            do
                                    sed -i -r '/'"${line/\\/\\\\}"'.+/d' a.txt
                            done
                            1. Modérateur
                              Bon effectivement, dans la mesure où il y a aussi plusieurs anti-slash à protéger il faut rajouter un slash dans l'expansion de la variable :
                              ${line//\\/\\\\}
                              Puis il m'a fallu convertir le fichier original (Coucou.txt) au format Unix (avec la commande dos2unix) et à partir de là, le script roule tout seul ;-))