Forum Programmation.shell Extraire un bout de chaîne

Posté par  .
Étiquettes : aucune
0
27
fév.
2008
Bonjour,

Je cherche des fichiers qui portent tous le même nom. Dans le chemin d'accès à ces fichiers, pas toujours au même niveau de l'arborescence, se trouve la date de création du fichier. Je dois faire une copie de ces fichiers en les renommant en utilisant la date.

Jusqu'à présent, j'ai utilisé ce script parceque la date avait toujours la même place dans le chemin d'accès ( ex :/home/truc/bidule/20060612/machin/le_fichier_que_je cherche)
J'utilisais donc ce scritp :

#!/bin/bash
# Cherche dans tous les repertoires enfants un fichier
# Quand une occurence du fichier est trouvee
# Extrait la date du chemin d'acces
# Copie le fichier dans /resultat en utilisant la date extraite pour le renommer.

extension='PACA1.nc.gz'

for fic in `find -name CHIMERE_PACA1.nc.gz`
do
date=`echo ${fic:9:8}`
nouveau_nom=$date$extension
cp $fic resultat/$nouveau_nom
echo resultat/$nouveau_nom [OK]
done

La position de la date dans l'arborescence est maintenant variable, je voudrais donc chercher dans la chaine de caractère "200" et copier les 8 caractères qui suivent dans une variable.
J'ai regardé du coté de sed, mais je n'ai pas trouvé comment faire.

Raph
  • # trouvé

    Posté par  . Évalué à 1.

    Je m'auto répond, je crois qu'un truc de la forme :

    # obtenir l'adresse de retour dans l'entête
    sed '/^Reply-To:/q; /^From:/h; /./d;g;q'

    ... pourrait aider :D
  • # Regexps ...

    Posté par  . Évalué à 4.

    Hello,

    Pour extraire une date formée d'exactement huit chiffres consécutifs, tu peux utiliser un truc du style :

    date=`echo $fic | sed -e "s/^.*\/\([0-9]\{8\}\)\/.*$/\1/g`

    Mais vérifie d'abord que tu n'as jamais plus de sept chiffres consécutifs autre part dans le chemin.

    A bientôt.
    • [^] # Re: Regexps ...

      Posté par  (site web personnel) . Évalué à 2.

      On peut contrôler les extémités, et restreindre un peu la regexp de date (du coup, ça ne marchera plus après 2099 :)

      ~ $ chemin='./abc/20010101/xy/12345678910/ssss20123456'
      ~ $ echo "A${chemin}Z" |sed -e 's/.*[^0-9]\(20[0-9][0-9][01][0-9][0123][0-9]\)[^0-9].*$/\1/g'
      20010101
      • [^] # Re: Regexps ...

        Posté par  . Évalué à 2.

        Merci également Jacques,

        je ne comprend pas trop le fonctionnement du contrôle des extrémités :
        X caractères quelconques [débute par un chiffre] (champ 1) [débute par un chiffre] Y caractères quelconques

        Merci !
        Raph
        • [^] # Re: Regexps ...

          Posté par  (site web personnel) . Évalué à 2.

          • J'ajoute deux lettres (des non-chiffres) autour de la chaîne à tester ;
          • et je construit la regexp
            1. le début, terminé par un non-chiffre : ^.*[^0-9]
            2. entre parenthèses \(...\), la sous-regexp de date du XXIème siècle (avec une vingtaine de mois qui peuvent avoir quarante jours, mais bon...)
            3. un non-chiffre, et la suite jusqu'à la fin.
            Sans les lettres d'encadrement, il faudrait remplacer le début de la regexp par ^\(.*[^0-9]\)\? pour le rendre facultatif, et changer le numéro du bloc conservé, qui passerait en \2 --- de même pour la fin, qui pourrait aussi être vide. Remarque : comme le premier .* est « avide », la date extraite est la dernière de la chaîne. Si ça ne convient pas, les regexps de Perl sont plus souples.
          • [^] # Re: Regexps ...

            Posté par  . Évalué à 2.

            Je comprend,

            je n'avais pas comprit que [^0-9] est le complément. C'est plus clair :)

            Par contre je ne comprend pas l'intérêt de '$' (fin de ligne ?)
          • [^] # Re: Regexps ...

            Posté par  . Évalué à 2.

            Ma date sera toujours encadré par des /, est ce que sed -e 's/.*\/\(200[0-9][01][0-9][0-3][0-9]\).*/\1/g' marche aussi sans encadrer la variable par des lettres ?

            Merci en tout cas, j'ai beaucoup appris :)
            • [^] # Re: Regexps ...

              Posté par  (site web personnel) . Évalué à 1.

              Oui --- les lettres en rab sont juste une précaution générale ; dans ce cas précis, si tu es sûr d'avoir tes huit chiffres de date entre deux /, c'est encore mieux de remplacer les deux balises « non-chiffre » [^0-9]. Comme c'est aussi le délimiteur habituel de la substitution de Sed, il suffit d'utiliser un autre caractère (sinon, il faut taper \/ comme tu l'as fait, c'est déjà assez illisible comme ça :).
              La commande

              sed -e 's,^.*/\(20[0-9][0-9][01][0-9][0-3][0-9]\)/.*$,\1,'

              devrait marcher ; j'ai laissé les ancres ^ et $ pour la lisibilité, mais elles sont superflues, et l'option g n'est pas utile, il n'y a qu'une substitution possible.

              J'ai aussi remplacé le deuxième zéro, sinon ça ne marcherait plus dès 2010.
    • [^] # Re: Regexps ...

      Posté par  . Évalué à 1.

      Merci beaucoup Obsidian,

      Est ce que je peux exagérer et te demander plus de détails sur la commande pour comprendre un peu son fonctionnement ?
      Voilà ce que je comprend :


      s/ : substitution
      ^.* : la chaîne commence par x caractères quelconques
      / : séparateur pour ?
      ( [0-9] {8} ) : 8 caractères compris entre 0 et 9
      / : je ne comprend pas la structure générale de la commande
      .* : nombre de caractère variable
      ??
      1/g : on remplace la chaine par le résultat de la commande


      à quoi sert le $ ? pourquoi un "\" avant le 1 ?

      s/debut de chaine / ce qu'on veut extraire / fin de chaine

      Merci pour votre aide
      • [^] # Re: Regexps ...

        Posté par  . Évalué à 1.

        décidément ...

        à quoi sert le $ ? pourquoi un "\" avant le 1 ?
        Je m'auto répond grâce à wikipédia : \1 représente le champs 1.
        • [^] # Re: Regexps ...

          Posté par  . Évalué à 3.

          Bravo pour avoir fait l'effort de chercher. C'est encore trop rare. Sinon, je conçois complètement que la syntaxe de sed est obscure quand on n'a pas l'habitude.

          Côté format des expressions régulières sous Unix :

          ^ en début d'expression représente le début de la ligne. Ca permet de se référer à ce début plutôt qu'à n'importe quelle position. Juste après un crochet ouvrant, il complémente l'ensemble, sinon, c'est un caractère ordinaire.
          $ en fin d'expression représente la fin de la ligne. Sinon, c'est un caractère ordinaire.

          La syntaxe de sed pour la commande s (substitute) est :

          commande/motif à retrouver/motif de remplacement/options

          Bien que n'importe quel caractère puisse servir de séparateur, le slash est le plus fréquement utilisé. Cependant, lorsqu'une expression contient elle aussi des slashes, il faut les alors échapper avec un contre-slash "\".

          Ensuite, certains méta-caractères des expressions régulières doivent toujours êtres échappés aussi pour fonctionner, sinon, ils sont pris pour des caractères normaux. Pour d'autres, c'est le contraire.

          Les opérateurs à échapper sont +,?,{,},(,) (ainsi que le slash si utilisé comme séparateur, et les chiffres, pour rappeler les motifs).
          Les opérateurs à ne pas échapper sont *,^,[,],.

          Les parenthèses, au niveau des regexps, servent à former des groupes sur lequels on peut appliquer des opérateurs (de l'algèbre classique, quoi), mais sed reconnaît également les groupes de premier niveau (ceux qui ne sont pas déjà eux-mêmes entre parenthèses), et permet de les rappeler ensuite avec \1, \2, \3, ...

          De là :

          ^.*\/\([0-9]\{8\}\)\/.*$ correspond en fait à ^.*/([0-9]{8})/.*$, ce qui signifie "début de ligne, puis n'importe quel groupe de caractère puis un slash (échappé), puis un groupe de huit chiffrs exactement, puis un slash, puis n'importe quel groupe de caractère à nouveau, enfin une fin de ligne".

          On met le groupe de huit chiffres entre parenthèses pour pouvoir le retrouver ensuite. Et on lui accole des slashes qui sont en fait ceux du chemin d'accès, de façon à retrouver nom un répertoire qui soit effectivement formés de huit chiffres, et non qui contienne huit chiffres.

          Cette expression reconnaît la ligne entière, le motif de substition va donc la remplacer entièrement. Le \1 dans le deuxième champ indique que le nouveau motif est la date extraite uniquement. Tout le reste disparaît, donc.

          L'option g signifie greedy. Elle sert à lever une ambigüité des expressions régulières. Quand plusieurs motifs identiques se suivent et que l'on utilise *, l'analyseur ne sait pas s'il faut s'arrêter au premier motif reconnu, ou au contraire tous les lire jusqu'à ce que ça ne corresponde plus. Par défaut, il se cantonne au premier cas, le mode greedy le fait opter pour le second.

          Dans le cas de sed, c'est différent mais quand même similaire : sed stoppe l'opération une fois que la première substitution est faite. Avec g, il lira la ligne entière et substituera chaque fois que c'est possible.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.