Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
4875 connectés 

  FORUM HardWare.fr
  Linux et OS Alternatifs
  Codes et scripts

  [RESOLU] extraction ligne avec valeur la plus proche d'un critère

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[RESOLU] extraction ligne avec valeur la plus proche d'un critère

n°1499089
HokutoNoFr​ed
Posté le 18-04-2025 à 16:19:16  profilanswer
 

Salut,
 
j'ai un fichier TXT qui contient des colonnes de données de ce type :
 

Citation :

   94.9     51.2     39.3     61.1     39.3     55.7     37.6  
    97.4     51.9     39.6     62.2     39.6     56.4     37.8    
    99.8     52.6     39.9     63.4     39.9     57.1     38.0    
   102.3     53.4     40.1     64.5     40.1     57.8     38.3  
   104.8     54.1     40.4     65.7     40.4     58.5     38.5  
   107.2     54.9     40.7     66.8     40.7     59.2     38.7  
   109.7     55.6     41.0     67.9     41.0     59.9     39.0  


 
Dans un script en BASH je cherche à extraire la ligne dont la valeur en première colonne est la plus proche, par exemple, du nombre 100.
 
Donc obtenir la ligne :

Citation :

   99.8     52.6     39.9     63.4     39.9     57.1     38.0  


 
Comment puis-je procéder ?
J'imagine qu'il faudrait passer par awk, ou une combinaison de commandes, mais je n'arrive pas à trouver de méthode d'analyse des lignes.


Message édité par HokutoNoFred le 02-06-2025 à 14:44:40

---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
mood
Publicité
Posté le 18-04-2025 à 16:19:16  profilanswer
 

n°1499091
rat de com​bat
attention rongeur méchant!
Posté le 18-04-2025 à 17:14:15  profilanswer
 

Perl5 possible? :o


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499092
HokutoNoFr​ed
Posté le 18-04-2025 à 17:20:10  profilanswer
 

Euh... kesskessé Perl ?  [:bighead]  
 
Ca s'intègre dans un script BASH ?
 
Je n'ai rien contre une solution "laborieuse" qui nécessiterai plusieurs étapes avec plusieurs commandes.
C'est ce que je fais d'habitude, étant un noob des scripts.
Mais là je sèche...


Message édité par HokutoNoFred le 18-04-2025 à 17:23:33

---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499093
rat de com​bat
attention rongeur méchant!
Posté le 18-04-2025 à 17:27:14  profilanswer
 

Perl c'est un language de prog (mourant, hélas). C'est généralement préinstallé sous Linux et tu peux très bien appeller ça depuis un bash. Attend, je vais te pondre un truc vite fait. :o


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499094
HokutoNoFr​ed
Posté le 18-04-2025 à 17:31:51  profilanswer
 

C'est toujours bon à prendre.  :)
 
Je pourrai pas tester immédiatement, parce que je pars en congés, mais je l'essayerai sans faute !


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499095
rat de com​bat
attention rongeur méchant!
Posté le 18-04-2025 à 17:39:00  profilanswer
 

Tiens, à adapter à tes besoins:

#! /usr/bin/env perl
 
#PUBLIC DOMAIN, NO WARRANTY
 
use strict;
use warnings FATAL=>'all';
use autodie;
 
die "usage: get_line.pl data.txt <number>\n" if(scalar(@ARGV)!=2);
 
my $file=shift(@ARGV);
my $target_val=shift(@ARGV);
my $delta_abs_min=99999999;
my $line_delta_min='';
 
print "target value is $target_val\n";
 
open my $fh, '<', $file;
 
my ($l, $d, $v);
while(($l=<$fh> ))
{
    chomp($l);
    $l=~s/^\s+//;
    $v=(split(/\s+/, $l))[0];
    my $d=abs($v-$target_val);
    if($d<$delta_abs_min)
    {
        $delta_abs_min=$d;
        $line_delta_min=$l;
    }
}
 
printf("nearest: %s (diff %02.1f%%)\n", $line_delta_min, 100*$delta_abs_min/$target_val);


 
Utilisation:

./get_line.pl data.txt 100
target value is 100
nearest: 99.8     52.6     39.9     63.4     39.9     57.1     38.0     (diff 0.2%)


 
Après si y'a un(e) geek barbu(e) qui passe je veux bien voir une solution en bash. :o


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499100
HokutoNoFr​ed
Posté le 18-04-2025 à 18:15:57  profilanswer
 

Merci.
Je testerai dans 10 jours.
 
Et reste effectivement intéressé par une version BASH.


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499517
StephAscoe​t
Posté le 26-04-2025 à 21:36:39  profilanswer
 

rat de combat a écrit :

Perl c'est un language de prog (mourant, hélas). C'est généralement préinstallé sous Linux et tu peux très bien appeller ça depuis un bash. Attend, je vais te pondre un truc vite fait. :o


 
Ah bon? Les mongueurs sont une espèce en voie de disparition ?
En Bash, il me vient l'idée de charger le tout dans un tableau, auquel on rajoute une colonne qui contient l'écart avec 100 et ensuite on prend la plus faible (restera aussi à gérer les cas marginaux d'égalité...)

n°1499520
rat de com​bat
attention rongeur méchant!
Posté le 26-04-2025 à 22:23:10  profilanswer
 

J'ai pas de chiffres, mais Python semble avoir pris la place de Perl dans beaucoup de domaines. :( Après y'a du "legacy" où Perl tourne toujours bien sûr.


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499521
HokutoNoFr​ed
Posté le 26-04-2025 à 22:29:41  profilanswer
 

StephAscoet a écrit :

 

Ah bon? Les mongueurs sont une espèce en voie de disparition ?
En Bash, il me vient l'idée de charger le tout dans un tableau, auquel on rajoute une colonne qui contient l'écart avec 100 et ensuite on prend la plus faible (restera aussi à gérer les cas marginaux d'égalité...)


Intéressé par cette proposition, que je ne sais pas modéliser...

 

Un exemple serait-il possible ?


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
mood
Publicité
Posté le 26-04-2025 à 22:29:41  profilanswer
 

n°1499727
HokutoNoFr​ed
Posté le 02-05-2025 à 08:07:08  profilanswer
 

rat de combat a écrit :

Tiens, à adapter à tes besoins:

#! /usr/bin/env perl
 
#PUBLIC DOMAIN, NO WARRANTY
 
use strict;
use warnings FATAL=>'all';
use autodie;
 
die "usage: get_line.pl data.txt <number>\n" if(scalar(@ARGV)!=2);
 
my $file=shift(@ARGV);
my $target_val=shift(@ARGV);
my $delta_abs_min=99999999;
my $line_delta_min='';
 
print "target value is $target_val\n";
 
open my $fh, '<', $file;
 
my ($l, $d, $v);
while(($l=<$fh> ))
{
    chomp($l);
    $l=~s/^\s+//;
    $v=(split(/\s+/, $l))[0];
    my $d=abs($v-$target_val);
    if($d<$delta_abs_min)
    {
        $delta_abs_min=$d;
        $line_delta_min=$l;
    }
}
 
printf("nearest: %s (diff %02.1f%%)\n", $line_delta_min, 100*$delta_abs_min/$target_val);


 
Utilisation:

./get_line.pl data.txt 100
target value is 100
nearest: 99.8     52.6     39.9     63.4     39.9     57.1     38.0     (diff 0.2%)


 
Après si y'a un(e) geek barbu(e) qui passe je veux bien voir une solution en bash. :o


 
Salut,
 
j'essaye cette proposition, mais lors de l'exécution il y a un message d'erreur relatif à l'initialisation :

Citation :

target value is 100
Use of uninitialized value $v in subtraction (-) at ./get_line.pl line 26, <$_[...]> line 1.


 
J'ai essayé d'ajouter une initialisation (en gras dans cet extrait du code) :

Citation :

open my $fh, '<', $file;
 
$v = 50;
 
my ($l, $d, $v);
while(($l=<$fh> ))
{
    chomp($l);
    $l=~s/^\s+//;
    $v=(split(/\s+/, $l))[0];
    my $d=abs($v-$target_val);
    if($d<$delta_abs_min)
    {
        $delta_abs_min=$d;
        $line_delta_min=$l;
    }
}


 
mais c'est encore pire  :D  :

Citation :

Global symbol "$v" requires explicit package name (did you forget to declare "my $v"?) at ./get_line.pl line 20.
Execution of ./get_line.pl aborted due to compilation errors.


 
 
 


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499733
rat de com​bat
attention rongeur méchant!
Posté le 02-05-2025 à 11:57:28  profilanswer
 

Tu dois avoir des lignes vides ou différentes de ce que tu as montré dans ton fichier texte? Attend...


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499734
rat de com​bat
attention rongeur méchant!
Posté le 02-05-2025 à 12:01:21  profilanswer
 

Un peu plus "solide". Si tu as toujours des soucis montre un fichier texte exemple RÉEL.
 

#! /usr/bin/env perl
 
#PUBLIC DOMAIN, NO WARRANTY
 
use strict;
use warnings FATAL=>'all';
use autodie;
 
die "usage: get_line.pl data.txt <number>\n" if(scalar(@ARGV)!=2);
 
my $file=shift(@ARGV);
my $target_val=shift(@ARGV);
my $delta_abs_min=99999999;
my $line_delta_min='';
 
print "target value is $target_val\n";
 
open my $fh, '<', $file;
 
my ($l, $d, $v);
my @a;
while(($l=<$fh> ))
{
    chomp($l);
    next if($l=~/^\s+$/);
    $l=~s/^\s+//;
    my @a=split(/\s+/, $l);
    next if(!scalar(@a));
    $v=$a[0];
    my $d=abs($v-$target_val);
    if($d<$delta_abs_min)
    {
        $delta_abs_min=$d;
        $line_delta_min=$l;
    }
}
 
printf("nearest: %s (diff %02.1f%%)\n", $line_delta_min, 100*$delta_abs_min/$target_val);


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499735
HokutoNoFr​ed
Posté le 02-05-2025 à 12:05:51  profilanswer
 

Je venais corriger mon message de ce matin, après diverses investigations, mais tu as déjà deviné l'erreur.  ;)  
 
Effectivement il y avait des lignes de textes (ou vides, oui) avant les colonnes de nombres.
 
En supprimant ces lignes le script a fonctionné.
Je vais testé la version "robuste".  :wahoo:


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499744
rat de com​bat
attention rongeur méchant!
Posté le 02-05-2025 à 16:10:04  profilanswer
 

Attention hein, c'est plus "robuste" que la première version mais ça peut toujours planter si y'a du non-numérique à la place des nombres etc. Je peux mettre plus de "protections" mais il me faudrait les specs EXACTES des fichiers en entrée...


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1499748
HokutoNoFr​ed
Posté le 02-05-2025 à 18:37:25  profilanswer
 

Je n'ai pas les fichiers sous la main, mais de mémoire les premières lignes du fichier sont du style :

 
Citation :

(ligne vide)

 

(ligne vide)

 

TITRE DU FICHIER [ INFO SUPPLÉMENTAIRE ]

 

------------------------------------------------------

 

(ligne vide)

 

ENCORE UN PEU DE TEXTE

 

Ensuite y'a les colonnes de valeurs.

 

Mais déjà ta 1ère version m'a bien aidé.
Dans mon script j'ai utilisé une commande sed pour supprimer les lignes indésirables des fichiers.

 

Merci encore pour ton aide. :jap:


Message édité par HokutoNoFred le 02-05-2025 à 18:38:05

---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1499749
rat de com​bat
attention rongeur méchant!
Posté le 02-05-2025 à 18:47:43  profilanswer
 

Vu que tu dis "de mémoire" je vais pas adapter mon script pour l'instant. Si besoin je peux le faire quand tu auras le format exact de tes fichiers sous la main.


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1500076
HokutoNoFr​ed
Posté le 12-05-2025 à 13:13:55  profilanswer
 

Salut,
 
voici comment est composé l'en-tête des fichiers TXT :
 

Citation :

 
 
          TITRE [UNITE]        
-------------------------------------------  
 
    T-col1   T-col2   T-col3   T-col4   T-col5   T-col6   T-col7   T-col8


 
Ensuite ce sont les colonnes de données.


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1500077
rat de com​bat
attention rongeur méchant!
Posté le 12-05-2025 à 13:34:45  profilanswer
 

Proposition à tester:

#! /usr/bin/env perl
 
#PUBLIC DOMAIN, NO WARRANTY
 
use strict;
use warnings FATAL=>'all';
use autodie;
 
die "usage: get_line.pl data.txt <number>\n" if(scalar(@ARGV)!=2);
 
my $file=shift(@ARGV);
my $target_val=shift(@ARGV);
my $delta_abs_min=99999999;
my $line_delta_min='';
my ($nb_lines, $nb_skipped)=(0, 0);
 
print "target value is $target_val\n";
 
open my $fh, '<', $file;
 
my ($l, $d, $v);
my @a;
while(($l=<$fh> ))
{
    chomp($l); #remove newline at the end
    if($l=~/^\s+$/) #skip empty
    {
        $nb_skipped++;
        next;
    }
    if($l=~/[^0-9,. ]/) #skip everything that contains other characters than '0'-'9', comma, dot, space
    {
        $nb_skipped++;
        next;
    }
    $l=~s/^\s+//; #remove spaces/tabs at beginning of line
    my @a=split(/\s+/, $l);
    if(!scalar(@a)) #skip if empty line (should not happen as this point, extra security)
    {
        $nb_skipped++;
        next;
    }
    $v=$a[0];
    my $d=abs($v-$target_val);
    if($d<$delta_abs_min)
    {
        $delta_abs_min=$d;
        $line_delta_min=$l;
    }
    $nb_lines++;
}
 
printf("nearest: %s (diff %02.1f%%) (%d lines checked, %d lines skipped)\n", $line_delta_min, 100*$delta_abs_min/$target_val, $nb_lines, $nb_skipped);


 
Attention, le code est "radical" au sens il suffit d'un seul charactère non-numérique pour ignorer la ligne. J'ai rajouté des compteurs pour pouvoir vérifier que "ça colle" avec les données brutes, libre à toi d'adapter le printf() final.
 
Données utilisés pour test:


 
 
     
        TITRE [UNITE]        
-------------------------------------------  
 
    T-col1   T-col2   T-col3   T-col4   T-col5   T-col6   T-col7   T-col8
   94.9     51.2     39.3     61.1     39.3     55.7     37.6  
    97.4     51.9     39.6     62.2     39.6     56.4     37.8    
    99.8     52.6     39.9     63.4     39.9     57.1     38.0    
   102.3     53.4     40.1     64.5     40.1     57.8     38.3  
   104.8     54.1     40.4     65.7     40.4     58.5     38.5  
   107.2     54.9     40.7     66.8     40.7     59.2     38.7  
   109.7     55.6     41.0     67.9     41.0     59.9     39.0  


 
Sortie du script:

target value is 100
nearest: 99.8     52.6     39.9     63.4     39.9     57.1     38.0     (diff 0.2%) (7 lines checked, 8 lines skipped)


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1500409
rat de com​bat
attention rongeur méchant!
Posté le 24-05-2025 à 00:19:17  profilanswer
 

Tu as testé le dernier code?


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1500412
HokutoNoFr​ed
Posté le 24-05-2025 à 09:55:39  profilanswer
 

Salut

 

J'ai été pas mal pris par ailleurs ces 2 dernières semaines.
Et la semaine prochaine je serai en congé.

 

Je me note de tester ça début juin.  ;)


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1500572
HokutoNoFr​ed
Posté le 02-06-2025 à 08:09:45  profilanswer
 

Je viens de tester la dernière version proposée, et elle fonctionne bien.
 
En faisant un tkdiff je comprends que tu as ajouté un contrôle de données alpha-numériques et d'espaces/tabs en début de ligne.
 
Merci à toi, je garde ça sous le coude pour une prochaine utilisation.


---------------
Quand Chuck Norris te répond "Dans ton cul", vérifie...
n°1500577
rat de com​bat
attention rongeur méchant!
Posté le 02-06-2025 à 13:38:11  profilanswer
 

:jap:  
 
Tu peux marquer le sujet comme résolu (éditer titre du premier message).


---------------
Ne laissez pas mourir vos sujets en cours de route!
n°1503347
itmicp
Posté le 04-09-2025 à 08:07:22  profilanswer
 

Bonjour
 
 

Citation :

… En Bash, il me vient l'idée de charger le tout dans un tableau, …


 
Bash ne peut pas faire d'opérations arithmétiques sur des nombres décimaux, il ne sait faire des opérations arithmétiques que sur des nombres entiers.
 
Mais comme je vois qu'il y a toujours un seul chiffre après le point decimal, alors on pourrait comparer à 1000 (au lieu de 100) chacune des valeurs dont on aura supprimé le point decimal.


Message édité par itmicp le 09-09-2025 à 04:24:20
n°1503412
itmicp
Posté le 05-09-2025 à 23:46:00  profilanswer
 

Bonjour
 

Citation :

… Dans un script en BASH je cherche à extraire la ligne dont la valeur en première colonne est la plus proche, par exemple, du nombre 100. …


Puisqu'il était demandé un script en bash,
en voilà deux qui ne font appel à aucune commande externe :
 

#!/bin/bash
 
valRef=100
numCol=1
fichSrc="$HOME/fichier.txt"
 
maReg='^[[:digit:]]+[.][[:digit:]]$'
 
valRef+="0"
 
while IFS= read -r ligneLue || [ -n "$ligneLue" ]; do
 
    declare -a tablDonnees=( $ligneLue )
    [[ ${#tablDonnees[@]} -lt numCol ]] && continue
 
    for champ in ${tablDonnees[@]}; do
        [[ $champ =~ $maReg ]] || continue 2
    done
 
    (( diff = ${tablDonnees[$numCol-1]/./} - valRef ))
    diff=${diff/-/}
 
    if [[ -z $min ]]; then {
        min=$diff
        ligneMin="$ligneLue"
    }
    elif [[ diff -lt min ]]; then {
        min=$diff
        ligneMin="$ligneLue"
    }
    fi
 
done < "$fichSrc"
 
printf '%s\n' "$ligneMin"


 
=======================================================

#!/bin/bash
 
# Dans ce script, le format de valRef va déterminer le format des données du fichSrc qui seront prises en compte.
 
# Si les données contenues dans fichSrc avaient trois chiffres après la virgule :
# valRef="100,000"
 
# Si les données contenues dans fichSrc ont un seul chiffre après le point décimal :
valRef="100.0"
 
numCol=1  # Numéro 1 <=> première colonne
fichSrc="$HOME/fichier.txt"
 
declare -a tablDonnees tablLignes
numLi=0 nbDec=0
 
if [[ $valRef =~ [,.] ]]; then
    sepDec=${BASH_REMATCH[0]}
    decim=${valRef#*$sepDec}
    nbDec=${#decim}
    valRef=${valRef/$sepDec/}
fi
 
maReg='^[[:digit:]]+$'
if [[ $nbDec -eq 1 ]]; then
    maReg='^[[:digit:]]+['$sepDec'][[:digit:]]$'
 elif [[ $nbDec -gt 1 ]]; then
    maReg='^[[:digit:]]+['$sepDec'][[:digit:]]{'$nbDec'}$'
fi
 
while IFS= read -r ligneLue || [ -n "$ligneLue" ]; do
    (( numLi++ ))
 
    tablDonnees=( $ligneLue )
    [[ ${#tablDonnees[@]} -lt numCol ]] && continue
 
    for champ in ${tablDonnees[@]}; do
        [[ $champ =~ $maReg ]] || continue 2
    done
 
    (( diff = ${tablDonnees[$numCol-1]/$sepDec/} - valRef ))
    diff=${diff/-/}
 
    if [[ -z $min ]]; then {
        min=$diff
        tablLignes=( "$numLi \"$ligneLue\"" )
    }
    elif [[ diff -lt min ]]; then {
        min=$diff
        tablLignes=( "$numLi \"$ligneLue\"" )
    }
    elif [[ diff -eq min ]]; then {
        tablLignes+=( "$numLi \"$ligneLue\"" )
    }
    fi
 
done < "$fichSrc"
 
for li in "${tablLignes[@]}"; do
    li="${li#*\"}"; printf '%s\n' "${li//\"/}"    # Sans le numéro de chaque ligne et sans les guillemets (comme demandé)
    # printf 'N° %s\n' "$li"    # Avec le numéro de chaque ligne et la ligne correspondante entre guillemets
done


Message édité par itmicp le 25-09-2025 à 17:03:58
mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Linux et OS Alternatifs
  Codes et scripts

  [RESOLU] extraction ligne avec valeur la plus proche d'un critère

 

Sujets relatifs
bash , probleme avec plusieur commande en une ligneScript bash. Lancement d'un sous-script?
Bash incrementation d'une variable avec un autre[BASH] remplacer des adresses par le contenu des fichiers
Installation, mis à jour et maintenance hors ligneRécupérer les adresse IP avec un script Bash
[Bash] Passer un argument dans l'execution d'une commande à distanceAide Script d'installation de service (bash)
Une alternative à JIRA , GIT + Bash ? 
Plus de sujets relatifs à : [RESOLU] extraction ligne avec valeur la plus proche d'un critère


Copyright © 1997-2025 Groupe LDLC (Signaler un contenu illicite / Données personnelles)