#1 Le 29/10/2018, à 07:35
- csc
[Résolu] Script découpage fichier texte formaté
Bonjour,
voila notre contexte et tout à la fin le problème à résoudre.
On a une base de données (SQLite) sécurisée qui contient des informations sur des comptes Web externes, des fichiers cryptés Gpg, etc.
On a un fichier texte (export de table de cette base) qui a le format suivant:
Chaque enregistrement contient plusieurs lignes (séparées par \n) avec le nom du champ en début de première ligne,
en majuscules, suivi de ': ' (caractère 2 points avec 1 espace ensuite) puis le contenu du champ avec un \n à la fin de
chaque champ. Les champs peuvent être vides ou contenir une ou plusieurs lignes (séparées par '\n').
A la fin de l'enregistrement on a une ligne qui contient le séparateur '===\n' (3 signes égal suivi de \n).
Pour éviter les erreurs, la saisie (faite dans le Sgbd) interdit de saisir des données identiques aux noms de champs
utilisés lors de l'export; cette vérification est aussi faite lors de l'export.
On a les éléments suivants pour chaque champ exporté suivi du contenu du champ:
'ID: '
'ADRESSE DE: '
'SITE: '
'USER: '
'PASSWORD: '
'REMARQUES: '
'==='
Exemple (espaces et texte entre parenthèses sont des explications):
ID: 54 (numérique)
ADRESSE DE: Compte Gmail du secrétariat (du texte qui permet d'identifier ou de rechercher cet enregistrement)
SITE: https://accounts.google.com/ (texte qui peut contenir ou pas une URI)
USER: unnomdutilisateur (texte qui peut contenir ou pas un nom ou une adresse email)
PASSWORD: motdepasseintrouvable (texte qui peut contenir ou pas un mot de passe)
REMARQUES: changer le mot de passe tous les 3 mois, svp (texte contenant ou pas des remarques pour cet enregistrement)
=== (le séparateur d'enregistrements)
Puis viennent les autres enregistrements avec la même syntaxe que précédemment.
Tous les champs (sauf ID: ) peuvent ëtre vides, ou contenir une ou plusieurs lignes mais pas tous vides en même temps !
Le problème (enfin !):
Ce fichier texte fait actuellement environ 50 Ko.
Il est utilisé (crypté) sur les ordinateurs n'ayant pas de Viewer SQL.
Par contre certaines machines (systèmes embarqués) ont une limite de taille pour les fichiers texte de 5 Ko.
On voudrait pouvoir découper ce fichier texte avec les contraintes:
- 4,9 Ko taille maxi,
- coupure en fin de chaque fichier sur le séparateur et pas en plein milieu d'un enregistrement comme c'est le cas parfois
actuellement en faisant un simple découpage avec la commande linux 'split'.
On sait évidemment le faire en programme langage 'C' ou 'Python', etc.
Mais on veut utiliser un shell script (bash) avec les seules commandes du shell ou du système (ni perl, ni php, etc.).
Seulement split, sed, awk, cut, découpage de chaine, etc.
Avez-vous une solution à proposer ?
Bonne journée,
Csc
Dernière modification par csc (Le 05/11/2018, à 08:26)
Hors ligne
#2 Le 29/10/2018, à 13:03
- Hizoka
Re : [Résolu] Script découpage fichier texte formaté
Salut, finalement le problème n'est que celui de la découpe du fichier ?
Si c'est ça, perso je ferais ça :
x=1
while read Ligne
do
# Envoi de la ligne dans le nouveau fichier txt
echo "${Ligne}" >> "fichier_export_${x}.txt"
# Si on traite une ligne de fin
if [[ ${Ligne} == "===" ]]
then
# SI le fichier atteint un certain poids, on incrémente x pour changer de fichier
[[ $(wc -c < "fichier_export_${x}.txt") -gt 4500 ]] && ((x++))
fi
done < fichier_export
Que du bash sauf pour la taille du fichier ou j'utilise du.
EDIT : Simplifié après conseille de pingouinux
Dernière modification par Hizoka (Le 29/10/2018, à 14:16)
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#3 Le 29/10/2018, à 13:15
- pingouinux
Re : [Résolu] Script découpage fichier texte formaté
Bonjour,
@Hizoka :
Pour obtenir directement la taille d'un fichier, tu as aussi cette méthode
Taille=$(wc -c fichier|cut -d\ -f1)
Édité :
Ou mieux
Taille=$(wc -c <fichier)
Dernière modification par pingouinux (Le 29/10/2018, à 13:34)
Hors ligne
#4 Le 29/10/2018, à 14:14
- Hizoka
Re : [Résolu] Script découpage fichier texte formaté
Taille=$(wc -c fichier|cut -d\ -f1)
Oui je sais mais j'essaie de limiter les pipe et les commandes externes
Taille=$(wc -c <fichier)
Par contre, je ne savais pas que wc permettait ça et le coup du < est pas mal
Merci
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#5 Le 30/10/2018, à 15:51
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
Salut,
En version full gawk :
LC_ALL=C gawk '
BEGIN {
RS="\n===\n"
taille_RS=length(RS)
}
{
taille+=length + taille_RS
if(taille > 4900) {
close("fic_sortie_" i ".txt")
i++
taille=length + taille_RS
}
print $0 RT >"fic_sortie_" i ".txt"
}
' 'i=1' 'ORS=' fic_entree.txt
Edit :
Remplacement de print $0 (RT?RT:"") par print $0 RT (c'était n'importe quoi !)
Re-édit : petite modif suite à la remarque Watael
Dernière modification par Postmortem (Le 31/10/2018, à 01:22)
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#6 Le 30/10/2018, à 17:37
- Watael
Re : [Résolu] Script découpage fichier texte formaté
pourquoi length +5 ?
Dernière modification par Watael (Le 30/10/2018, à 17:39)
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#7 Le 30/10/2018, à 17:40
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
pourquoi length +5 ?
length, c'est length($0) ; et dans $0, il n'y a pas le séparateur d'enregistrement (qui a une longueur de 5)
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#8 Le 30/10/2018, à 23:42
- Watael
Re : [Résolu] Script découpage fichier texte formaté
ma question portait sur le +5.
par souci de clarté et de maintenabilité, sans me soucier du coût, je préférerais lire length(RS), plutôt que de me poser la même question dans quelques mois.
Connected \o/
Welcome to sHell. · eval is evil.
Hors ligne
#9 Le 31/10/2018, à 01:23
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
ma question portait sur le +5.
par souci de clarté et de maintenabilité, sans me soucier du coût, je préférerais lire length(RS), plutôt que de me poser la même question dans quelques mois.
T'as raison, j'ai modifié.
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#10 Le 31/10/2018, à 07:14
- csc
Re : [Résolu] Script découpage fichier texte formaté
Bonjour Isoka (et Postmortem),
merci d'avoir pris le temps de me répondre,
malheureusement j'ai identifié 2 bugs dans ta solution:
Premier bug:
Le fichier d'entrée est lu ligne par ligne et écrit ligne par ligne AVANT de tester si la taille limite est atteinte.
Le test de dépassement devrait se faire sur les enregistrements complet avant d'écrire.
Exemple:
imagine une taille limite de 4500 octets et un fichier d'entrée de 4800 octets et n enregistrements (séparateur "===").
Si les n-2 premiers enregistrements font 4400 octets ton script continue d'écrire dans le même fichier (4400<4500).
Imagine que l'enregistrement suivant fasse 600 octets, ton script écrit cet enregistrement puis teste si maintenant les n-1 enregistrements font plus de 4500 octets.
Le test est "OUI", ton script passe à l'écriture dans le fichier suivant pour les enregistrements n et suivants.
MAIS ton premier fichier fera 4400+600=5000 octets ce qui est plus que le max de 4500 !
Deuxième bug:
quand le contenu d'un champs est vide, on a donc par exemple
"USER: "
l'espace à la fin fait partie, pour nous, de l'identifiant d'un nom de champ (tests dans d'autres programmes).
Ton script enlève l'espace en fin de ligne.
Je suppose que c'est le 'read' du fichier d'entrée ou le "write" dans le fichier de sortie mais le contenu est donc modifié ce qui n'est pas possible.
Désolé,
Bonne journée
Hors ligne
#11 Le 31/10/2018, à 08:45
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
Pour pas perdre l'espace, remplacer :
while read Ligne
Par :
while IFS= read Ligne
Pour le contrôle, peut-être qu'en cherchant un peu, tu devrais t'en sortir, non ?
Dernière modification par Postmortem (Le 31/10/2018, à 08:46)
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#12 Le 31/10/2018, à 09:51
- Hizoka
Re : [Résolu] Script découpage fichier texte formaté
Premier bug:
Le fichier d'entrée est lu ligne par ligne et écrit ligne par ligne AVANT de tester si la taille limite est atteinte.
Le test de dépassement devrait se faire sur les enregistrements complet avant d'écrire.
En effet, pour ça que j'ai mis 4500 pour laisser de la marge.
Mais il est possible de passer par une variable temporaire :
x=1
texte=()
while IFS= read Ligne
do
# Envoi de la ligne dans le nouveau fichier txt
texte+=("${Ligne}")
# Si on traite une ligne de fin
if [[ ${Ligne} == "===" ]]
then
# Calcul la taille que représente la liste, le fichier puis le total
taille1=$(wc -c <<< "${texte[@]}")
taille2=$(wc -c 2>/dev/null < "fichier_export_${x}.txt")
tailletotale=$((${taille1} + ${taille2:-0}))
# Si la taille totale dépasse, on incrémente x et donc le nom du fichier
[[ ${tailletotale} -gt 4900 ]] && ((x++))
# Envoi des lignes dans le fichier
for SubLigne in "${texte[@]}"
do
echo "${SubLigne}" >> "fichier_export_${x}.txt"
done
# Réinitialisation de la liste
texte=()
fi
done < fichier_export
Ça te va ?
Deuxième bug:
quand le contenu d'un champs est vide, on a donc par exemple
"USER: "
l'espace à la fin fait partie, pour nous, de l'identifiant d'un nom de champ (tests dans d'autres programmes)
utilisation de IFS= comme proposé par Postmortem.
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#13 Le 31/10/2018, à 11:34
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
En version "awk classique" (mais c'est moins classe !) :
LC_ALL=C awk '
BEGIN { i=1 }
{
enreg=enreg sep $0
sep="\n"
}
/^===$/ {
taille_enreg=length(enreg) + 1 # + 1 pour le \n ajouté par print
taille_fic=taille_fic + taille_enreg
if (taille_fic > 4900) {
close("fic_sortie_" i ".txt")
i++
taille_fic=taille_enreg
}
print enreg >"fic_sortie_" i ".txt"
enreg=""
sep=""
}
' fic_entree.txt
Mais niveau temps d'exécution, comme gawk, ça explose le bash !
Dernière modification par Postmortem (Le 31/10/2018, à 11:35)
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#14 Le 05/11/2018, à 08:24
- csc
Re : [Résolu] Script découpage fichier texte formaté
Bonjour et merci Hizoka et Postmortem,
vos solutions semblent Ok.
Après la recommandation de Postmortem j'ai essayé d'écrire à nouveau mon propre script en partant de zéro.
Pourquoi j'avais fait cette demande d'aide ?
Le dernier script Unix (traduire de nos jours par Linux si vous voulez) que j'ai écrit c'était en 1983 sur Unix Berkley et Bourne shell. Quand j'ai vu à quel point la syntaxe avait changé, ça m'a un peu g...é
Accolades ou pas crochets simples ou double ou pas, espace avant, après ou pas, # ou pas, double quote ou pas, $ ou pas, etc.
Cela ressemble diablement à de la sédimentation ?
Ce script que j'ai écrit marche mais est malpropre. Chaque fois que j'ai eu un problème (comparaisons, affectation, etc.) je suis allé sur internet chercher une solution. Il est donc parfaitement possible
que pour faire 2 fois la même chose, j'ai utilisé 2 syntaxes différentes, Horreur !
Je vous le livre quand même mais pitié, lisez le en fermant les yeux.
#!/bin/bash
x=1
Taillemax=4900
Enreg=""
Tailleenreg=0
Enregtmp=""
Tailleenregtmp=0
Taillepossible=0
Separateur="==="$'\n'
while IFS= read Ligne
do
Ligne="$Ligne"$'\n'
Enregtmp="${Enregtmp}${Ligne}"
let Tailleenregtmp=${#Enregtmp}
let Tailleenreg=${#Enreg}
let Taillepossible=Tailleenreg+Tailleenregtmp # taille des enreg en cours + enreg actuel lu
if [ "${Ligne}" = "$Separateur" ]
then
if [ $Taillepossible -eq $Taillemax ] # si enreg en cours+tmpenreg == taillemax on écrit Enreg+Enregtmp
then
echo "${Enreg}${Enregtmp}" >> "fic_sortie_${x}.txt"
let x++
unset Enreg
unset Enregtmp
fi
if [ $Taillepossible -gt $Taillemax ] # si enregs en cours+tmpenreg>Max alors on écrit Enreg seul
then
echo "${Enreg}" >> "fic_sortie_${x}.txt"
let x++
Enreg="${Enregtmp}"
unset Enregtmp
fi
if [ $Taillepossible -lt $Taillemax ] # si taille max non atteinte on continue de lire
then
Enreg="${Enreg}${Enregtmp}"
unset Enregtmp
fi
fi
done < fic_entree.txt
#
# fin du fichier d'entrée atteinte
if test -n "$Enreg" # mais Enreg était trop petit pour être écrit
then
echo "${Enreg}" >> "fic_sortie_${x}.txt"
fi
# s'il y a des données dans Enregtmp c'est que Enreg était déjà suffisant seul pour être juste sous ou égal à Taillemax
# il faut donc créer un nouveau fichier (d'où le x++) pour ce qui peut traîner dans Enregtmp...
if test -n "$Enregtmp"
then
let x++
echo "${Enregtmp}" >> "fic_sortie_${x}.txt"
fi
J'espère que l'utilisation du Bbcode est correcte.
Avancée dans l'inconnu one more time.
Sujet résolu donc,
Bonne journée
Hors ligne
#15 Le 05/11/2018, à 23:53
- Postmortem
Re : [Résolu] Script découpage fichier texte formaté
Salut,
Petite remarque ; ${#Enreg} ne donne pas la taille d'un enregistrement mais le nombre de caractères d'un enregistrement.
C'est pour cela que j'avais préfixé les commandes (g)awk de « LC_ALL=C ».
$ var=é
$ echo "${#var}"
1
$ LC_ALL=C echo "${#var}"
1
$ awk -v "var=$var" 'BEGIN {print length(var)}'
1
$ LC_ALL=C awk -v "var=$var" 'BEGIN {print length(var)}'
2
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne