Forum Programmation.shell cat /etc/fstab | fgrep -v $udi > /etc/fstab

Posté par  .
Étiquettes : aucune
0
10
sept.
2007
Bonjour à tous,

Voilà, j'ai une petite ligne de shell script qui plante parfois, et je n'arrive pas à comprendre pourquoi:

cat /etc/fstab | fgrep -v $udi > /etc/fstab

Cette ligne est simplement supposée retirer toutes les lignes comprenant $udi (qui est toujours défini) du fichier fstab, $udi n'étant pas un motif complexe (une simple chaîne de caractères).

Parfois, et c'est hélas non reproduisible, je me retrouve avec un fstab totalement vide au lieu d'avoir juste le contenu du fichier fstab sans la ligne contenant $udi.

J'ai trouvé une solution de dépannage en passant par un fichier temporaire, puis un déplacement, mais j'aimerais bien comprendre ce qui cloche.

Merci.
  • # google et cat

    Posté par  . Évalué à 3.

    il semblerait que les options choisies ne soient pas les plus pertinentes.

    le sujet a deja été abordé dans un autre forum et une recherche te donnerait des pistes.

    entre autre ...

    ta manip suppose que l'ouverture du fichier pour lecture se fasse entierement avant l'ouverture de ce meme fichier en ecriture.

    la solution du fichier temporaire confirme cela,
    ouverture du fichier en entrée -> lecture -> debut de traitement -> ouverture du fichier de sortie -> suite du traitement -> fin de lecture -> fin de traitement -> fin d'ecriture.

    il y a des solutions comme les redirections multiples, les jonglages avec les options, et d'autres solutions que cat/grep
    • [^] # Re: google et cat

      Posté par  . Évalué à 2.

      Effectivement, c'est bien ce que je pensais. Je connais $| en perl qui permet de faire un vidage à chaque E/S, mais pas l'équivalent en shell.

      Bon, je viens de trouver une alternative avec sed (sed -i), je teste ça.

      En revanche, pour les options, je suis perplexe, même après avoir regardé la Quick ref card de shell script.
      • [^] # Re: google et cat

        Posté par  . Évalué à 2.

        Ca y est:

        sed -i "#$udi#d" /etc/fstab

        Les '#' sont nécessaires car udi contient '/'.

        Quand je pense que j'avais regardé sd pendant deux heures sans rien y trouver...
        • [^] # Re: google et cat

          Posté par  . Évalué à 2.

          Bon, je suis fatigué (déjà), et ce code est faux. Le bon code se trouve en-dessous.

          Merci de moinsser ce code !
          • [^] # Re: google et cat

            Posté par  . Évalué à 2.

            sed -i -e s#$EDI##g ton_fichier


            -i pour qu'il fasse lui meme le fichier temporaire
            -e pour executer le script sed qui suit

            s#XXX#YYY#g pour remplacer tout les XXX par YYY partout dans le fichier
  • # warf

    Posté par  . Évalué à 4.

    d'un côté un cat qui liste un fichier,
    de l'autre une redirection qui écrase le contenu de ce même fichier...

    L'ouverture de la redirection se faisant en premier, le contenu du fichier est avant tout écrasé.
    • [^] # Re: warf

      Posté par  . Évalué à 3.

      C'est un problème intéressant.

      sed -i est une solution à ce problème comme dit plus haut, mais au final, on ne fait que le contourner et pour une autre commande ça ne marchera pas.

      Personne n'a une vraie solution qui garderait la puissance du pipe ?
      • [^] # Re: warf

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

        Salut,

        la seule vraie solution, c'est de passer par un fichier temporaire.
        sache que quand tu utilises des pipes, toutes les commandes sont exécutées *en même temps*, et non pas l'une après l'autre...
        donc en gros, la dernière commande va écrire dans le fichier pendant que la première le lit. Le comportement est indéterminé, et généralement se solde par un fichier vide (à cause justement du ">").

        Pour l'utilisation d'un fichier temporaire, tu peux faire ça de façon plus ou moins "transparente" avec zsh :

        fgrep -v "$udi" =(cat /etc/fstab) > /etc/fstab

        le =(...) va demander à zsh de créer un fichier temporaire (typiquement sous /tmp/zshXXXXX) contenant le résultat de la commande entre parenthèses.
        Et hop, plus de souci. :)


        La solution la plus propre et la plus portable consiste à faire une lecture d'une copie du /etc/fstab, copie que tu peux créer dans ton /tmp (comme cela tu peux éventuellement ne pas te casser le chou à supprimer le fichier).
        • [^] # Re: warf

          Posté par  . Évalué à 1.

          C'est ce que j'avais fait. Ça marche, mais je trouve le code avec "sed -i" plus élégant. Plus qu'à espérer que sed tienne le coup...
    • [^] # Re: warf

      Posté par  . Évalué à 2.

      Je ne savais pas que la redirection était ouverte en premier - merci de me l'apprendre. Ce qui était le plus étonnant, c'est le côté aléatoire de la chose: la plupart du temps, ça marchait, mais parfois ça plantait et je perdais tout mon fstab à cause de ça.

      Bon, j'ai trouvé un bon remplacement avec sed -i.

      Comme $udi contient des '/', je crée une nouvelle variable $udi_sed qui échappe tous les slashs, car malheureusement, il semble impossible d'utiliser un autre caractère de délimitation avec l'extension d (du genre #...#d).

      udi_sed=$(echo $udi | sed "s#/#\\\/#g")

      Et ensuite, un coup de sed:

      sed -i "/$udi_sed/d" /etc/fstab

      Pour info, udi vient de HAL (unique device identifier); il faudra que je regarde du côté des règles de nommage d'udi pour vérifier qu'il ne peut pas inclure '*', ',' ou tout autre caractère faisant partie des expressions régulières / rationnelles...
      • [^] # Re: warf

        Posté par  . Évalué à 3.

        malheureusement, il semble impossible d'utiliser un autre caractère de délimitation avec l'extension d (du genre #...#d).

        "d" se met à la fin du pattern à matcher, donc on ne peut pas savoir à l'avance quels sont tes délimiteurs, contrairement à "s//" qui est au début.
      • [^] # Re: warf

        Posté par  . Évalué à 3.

        Tu peux utiliser un autre caractère que '/' en échappant le premier, ainsi: sed -i "\#$udi#d" /etc/fstab En revanche sed interprète ce qu'il y a entre // (ou entre \##) comme une expression régulière, tu ferais mieux d'échapper quand même les '.', '*' et compagnie... Je peux aussi te proposer: echo "`fgrep -v $udi /etc/fstab`" > /etc/fstab Comme ça t'es sûr que fstab sera fermé en lecture avant d'être ouvert en écriture.

Suivre le flux des commentaires

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