IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[bash] Problème sur un programme en bash


Sujet :

Shell et commandes GNU

  1. #1
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut [bash] Problème sur un programme en bash
    Bonjour,

    Un collègue de bureau m'avait fait un code en bash qui me permet de supprimer ou commenter une ligne à l'aide d'un "$" dans un fichier A lorsque le nombre dans le fichier B est trouvé dans le fichier A.

    Pour être plus précis, j'ai un fichier dans lequel j'ai des nombres les uns en dessous des autres comme ceci (coorespond à mon fichier B):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    2115050
    2165410
    2125953
    2000000
    5200025
    1523215
     
    etc.......
    Le programme va lire chaque nombre et va les chercher dans mon fichier A qui se présente comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    FORCE       2500 2115050       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2115061       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2095050       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2095060       0      1.-2448.27 2683.11 5590.67
    $
    $				
    $
    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    FORCE    2500    2165410 0      5394.09 -.003393.323574 -.946197
    FORCE    2500    2165420 0      2686.05 -.003393.323574 -.946197
    FORCE    2500    2165430 0      5509.89 -.003393.323574 -.946197
    FORCE    2500    2175410 0      5395.21 -.003393.323574 -.946197
    FORCE    2500    2175420 0      2687.27 -.003393.323574 -.946197
    FORCE    2500    2175430 0      5509.98 -.003393.323574 -.946197
    $
    FORCE    2600    270800  0      612.051 -.003669.985618 -.16895
    FORCE    2600    270801  0      689.983 -.003804.98932  -.145711
    FORCE    2600    270804  0      689.596 -.003805.989918 -.141593
    FORCE    2600    270830  0      79.359  -.005185.999987 4.7803-5
    FORCE    2600    270831  0      158.767 -.005185.999987 1.3383-4
    FORCE    2600    270832  0      158.769 -.005154.999987 1.7677-4
    FORCE    2600    270930  0      2868.73  0.     .490462 -.871463
    FORCE    2600    270940  0      4572.04  0.     .469962 -.882687
    FORCE    2600    270950  0      7981.49  0.     .383634 -.923485
    FORCE    2600    280010  0      6617.53 -.007605 0.     .999971
    FORCE    2600    280020  0      9923.29 -.007618-.052568.998588
    FORCE    2600    280030  0      13226.3 -.007669-.104955.994447
    FORCE    2600    280040  0      13226.4 -.007769-.174333.984656
    FORCE    2600    280050  0      13225.8 -.007911-.242862.970029
    FORCE    2600    280060  0      13225.5 -.008097-.310206.950635
    FORCE    2600    280070  0      13225.4 -.008332-.376028.926571
    FORCE    2600    280080  0      13224.6 -.008619-.43998 .897966
    FORCE    2600    280090  0      13224.  -.008947-.501783.864947
    FORCE    2600    280100  0      13581.7 -.009323-.562686.826618
    FORCE    2600    280110  0      13572.5 -.009735-.61915 .785213
    FORCE    2600    280120  0      13205.3 -.010175-.673957.738701
    Les nombres cherché se trouve dans le troisième champ

    Voici le code qui permet de faire cela:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
     
    #!/bin/ksh
    #
    #
    #--------------------------------------------------------------------------------
    # INPUTS
    #--------------------------------------------------------------------------------
    dat_files=""
     
    # Get inputs
    ls -al
    echo "-- INPUTS --"
    echo " This script removes nodes force/moment in dat files"
    echo "Give DAT files:"
    read dat_files
    echo "Give nodes IDs list to remove:"
    read target_node_ids
    target_node_ids=`cat $target_node_ids`
    # Check inputs valididy
    TMP_VAR=""
    for file in $dat_files
    do
    	if [ -f $file ] ; then
    		TMP_VAR="$TMP_VAR $file"
    	else
    	  echo ""
    	  echo " File $file doesn't exist."
    	fi
    done
    dat_files=$TMP_VAR
     
    if [ "$dat_files" = "" ] ; then
    	echo "No DAT input files !"
    	exit
    fi
     
    # Ask for confirmation
    echo ""
    echo "Selected DAT files:"
    for file in $dat_files
    do
    	echo $file
    done
    echo "Selected nodes IDs to remove: $target_node_ids"
    echo ""
    echo "Proceed (y)?"
    read answer
    if [ "$answer" != "y" ] ;then
    	exit
    fi
    #
    #--------------------------------------------------------------------------------
    # PROCESS
    #--------------------------------------------------------------------------------
    #
    for curr_dat_file in $dat_files
    do
    	echo ""
    	echo "File "$curr_dat_file
    	# Copy DAT File
    	cp $curr_dat_file $curr_dat_file"_old"
    	# Process each target node
    	target_node_count=0
    	# FORCE/MOMENT cards need to be in small format!
    	pos_index=9
    	((pos_index_end=$pos_index+8))
    	grep "^FORCE " $curr_dat_file | cut -c$pos_index-$pos_index_end | sed "s/[ ]//g" > tmp_col.tmp
    	for curr_target_node in $target_node_ids
    	do
                    target_node_count=$(($target_node_count+1))
    		k=`grep $curr_target_node tmp_col.tmp | wc -l`
    		if [ $k -ge 1 ] ; then
    			i=1
    			while  [ $i -le $k ]
    			do
    				raw_to_delete=`grep "^FORCE[0-9 ].*$curr_target_node" $curr_dat_file | head -n $i`
    ###########
    # 1=removal
    #				sed "/^$raw_to_delete$/d" $curr_dat_file > tmp
    ###########
    # 2=comment
    				sed "s/$raw_to_delete/\$$raw_to_delete/g" $curr_dat_file > tmp
    ###########
    				mv tmp $curr_dat_file
    				echo " Following card has been commented = <"$raw_to_delete">"
    				i=$(($i+1))
    			done
    		fi
            done
    	echo $target_node_count" nodes removed"
    done
    rm -f tmp_col.tmp
    #
    echo ""
    echo "Job done!"
    #--------------------------------------------------------------------------------
    # END OF FILE
    #--------------------------------------------------------------------------------
    Mon soucis est le suivant, lorsqu'un nombre du fichier B existe plus de deux fois dans mon fichier A le fichier de sortie est vide.

    Je ne suis pas expert je comprend plus ou moins le code mais là je suis bloqué.

    quelqu'un pour m'aider m'aiguiller ?

    Ps : Je n'ai plus contact avec ce collègue sinon je me serai adressé à lui directement

    Merci d'avance

  2. #2
    Membre expérimenté Avatar de fransoo
    Inscrit en
    Novembre 2009
    Messages
    209
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 209
    Par défaut
    J'essayerais quelque chose avec grep, du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -f "fichier B" "fichier A"
    qui sortira toutes les lignes ayant l'occurence extraite du fichier B.
    C'est le contraire qu'on veut ! on va donc envoyer la sortie pour la comparer avec le fichier A :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -f "fichier B" "fichier A" | diff -y --suppress-common-lines "fichier A" -
    Attention, le tiret à la fin signale qu'on utilise STDIN comme fichier N°2.
    On obtient, je pense, quelque chose de très proche du résultat escompté, en une seule ligne de commande. Toute la puissance de *nix
    P.S.
    Selon l'usage final, d'autres outils comme awk, join ou sed pourraient être encore plus efficaces.

  3. #3
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Par défaut
    Citation Envoyé par fransoo Voir le message
    J'essayerais quelque chose avec grep, du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -f "fichier B" "fichier A"
    qui sortira toutes les lignes ayant l'occurence extraite du fichier B.
    C'est le contraire qu'on veut !
    Donc quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -fv "fichier B" "fichier A"
    Sauf que tu oublies une contrainte ici, c'est la 3eme colonne...
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  4. #4
    Membre expérimenté Avatar de fransoo
    Inscrit en
    Novembre 2009
    Messages
    209
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 209
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Donc quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -fv "fichier B" "fichier A"
    Sauf que tu oublies une contrainte ici, c'est la 3eme colonne...
    Si la chaîne est susceptible d'apparaître dans une autre colonne, il faudra alors utiliser awk, sinon, ce n'est pas nécessaire.
    J'ai essayé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -fv "fichier B" "fichier A"
    mais comme grep lit le fichier B ligne à ligne la non-occurence des chaînes arrivera toujouirs à être juste, c'est donc tout le fichier qui sort. C'est ce que j'en ait déduit quand j'ai essayé.

  5. #5
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut
    En fait la partie du code à regarder est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    #--------------------------------------------------------------------------------
    # PROCESS
    #--------------------------------------------------------------------------------
    #
    for curr_dat_file in $dat_files
    do
    	echo ""
    	echo "File "$curr_dat_file
    	# Copy DAT File
    	cp $curr_dat_file $curr_dat_file"_old"
    	# Process each target node
    	target_node_count=0
    	# FORCE/MOMENT cards need to be in small format!
    	pos_index=9
    	((pos_index_end=$pos_index+8))
    	grep "^FORCE " $curr_dat_file | cut -c$pos_index-$pos_index_end | sed "s/[ ]//g" > tmp_col.tmp
    	for curr_target_node in $target_node_ids
    	do
                    target_node_count=$(($target_node_count+1))
    		k=`grep $curr_target_node tmp_col.tmp | wc -l`
    		if [ $k -ge 1 ] ; then
    			i=1
    			while  [ $i -le $k ]
    			do
    				raw_to_delete=`grep "^FORCE[0-9 ].*$curr_target_node" $curr_dat_file | head -n $i`
    ###########
    # 1=removal
    #				sed "/^$raw_to_delete$/d" $curr_dat_file > tmp
    ###########
    # 2=comment
    				sed "s/$raw_to_delete/\$$raw_to_delete/g" $curr_dat_file > tmp
    ###########
    				mv tmp $curr_dat_file
    				echo " Following card has been commented = <"$raw_to_delete">"
    				i=$(($i+1))
    			done
    		fi
            done
    	echo $target_node_count" nodes removed"
    done
    rm -f tmp_col.tmp
    #
    echo ""
    echo "Job done!"
    #--------------------------------------------------------------------------------
    # END OF FILE
    #--------------------------------------------------------------------------------
    Mon soucis est le suivant : Imaginons qu'a un moment je tombe sur le nombre 9999999 présent dans mon fichier B.

    La routine va chercher dans mon fichier A dans le troisième champ le si le nombre correspondant est le même .... Si c'est le cas il supprime ou commente la ligne en question à l'aide de ces lignes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    ###########
    # 1=removal
    #				sed "/^$raw_to_delete$/d" $curr_dat_file > tmp
    ###########
    # 2=comment
    				sed "s/$raw_to_delete/\$$raw_to_delete/g" $curr_dat_file > tmp
    ###########
    Cependant pour être sur que le nombre n'est pas présent plusieurs fois j'imagine que le programme continu de regarder chaque champ jusqu'a la fin du fichier. Lorsque le nombre est présent une seconde fois le programme fonctionne correctement et supprime ou commente cette seconde ligne en question. Le problème intervient lorsque le nombre est présent une troisième fois ..... A ce moment le programme continu de tourner mais le fichier de sortie est nul (vide). A mon avis il y a un soucis sur une boucle ... Mais je ne sais pas trop laquelle.

    J'espère avoir été plus précis.

    Encore merci de votre aide

  6. #6
    Membre expérimenté Avatar de fransoo
    Inscrit en
    Novembre 2009
    Messages
    209
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 209
    Par défaut
    Je dirais que le problème doit se situer au niveau de sed (qui parcourt tout le fichier A). À essayer : enlever le g de global dans sed
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "s/$raw_to_delete/\$$raw_to_delete/g"
    pour avoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "s/$raw_to_delete/\$$raw_to_delete/"
    car je pense qu'il n'y a qu'une occurence par ligne et sed parcourt TOUTES les lignes de toute façon.

  7. #7
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    290
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 290
    Par défaut
    Bonjour,

    Voici une proposition pour votre problème. Elle s'appuie sur l'option -n de grep et sur la possibilité pour sed de ne modifier qu'une ligne du fichier d'entrée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #!/bin/bash
     
    [[ -f fichier_a.dat.bak ]] || cp fichier_a.dat fichier_a.dat.bak
     
    echo "GO"
     
    # Dans le code le fichier_b correspond a votre fichier B et fichier_a.dat a
    # votre fichier A
     
    # On trie et de doublonne les enregistrements du fichier B pour eviter de faire
    # plusieurs fois le commentaire. On boucle sur chaque entree du fichier B
    sort -u fichier_b | while read num_to_del
    do
        # Pour chaque entree, on extrait les lignes du fichier A correspondantes.
        # L'option -n fait prefixer la ligne par son numero dans le fichier
        grep -n $num_to_del fichier_a.dat | while read line_to_del
        do
            echo $line_to_del
            # J'extrais le numero de la ligne a modifier. C'est de forme ^[0-9]+:
            line_number=`echo $line_to_del | cut -d: -f1`
            echo "line number = $line_number"
            # Si le 3e champ de la ligne a commenter est effectivement egal au
            # nombre du fichier B, on modifie uniquement cette ligne.
            if [[ `echo $line_to_del | awk '{ print $3 }'` = $num_to_del ]]
            then
                echo "commentaire de $line_to_del (ligne $line_number)"
                # Traduction : remplace dans la ligne $line_number le debut de
                # ligne par $
                sed -e "${line_number}s/^/\$/" fichier_a.dat > fichier_a.dat.new
                mv fichier_a.dat.new fichier_a.dat
            fi
        done
    done
     
    echo "DONE"
    Dans votre code, il y a beaucoup d'utilisations de boucles avec compteur ce qui n'est pas un style super lisible pour du shell unix. Je préfère miser d'avantage sur l'utilisation du "pipe" qui est quand même la killer feature en shell !

  8. #8
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut
    Citation Envoyé par fransoo Voir le message
    Je dirais que le problème doit se situer au niveau de sed (qui parcourt tout le fichier A). À essayer : enlever le g de global dans sed
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "s/$raw_to_delete/\$$raw_to_delete/g"
    pour avoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "s/$raw_to_delete/\$$raw_to_delete/"
    car je pense qu'il n'y a qu'une occurence par ligne et sed parcourt TOUTES les lignes de toute façon.
    Merci de ta réponse, mais même en retirant le g dans le sed j'ai toujours le même problème.

    voici l'erreur que j'ai en rouge:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Following card has been commented = <FORCE 2500 2018970 0 825.246 -.798277.382362 -.465353>
     Following card has been commented = <FORCE 2500 2020000 0 875.53 -.618341 0. .78591>
     Following card has been commented = <FORCE 2500 2020200 0 362.956 -.707072 0. .707142>
    sed: -e expression #1, char 66: unterminated `s' command Following card has been commented = <FORCE 2500 2020200 0 1.-2147.99-288.875 2017.05 FORCE 2500 2020200 0 1.-2147.99 288.875 2017.05>
     Following card has been commented = <>
     Following card has been commented = <>
     Following card has been commented = <>
     Following card has been commented = <>
     Following card has been commented = <>
    En fait le numero 2020200 est présent 3 fois dans mon fichier A :

    1ère fois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FORCE 2500 2020000 0 875.53 -.618341 0. .78591
    2ème fois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FORCE 2500 2020200 0 362.956 -.707072 0. .707142
    3ème fois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FORCE 2500 2020200 0 1.-2147.99-288.875 2017.05
    Lorsque je retire à la main une des 3 lignes précédente et que je relance le script, tout se passe bien. J'en conclus donc que lorsqu'il doit supprimer plus de 3 fois une ligne contenant le même nombre il beugue.

    Encore merci

  9. #9
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut
    Citation Envoyé par Drawingrom Voir le message
    Bonjour,

    Voici une proposition pour votre problème. Elle s'appuie sur l'option -n de grep et sur la possibilité pour sed de ne modifier qu'une ligne du fichier d'entrée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #!/bin/bash
     
    [[ -f fichier_a.dat.bak ]] || cp fichier_a.dat fichier_a.dat.bak
     
    echo "GO"
     
    # Dans le code le fichier_b correspond a votre fichier B et fichier_a.dat a
    # votre fichier A
     
    # On trie et de doublonne les enregistrements du fichier B pour eviter de faire
    # plusieurs fois le commentaire. On boucle sur chaque entree du fichier B
    sort -u fichier_b | while read num_to_del
    do
        # Pour chaque entree, on extrait les lignes du fichier A correspondantes.
        # L'option -n fait prefixer la ligne par son numero dans le fichier
        grep -n $num_to_del fichier_a.dat | while read line_to_del
        do
            echo $line_to_del
            # J'extrais le numero de la ligne a modifier. C'est de forme ^[0-9]+:
            line_number=`echo $line_to_del | cut -d: -f1`
            echo "line number = $line_number"
            # Si le 3e champ de la ligne a commenter est effectivement egal au
            # nombre du fichier B, on modifie uniquement cette ligne.
            if [[ `echo $line_to_del | awk '{ print $3 }'` = $num_to_del ]]
            then
                echo "commentaire de $line_to_del (ligne $line_number)"
                # Traduction : remplace dans la ligne $line_number le debut de
                # ligne par $
                sed -e "${line_number}s/^/\$/" fichier_a.dat > fichier_a.dat.new
                mv fichier_a.dat.new fichier_a.dat
            fi
        done
    done
     
    echo "DONE"
    Dans votre code, il y a beaucoup d'utilisations de boucles avec compteur ce qui n'est pas un style super lisible pour du shell unix. Je préfère miser d'avantage sur l'utilisation du "pipe" qui est quand même la killer feature en shell !
    Merci, en plus bien commenté. Je vais regarder ça et je te tiens au courant.

  10. #10
    Membre expérimenté Avatar de fransoo
    Inscrit en
    Novembre 2009
    Messages
    209
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 209
    Par défaut
    Citation Envoyé par nekcorp Voir le message
    1ère fois : FORCE 2500 2020000 0 875.53 -.618341 0. .78591

    2ème fois : FORCE 2500 2020200 0 362.956 -.707072 0. .707142

    3ème fois : FORCE 2500 2020200 0 1.-2147.99-288.875 2017.05

    Lorsque je retire à la main une des 3 lignes précédente et que je relance le script, tout se passe bien. J'en conclus donc que lorsqu'il doit supprimer plus de 3 fois une ligne contenant le même nombre il beugue.
    J'ai seulement de la pein à comprendre pourquoi cela beugue à la troisième occurence : Quelle est la logique ? la raison be serait-elle pas autre ?

    Qu'est-ce que ça donne si on met la troisième occurence de la ligne en deuxième ou première place dans le fichier ?

    À part ça je persiste à penser que awk serait l'outil le plus indiqué pour effectuer la tâche demandée. Il y a plusieurs tutos, même en français, sur le net et j'ai souvent avantageusement remplacé des combiaisons de boucles bash avec des grep et des sed par de simples expressions awk.
    (je ne connais pas bien awk mais l'utilise au cas par cas avec toujours des résultats étonnants). C'est l'outil idéal pour l'analyse de BDD en texte.

  11. #11
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut
    Citation Envoyé par fransoo Voir le message
    J'ai seulement de la pein à comprendre pourquoi cela beugue à la troisième occurence : Quelle est la logique ? la raison be serait-elle pas autre ?

    Qu'est-ce que ça donne si on met la troisième occurence de la ligne en deuxième ou première place dans le fichier ?

    À part ça je persiste à penser que awk serait l'outil le plus indiqué pour effectuer la tâche demandée. Il y a plusieurs tutos, même en français, sur le net et j'ai souvent avantageusement remplacé des combiaisons de boucles bash avec des grep et des sed par de simples expressions awk.
    (je ne connais pas bien awk mais l'utilise au cas par cas avec toujours des résultats étonnants). C'est l'outil idéal pour l'analyse de BDD en texte.
    J'essaie de comprendre également. Je fais des tests pour essayer de trouver le beugue.

    Je vous tiens informé.

  12. #12
    Membre éclairé Avatar de nekcorp
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2006
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2006
    Messages : 592
    Par défaut
    Citation Envoyé par Drawingrom Voir le message
    Bonjour,

    Voici une proposition pour votre problème. Elle s'appuie sur l'option -n de grep et sur la possibilité pour sed de ne modifier qu'une ligne du fichier d'entrée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #!/bin/bash
     
    [[ -f fichier_a.dat.bak ]] || cp fichier_a.dat fichier_a.dat.bak
     
    echo "GO"
     
    # Dans le code le fichier_b correspond a votre fichier B et fichier_a.dat a
    # votre fichier A
     
    # On trie et de doublonne les enregistrements du fichier B pour eviter de faire
    # plusieurs fois le commentaire. On boucle sur chaque entree du fichier B
    sort -u fichier_b | while read num_to_del
    do
        # Pour chaque entree, on extrait les lignes du fichier A correspondantes.
        # L'option -n fait prefixer la ligne par son numero dans le fichier
        grep -n $num_to_del fichier_a.dat | while read line_to_del
        do
            echo $line_to_del
            # J'extrais le numero de la ligne a modifier. C'est de forme ^[0-9]+:
            line_number=`echo $line_to_del | cut -d: -f1`
            echo "line number = $line_number"
            # Si le 3e champ de la ligne a commenter est effectivement egal au
            # nombre du fichier B, on modifie uniquement cette ligne.
            if [[ `echo $line_to_del | awk '{ print $3 }'` = $num_to_del ]]
            then
                echo "commentaire de $line_to_del (ligne $line_number)"
                # Traduction : remplace dans la ligne $line_number le debut de
                # ligne par $
                sed -e "${line_number}s/^/\$/" fichier_a.dat > fichier_a.dat.new
                mv fichier_a.dat.new fichier_a.dat
            fi
        done
    done
     
    echo "DONE"
    Dans votre code, il y a beaucoup d'utilisations de boucles avec compteur ce qui n'est pas un style super lisible pour du shell unix. Je préfère miser d'avantage sur l'utilisation du "pipe" qui est quand même la killer feature en shell !
    A première vue ça marche.

    Mais j'aurai aimé un peu d'explication concernant cette ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     # On trie et de doublonne les enregistrements du fichier B pour eviter de faire
    # plusieurs fois le commentaire. On boucle sur chaque entree du fichier B
    sort -u fichier_b | while read num_to_del
    Merci

  13. #13
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    290
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 290
    Par défaut
    sort -u trie et supprime les doublons.

    En envoie ensuite dans le while read <variable> qui met le contenu de chaque ligne dans <variable> à chaque itération.

  14. #14
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    290
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2006
    Messages : 290
    Par défaut
    Citation Envoyé par fransoo Voir le message
    J'ai seulement de la pein à comprendre pourquoi cela beugue à la troisième occurence : Quelle est la logique ? la raison be serait-elle pas autre ?
    Je n'ai pas testé mais à mon avis, c'est là que le bât blesse :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    raw_to_delete=`grep "^FORCE[0-9 ].*$curr_target_node" $curr_dat_file | head -n $i`
    On ne maîtrise plus ce qu'il y a dans raw_to_delete si $i est supérieur à 1. Et sed est plutôt sensible...

    Citation Envoyé par fransoo Voir le message
    À part ça je persiste à penser que awk serait l'outil le plus indiqué pour effectuer la tâche demandée.
    Ouais, j'ai essayé de regarder pour le fun... en vain. Pour un truc comme ça je partirais direct sur du python aujourd'hui (perl au pire). Avec un awk compliqué, on est certain que personne ne comprendra rien au traitement !

  15. #15
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 651
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 651
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    $ cat fichierB
    2115050
    2165410
    2125953
    2000000
    5200025
    1523215
    $ awk '{if(NR==FNR){a[$1]++}else{print $3 in a ? "$"$0 : $0}}' fichierB fichierA
    $FORCE       2500 2115050       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2115061       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2095050       0      1.-2448.27 2683.11 5590.67
    FORCE       2500 2095060       0      1.-2448.27 2683.11 5590.67
    $
    $
    $
    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    $FORCE    2500    2165410 0      5394.09 -.003393.323574 -.946197
    FORCE    2500    2165420 0      2686.05 -.003393.323574 -.946197
    FORCE    2500    2165430 0      5509.89 -.003393.323574 -.946197
    FORCE    2500    2175410 0      5395.21 -.003393.323574 -.946197
    FORCE    2500    2175420 0      2687.27 -.003393.323574 -.946197
    FORCE    2500    2175430 0      5509.98 -.003393.323574 -.946197
    $
    FORCE    2600    270800  0      612.051 -.003669.985618 -.16895
    FORCE    2600    270801  0      689.983 -.003804.98932  -.145711
    FORCE    2600    270804  0      689.596 -.003805.989918 -.141593
    FORCE    2600    270830  0      79.359  -.005185.999987 4.7803-5
    FORCE    2600    270831  0      158.767 -.005185.999987 1.3383-4
    FORCE    2600    270832  0      158.769 -.005154.999987 1.7677-4
    FORCE    2600    270930  0      2868.73  0.     .490462 -.871463
    FORCE    2600    270940  0      4572.04  0.     .469962 -.882687
    FORCE    2600    270950  0      7981.49  0.     .383634 -.923485
    FORCE    2600    280010  0      6617.53 -.007605 0.     .999971
    FORCE    2600    280020  0      9923.29 -.007618-.052568.998588
    FORCE    2600    280030  0      13226.3 -.007669-.104955.994447
    FORCE    2600    280040  0      13226.4 -.007769-.174333.984656
    FORCE    2600    280050  0      13225.8 -.007911-.242862.970029
    FORCE    2600    280060  0      13225.5 -.008097-.310206.950635
    FORCE    2600    280070  0      13225.4 -.008332-.376028.926571
    FORCE    2600    280080  0      13224.6 -.008619-.43998 .897966
    FORCE    2600    280090  0      13224.  -.008947-.501783.864947
    FORCE    2600    280100  0      13581.7 -.009323-.562686.826618
    FORCE    2600    280110  0      13572.5 -.009735-.61915 .785213
    FORCE    2600    280120  0      13205.3 -.010175-.673957.738701
    en bash
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/bin/bash
     
    mapfile -t B < <(sort -u fichierB)
     
    while read un deux trois reste
    do
       for i in "${B[@]}"
       do  
          test -n "$trois" && test $i -eq $trois && {
             ((n++))
             break
          }   
       done
       test -n "$n" && {
          echo "\$$un $deux $trois $reste"
          n=""
       } || echo "$un $deux $trois $reste"
    done < fichierA
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  16. #16
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 347
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 347
    Par défaut
    Juste pour le fun (fonctionne pour des petits fichierB)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed -e `xargs -I strreplace -a fichierB echo -n 's/\(.*strreplace.*\)/$\1/g'\;` fichierA
    Cordialement.

  17. #17
    Membre expérimenté Avatar de fransoo
    Inscrit en
    Novembre 2009
    Messages
    209
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 209
    Par défaut
    Citation Envoyé par N_BaH Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $ awk '{if(NR==FNR){a[$1]++}else{print $3 in a ? "$"$0 : $0}}' fichierB fichierA
    Je le savais qu'il y avait des pointures en awk sur ce forum

Discussions similaires

  1. Petit problème sur petit programme
    Par tamerla dans le forum Débuter avec Java
    Réponses: 7
    Dernier message: 19/01/2014, 11h32
  2. Problème sur un programme
    Par Hunshy dans le forum Débuter
    Réponses: 5
    Dernier message: 19/09/2012, 21h33
  3. Débutant en Ruby - Problème sur un programme
    Par Mo0oN dans le forum Ruby
    Réponses: 17
    Dernier message: 16/12/2008, 12h33
  4. Réponses: 3
    Dernier message: 26/11/2008, 15h52
  5. redirigé la sortie d'un script bash sur un programme C
    Par onaipadesmickey dans le forum Shell et commandes GNU
    Réponses: 6
    Dernier message: 07/08/2007, 18h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo