Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 22/10/2023, à 18:18

zigroful1

bash : Nb de jours entre 2 dates

Bonjour à tous,
Je travaille avec bash et j'ai essayé de faire un script me permettant de calculer le nombre de jours entre 2 dates entrées comme arguments dans une fonction. Les dates sont sous format dd/mm/aaaa.
J'ai conçu le script suivant:

#!/bin/bash

function NbJEntre2Dates()
{
d1=`echo -e $1|sed -E 's/([0-3][0-9])\/([0-1][0-9])\/([0-2][0-1][0-4][0-9])/\3-\2-\1/g'`
d1=`echo $((d1+0))`
d2=`echo -e $2|sed -E 's/([0-3][0-9])\/([0-1][0-9])\/([0-2][0-1][0-4][0-9])/\3-\2-\1/g'`
d2=`echo $((d2+0))`
d=`expr $((($(date +%s -d d1)-$(date +%s -d d2))/86400))`
d=`echo $((d+0))`
return $d1, $d2 ,$d
}

NbJEntre2Dates 31/08/2023 31/07/2023
echo $d1, $d2,   $d

Les 4 premières lignes du code servent à convertir les dates du format dd/mm/aaaa au format aaaa-mm-dd et à convertir le résultat en numérique pour d1 et d2. Et enfin les 2 dernières servent à calculer le nombre de secondes écoulées depuis le 01/01/1970 jusqu'aujourd'hui et à faire la différence, cette dernière étant divisée par 86400 pour convertir les secondes en jours.
Manque de pot: le script me donne une réponse fausse en ce qui concerne d, comme l'atteste la copie d'écran suivante:

./DifferenceEntre2Dates.sh: ligne 11 : return: 2023-08-31, : argument numérique nécessaire
20230831, 20230731, 0

Je ne comprends pas l'erreur relevée ni pourquoi d n'est pas égal à 31 comme on devrait pouvoir s'y attendre !
Quelqu'un a-t-il une idée ? Où se trouve l'erreur ?

Hors ligne

#2 Le 22/10/2023, à 18:45

Sciensous

Re : bash : Nb de jours entre 2 dates

il manque les $ devant d1 et d2 dans date
cool

et je ne crois pas que return prenne plusieurs arguments
surtout que d1,d2 et d sont des variables globales dans ton script

Dernière modification par Sciensous (Le 22/10/2023, à 18:46)


antiX 19 et 21 et Ubuntu 20.04 et 22.04
( sous LXDE et gnome-shell )

Hors ligne

#3 Le 22/10/2023, à 18:55

pingouinux

Re : bash : Nb de jours entre 2 dates

Bonjour,
Je te suggère ceci (à adapter éventuellement) :

#!/bin/bash

delta_jour ()
{
    dat1=$1;
    dat2=$2;
    dat1_en_secondes=$(date -ud "$dat1" +"%s");
    dat2_en_secondes=$(date -ud "$dat2" +"%s");
    printf "dat1=%s dat2=%s\n" $dat1 $dat2;
    printf "Nb de jours = %s\n" $(( (dat1_en_secondes - dat2_en_secondes) / 86400 ))
}   

delta_jour 2023-08-31 2023-07-31

Hors ligne

#4 Le 22/10/2023, à 19:16

pingouinux

Re : bash : Nb de jours entre 2 dates

Ou en utilisant ton format de dates :

#!/bin/bash

function NbJEntre2Dates()
{
d1=$(echo -e $1|sed -E 's/([0-3][0-9])\/([0-1][0-9])\/([0-2][0-1][0-4][0-9])/\3-\2-\1/g')
d2=$(echo -e $2|sed -E 's/([0-3][0-9])\/([0-1][0-9])\/([0-2][0-1][0-4][0-9])/\3-\2-\1/g')
d=$(( ($(date +%s -d $d1)-$(date +%s -d $d2)) / 86400 ))
}

NbJEntre2Dates 31/08/2023 31/07/2023
echo $d1, $d2,   $d

ou en simplifiant un peu :

#!/bin/bash

function NbJEntre2Dates()
{
d1=$(sed -E 's#([0-3][0-9])/([0-1][0-9])/([0-2][0-1][0-4][0-9])#\3-\2-\1#g' <<<$1)
d2=$(sed -E 's#([0-3][0-9])/([0-1][0-9])/([0-2][0-1][0-4][0-9])#\3-\2-\1#g' <<<$2)
d=$(( ($(date +%s -d $d1)-$(date +%s -d $d2)) / 86400 ))
}

NbJEntre2Dates 31/08/2023 31/07/2023
echo $d1, $d2,   $d

Dernière modification par pingouinux (Le 22/10/2023, à 19:28)

Hors ligne

#5 Le 22/10/2023, à 21:41

Watael

Re : bash : Nb de jours entre 2 dates

je crois que j'ajouterais une fonction pour la conversion du format des dates, afin de m'épargner la réécriture d'un code identique.
et je me passerais du sed, pour traiter une ligne roll

convDate() {
    IFS='/' read -r j m a <<<$1
    date -d $a/$m/$j +%s
}
nbJours() {
    echo $(( ( $(convDate $1) - $(convDate $2) ) / (60**2*24) ))
}
nbJours 31/08/2023 31/07/2023

NB: le mot clé function est superfétatoire avec les parenthèses; c'est l'un ou l'autre.


Connected \o/
Welcome to sHell. · eval is evil.

En ligne

#6 Le 23/10/2023, à 11:41

zigroful1

Re : bash : Nb de jours entre 2 dates

Merci à tous,
J'ai cependant encore une question: J'ai besoin de faire appel à cette fonction (NbJEntre2Dates()) à l'intérieur d'un script gawk (ou awk) pour comparer des dates qui apparaissent en colonne 2 dans un fichier csv.  Je ne vois nulle part dans la documentation de awk, la possibilité d'appeler une fonction bash à l'intérieur d'un script awk. Est-ce possible, et comment s'y prendre ?

Hors ligne

#7 Le 23/10/2023, à 12:26

Watael

Re : bash : Nb de jours entre 2 dates

ah, tu nous l'aurais dit tout de suite, on aurait cherché une solution en gawk !
ça se fait très bien avec gawk, qui possède des fonctions pour gérer les dates; notamment, mktime qui va transformer une date (attention au format que tu lui passes ! ) en secondes depuis Epoch (comme date -d "$uneDate" +%s)

$ gawk '{print mktime($0" 00 00 00")}' <<<'2023 07 31'
1690754400
$

Connected \o/
Welcome to sHell. · eval is evil.

En ligne

#8 Le 23/10/2023, à 17:13

zigroful1

Re : bash : Nb de jours entre 2 dates

Je suis désolé, mais je pensais pouvoir développer tout mon script avec bash et sed. Mais je me suis aperçu, hier soir qu'il était difficile, pour ainsi dire impossible, d'extraire avec ces deux seuls langages, un champs dans un fichier csv et de travailler avec. Notamment de n'extraire que les lignes dont les dates situées dans le deuxième champ, sont relatives à un mois donné.
Exemple:

a400;12/04/22;a401;a402
a500;31/05/22;a501;a502
a600;11/06/22;a601;a602
a70;18/07/22;a71;a72
a300;20/08/22;a301;a302
a7000;21/08/22;a2001;a2002
a900;24/09/22;a901;a902
a4000;30/09/22;a4001;a4002
a5000;01/10/22;a5001;a5002
a1000;17/10/22;a1001;a1002
a200;12/11/22;a201;a202
a80;20/11/22;a81;a82
a700;24/07/23;a701;a702
a800;22/08/23;a801;a802
a7000;22/08/23;a2001;a2002
a3000;31/08/23;a3001;a3002
b00;01/09/23;b01;b02
b10;05/09/23;b11;b12
b30;18/09/23;b21;b22
b40;21/09/23;b41;b42
b50;25/09/23;b51;b52
b60;30/09/23;b61;b62
a7000;19/08/27;a2001;a2002

Et je voudrais n'extraire que les lignes dont les dates sont entre 01/10/22 et 30/09/23, sachant qu'il peut y avoir d'autres dates dans d'autres champs (ce qui signifie qu'il faut restreindre la condition d'extraction au deuxième champ exclusivement !). Ainsi sed et peut-être bash sont exclus car le ciblage d'un champs précis est plus difficile.

Hors ligne

#9 Le 23/10/2023, à 17:31

Watael

Re : bash : Nb de jours entre 2 dates

CSV, c'est un travail pour (g)awk.
mais, là tu entames une nouveau sujet qui mériterait une nouvelle discussion : "comment extraire des lignes dont la valeur d'un champ est comprise entre deux dates".

Dernière modification par Watael (Le 23/10/2023, à 17:32)


Connected \o/
Welcome to sHell. · eval is evil.

En ligne

#10 Le 23/10/2023, à 20:35

pingouinux

Re : bash : Nb de jours entre 2 dates

Essaye ceci :

$ cat iawk

function secondes(dat) {
 datspec=sprintf("20%s %s %s 00 00 00 0",substr(dat,7),substr(dat,4,2),substr(dat,0,2))
 return mktime(datspec)
}

BEGIN {
 t1=secondes("01/10/22")
 t2=secondes("30/09/23")
}
{ t=secondes($2)
  if (t1<=t && t<=t2) print $0
}

À utiliser ainsi :

gawk -F";" -f iawk fichier.csv

Hors ligne

#11 Le 23/10/2023, à 21:06

Watael

Re : bash : Nb de jours entre 2 dates

j'aurais fait un split(dat,ar,"/"), et utilisé ar[1]...
plutôt que de répéter substr().


Connected \o/
Welcome to sHell. · eval is evil.

En ligne

#12 Le 23/10/2023, à 21:27

pingouinux

Re : bash : Nb de jours entre 2 dates

Watael #11 a écrit :

j'aurais fait un split(dat,ar,"/"), et utilisé ar[1]...
plutôt que de répéter substr().

Effectivement, c'est plus simple.
Version corrigée :

function secondes(dat) {
 split(dat,ar,"/")
 datspec=sprintf("20%s %s %s 00 00 00 0",ar[3],ar[2],ar[1])
 return mktime(datspec)
}

BEGIN {
 t1=secondes("01/10/22")
 t2=secondes("30/09/23")
}
{ t=secondes($2)
  if (t1<=t && t<=t2) print 
}

Hors ligne

#13 Le 23/10/2023, à 21:40

Watael

Re : bash : Nb de jours entre 2 dates

et, un awk, ça se scripte, ± comme un shell :

#!/usr/bin/gawk -f

BEGIN {
    FS=";" # voire FS=OFS=";" ça dépend des opérations réalisées, et de la façon dont on affiche les résultats.
    t1=...
...

une solution a été donnée, il y a quelques mois, qui consistait à comparer les dates concaténées : 20230531 < 20230612 < 20230731
ça peut simplifier le traitement, et être utile si gawk (avec ses fonctions de gestion des dates) n'est pas disponible sur un système.

EDIT:

$ awk -F ';' '{split($2,a,"/"); if (20221001 < "20"a[3]a[2]a[1] && "20"a[3]a[2]a[1] < 20230930)print$0}' /tmp/csv.csv
a1000;17/10/22;a1001;a1002
a200;12/11/22;a201;a202
a80;20/11/22;a81;a82
a700;24/07/23;a701;a702
a800;22/08/23;a801;a802
a7000;22/08/23;a2001;a2002
a3000;31/08/23;a3001;a3002
b00;01/09/23;b01;b02
b10;05/09/23;b11;b12
b30;18/09/23;b21;b22
b40;21/09/23;b41;b42
b50;25/09/23;b51;b52
$ 

Dernière modification par Watael (Le 23/10/2023, à 22:34)


Connected \o/
Welcome to sHell. · eval is evil.

En ligne

#14 Le 24/10/2023, à 14:42

zigroful1

Re : bash : Nb de jours entre 2 dates

Excellent ! Tout marche à merveille ! Je vous remercie

Hors ligne