Remplacer un champ d'une positionnée donnée
Résolu
Decon
Messages postés
91
Date d'inscription
Statut
Membre
Dernière intervention
-
Decon Messages postés 91 Date d'inscription Statut Membre Dernière intervention -
Decon Messages postés 91 Date d'inscription Statut Membre Dernière intervention -
Bonjour,
Je souhaite parcourir un fichier en shell sh ou ksh (peu importe) et pour une positionnée donnée, remplacer le champ à cette position par autre chose.
Plus concrètement:
Soit mon fichier $fic_prov avec des lignes du type:
Je souhaite donc que pour une ligne commençant par ABB, il me remplace le deuxième champs 168063110 ( le délimiteur étant | ) par <champ_2>10; où 10 représente les deux derniers caractères du champ 2 (168063110).
Pour finir, je souhaite mettre le résultat final (fichier modifié) dans un autre fichier.
Actuellement j'essaye un truc pas très potable qui me provoque une erreur.
Voici un extrait du code:
Erreur:
Merci d'avance
Je souhaite parcourir un fichier en shell sh ou ksh (peu importe) et pour une positionnée donnée, remplacer le champ à cette position par autre chose.
Plus concrètement:
Soit mon fichier $fic_prov avec des lignes du type:
ACC|168063100|168063100|1|1680631||0|0||19900101000000||7||250 ABB|168063110|168063112|1|1680631001|1680631|1|0||20110118000000|
Je souhaite donc que pour une ligne commençant par ABB, il me remplace le deuxième champs 168063110 ( le délimiteur étant | ) par <champ_2>10; où 10 représente les deux derniers caractères du champ 2 (168063110).
Pour finir, je souhaite mettre le résultat final (fichier modifié) dans un autre fichier.
Actuellement j'essaye un truc pas très potable qui me provoque une erreur.
Voici un extrait du code:
a=1 for fic_prov in $Repertoire/type_fichier* do nom_fic='basename $fic_prov .txt'_"$a"_"txt" awk -F "|" '{ $2 = "<champ_2>substr($2,length("$2")-2,length("$2"))" ; print $0 > $nom_fic}' $fic_prov a=a+1 done
Erreur:
awk: syntax error near line 1 awk: bailing out near line 1
Merci d'avance
A voir également:
- Remplacer un champ d'une positionnée donnée
- Remplacer disque dur par ssd - Guide
- Remplacer word - Guide
- Remplacer coco - Accueil - Réseaux sociaux
- Quel site pour remplacer coco - Accueil - Réseaux sociaux
- Remplacer un visage sur une photo - Guide
18 réponses
Personnellement je travaillerais dans un premier temps avec un fichier toto.awk pour trouver la bonne syntaxe, quitte à la mettre en ligne. Je ne sais pas si c'est exactement ce que tu veux mais :
... j'obtiens :
Bonne chance
BEGIN { FS = "|"; OFS = "|"; } $1 ~ /^ABB$/ { $2 = "<champ2>" length($2) ";"; printed = 1; print $0; } { if(!printed) print $0; printed = 0; }
... j'obtiens :
(mando@aldur) (~) $ cat toto.txt ACC|168063100|168063100|1|1680631||0|0||19900101000000||7||250 ABB|168063110|168063112|1|1680631001|1680631|1|0||20110118000000| (mando@aldur) (~) $ awk -f toto.awk toto ACC|168063100|168063100|1|1680631||0|0||19900101000000||7||250 ABB|<champ2>9;|168063112|1|1680631001|1680631|1|0||20110118000000|
Bonne chance
Bonjour,
Je te propose une autre méthode avec sed.
sed et awk sont LES deux outils à connaitre de préférence.
Je te propose une autre méthode avec sed.
sed et awk sont LES deux outils à connaitre de préférence.
sed '/ABB/ s/[0-9]*\([0-9][0-9]\)/<fichier>\1/1' toto.txt
Merci de vous intéresser à mon problème.
Mamiemando dans l'exemple que j'ai donné les deux derniers chiffres étaient "10" alors qu'après remplacement tu as "9;".
Aussi, ce que je n'ai pas dit c'est que le but est de faire plusieurs traitements de ce type dans et sur plusieurs lignes. Ce qui alourdirait énormément l'idée d'un ficher awk à part.
Synopsis8, je ne pourrai pas tester ton code ce soir; mais à vu d'oeil, j'ai l'impression qu'il fera ce traitement pour tous les champs. Je ne vois pas à quel moment tu lui indique à quel champ s'applique ce traitement.
Mamiemando dans l'exemple que j'ai donné les deux derniers chiffres étaient "10" alors qu'après remplacement tu as "9;".
Aussi, ce que je n'ai pas dit c'est que le but est de faire plusieurs traitements de ce type dans et sur plusieurs lignes. Ce qui alourdirait énormément l'idée d'un ficher awk à part.
Synopsis8, je ne pourrai pas tester ton code ce soir; mais à vu d'oeil, j'ai l'impression qu'il fera ce traitement pour tous les champs. Je ne vois pas à quel moment tu lui indique à quel champ s'applique ce traitement.
Je vais essayer de te décomposer la commande
/ABB/ # Seulement les lignes contenant "ABB"
[0-9] # commençant par un chiffre (que je n'enregistre pas)
* # tout les caractères qui suivent
\([0-9][0-9]\) # finissant par 2 chiffres (que j'enregistre cette fois)
/<fichier> # que je remplace par le mot "fichier" (en fait je me suis trompé, c'était plutôt "champ2" que tu voulais mettre
\1 # aurquel je rajoute le premier tampon de mémoire (celui qui contient les 2 derniers chiffres)
/1 # Uniquement la première occurence de chiffres (après ABB). Si je n'avais pas mis ça, il aurait remplacé la dernière, et si j'avais voulu qu'il remplace tous les champs, j'aurais mis /g
Voila.
/ABB/ # Seulement les lignes contenant "ABB"
[0-9] # commençant par un chiffre (que je n'enregistre pas)
* # tout les caractères qui suivent
\([0-9][0-9]\) # finissant par 2 chiffres (que j'enregistre cette fois)
/<fichier> # que je remplace par le mot "fichier" (en fait je me suis trompé, c'était plutôt "champ2" que tu voulais mettre
\1 # aurquel je rajoute le premier tampon de mémoire (celui qui contient les 2 derniers chiffres)
/1 # Uniquement la première occurence de chiffres (après ABB). Si je n'avais pas mis ça, il aurait remplacé la dernière, et si j'avais voulu qu'il remplace tous les champs, j'aurais mis /g
Voila.
Salut,
[0-9] # commençant par un chiffre (que je n'enregistre pas)
* # tout les caractères qui suivent
En fait l'expression doit se lire avec les deux combinés et de ce fait ne correspond plus trop à ta définition ;-(
[0-9]*
Tous caractères numériques y compris aucun.
/1 # Uniquement la première occurrence de chiffres (après ABB). Si je n'avais pas mis ça, il aurait remplacé la dernière, et si j'avais voulu qu'il remplace tous les champs, j'aurais mis /g
Non plus ;-\
Par défaut sed ne traite que la 1ère occurrence rencontrée, d'où en général l'utilisation du "g" pour qu'il traite toutes les occurrences. Ici le "/1" est inutile. Par contre si on avait voulu traiter la seconde occurrence, là oui il aurait été utile de mettre "/2"
;-))
[0-9] # commençant par un chiffre (que je n'enregistre pas)
* # tout les caractères qui suivent
En fait l'expression doit se lire avec les deux combinés et de ce fait ne correspond plus trop à ta définition ;-(
[0-9]*
Tous caractères numériques y compris aucun.
/1 # Uniquement la première occurrence de chiffres (après ABB). Si je n'avais pas mis ça, il aurait remplacé la dernière, et si j'avais voulu qu'il remplace tous les champs, j'aurais mis /g
Non plus ;-\
Par défaut sed ne traite que la 1ère occurrence rencontrée, d'où en général l'utilisation du "g" pour qu'il traite toutes les occurrences. Ici le "/1" est inutile. Par contre si on avait voulu traiter la seconde occurrence, là oui il aurait été utile de mettre "/2"
;-))
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Si j'ai bien compris cette méthode ne traiterait au final que soit le champ suivant ABB pour(/1), soit tous les champs(/g).
Par contre vu que mes traitements vont différer suivant le champ, c'est la raison pour laquelle la notion de n° de champ est importante pour moi.
Par contre vu que mes traitements vont différer suivant le champ, c'est la raison pour laquelle la notion de n° de champ est importante pour moi.
Et bien tu changes le numéro en fonction de tes nécéssités /1 ou /2, etc
Tu peux cumuler aussi....
Tu peux cumuler aussi....
C'est exact, la méthode n'est pas mieux, c'est un autre outil qu'il faut à mon sens connaître comme awk.
Celà étant, je n'ai jamais eu l'occasion d'essayer des /10 ou même des /* voir /$
Ce serait à tenter, En même temps, on peux enchainer les statements en les séparant par des points virgules.
Pour moi, sed et awk sont des outils confortables pour faire du parsing sans avoir à apprendre python (qui n'est jamais installé en natif sous UNIX par exemple).
J'ai essayé python, c'est bien, mais comme tout, il faut se donner le temps.
Celà étant, je n'ai jamais eu l'occasion d'essayer des /10 ou même des /* voir /$
Ce serait à tenter, En même temps, on peux enchainer les statements en les séparant par des points virgules.
Pour moi, sed et awk sont des outils confortables pour faire du parsing sans avoir à apprendre python (qui n'est jamais installé en natif sous UNIX par exemple).
J'ai essayé python, c'est bien, mais comme tout, il faut se donner le temps.
D'accord,
C'est un petit peu problématique dans ce cas parce que je peut avoir par ligne une cinquantaine de lignes.
C'est un petit peu problématique dans ce cas parce que je peut avoir par ligne une cinquantaine de lignes.
Si je m'en réfère à ton intitulé, c'est le 2 champ qui t'intéresse, mais si tu veux essayer avec des /10 ou plus, il faut tester, sinon tu fais avec awk et tu rentres le numéro de colonne si tu en as tant beaucoup
Oui j'ai mis dans l'intitulé l'exemple pour le deuxième champ en espérant avoir une solution extensible à n'importe quel champ.
Grand merci pour ton aide.
J'essayerai cette solution demain.
Grand merci pour ton aide.
J'essayerai cette solution demain.
Mamiemando dans l'exemple que j'ai donné les deux derniers chiffres étaient "10" alors qu'après remplacement tu as "9;".
J'y peux rien si la longueur de la chaîne fait 9 :-) Moi je fais ce qui est dit dans l'énoncé, rien ne t'empêche d'ajouter 1 au calcul...
Aussi, ce que je n'ai pas dit c'est que le but est de faire plusieurs traitements de ce type dans et sur plusieurs lignes. Ce qui alourdirait énormément l'idée d'un ficher awk à part.
Moi je veux bien mais en awk tu dois bien préciser les délimiteurs (soit 4 lignes). Tu peux les passer en options (option -F etc...) mais ça ne règlera pas le problème du délimiteur de sortie et surtout, ce n'est pas forcément plus lisible ou plus clair.
Ensuite le code est extensible et me paraît lisible. Il y a peut être moyen de faire mieux mais pour gagner quelques lignes je ne vois pas trop l'intérêt.
Rien ne t'empêche de l'écrire sur une ligne si c'est plus à ton idée :
Par rapport à la discussion sur sed, je trouve que c'est une bonne idée de le connaître, c'est une syntaxe que tu retrouveras dans vim. Mais à mon avis pour des utilisations avancées ça fait doublon avec awk donc ce n'est pas forcément utile de connaître les deux.
Personnellement je fais les substitutions simples dans vim (comparé à sed, on peut annuler si le résultat ne nous plaît pas, débugger l'expression régulire avec "set hls" etc...), et les plus compliquées avec sed.
Enfin je ne pense pas qu'il soit possible de s'en sortir avec juste une substitution, car injecter le calcul de la longueur va être problématique pour le faire avec juste un sed. On peut faire un truc moche, comme par exemple un gsub sur une expression régulière qui va extraire le second champ et le remplacer mais ce sera beaucoup moins lisible et moins évolutif. Après c'est à toi de voir...
J'y peux rien si la longueur de la chaîne fait 9 :-) Moi je fais ce qui est dit dans l'énoncé, rien ne t'empêche d'ajouter 1 au calcul...
Aussi, ce que je n'ai pas dit c'est que le but est de faire plusieurs traitements de ce type dans et sur plusieurs lignes. Ce qui alourdirait énormément l'idée d'un ficher awk à part.
Moi je veux bien mais en awk tu dois bien préciser les délimiteurs (soit 4 lignes). Tu peux les passer en options (option -F etc...) mais ça ne règlera pas le problème du délimiteur de sortie et surtout, ce n'est pas forcément plus lisible ou plus clair.
Ensuite le code est extensible et me paraît lisible. Il y a peut être moyen de faire mieux mais pour gagner quelques lignes je ne vois pas trop l'intérêt.
Rien ne t'empêche de l'écrire sur une ligne si c'est plus à ton idée :
awk 'BEGIN { FS = "|"; OFS = "|"; } $1 ~ /^ABB$/ { $2 = "<champ2>" length($2) ";"; printed = 1; print $0; } { if(!printed) print $0; printed = 0; }' toto.txt
Par rapport à la discussion sur sed, je trouve que c'est une bonne idée de le connaître, c'est une syntaxe que tu retrouveras dans vim. Mais à mon avis pour des utilisations avancées ça fait doublon avec awk donc ce n'est pas forcément utile de connaître les deux.
Personnellement je fais les substitutions simples dans vim (comparé à sed, on peut annuler si le résultat ne nous plaît pas, débugger l'expression régulire avec "set hls" etc...), et les plus compliquées avec sed.
Enfin je ne pense pas qu'il soit possible de s'en sortir avec juste une substitution, car injecter le calcul de la longueur va être problématique pour le faire avec juste un sed. On peut faire un truc moche, comme par exemple un gsub sur une expression régulière qui va extraire le second champ et le remplacer mais ce sera beaucoup moins lisible et moins évolutif. Après c'est à toi de voir...
Essaie avec ça :
$ cat plop ACC|168063100|168063100|1|1680631||0|0||19900101000000||7||250 ABB|168063110|168063112|1|1680631001|1680631|1|0||20110118000000| $ awk 'BEGIN { FS="|";OFS="|" } $1 ~ /^ABB/ {X=length($2)-1; sub(/.*/,"<champ_2>"substr($2, X, length("$2")),$2) }{ print }' plop ACC|168063100|168063100|1|1680631||0|0||19900101000000||7||250 ABB|<champ_2>10|168063112|1|1680631001|1680631|1|0||20110118000000| $
Autre question!
Avez vous une idée de comment passer une variable en paramètre à awk.
Je veux redirriger le résultat en sortie dans un fichier dont le nom et chemin lui aura été passer en paramètre.
Merci d'avance.
Avez vous une idée de comment passer une variable en paramètre à awk.
Je veux redirriger le résultat en sortie dans un fichier dont le nom et chemin lui aura été passer en paramètre.
Merci d'avance.
Salut,
Utilise les rédirections > ou >>
Le fichier doit être entre guillemets
Utilise les rédirections > ou >>
Le fichier doit être entre guillemets
lami20j@debian-acer:~$ cat plop 1 2 3 4 5 lami20j@debian-acer:~$ awk '{print $1 " " $3 }' plop 1 3 lami20j@debian-acer:~$ awk '{print $1 " " $3 > "plop2" } ' plop lami20j@debian-acer:~$ cat plop2 1 3 lami20j@debian-acer:~$ awk '{print $1 " " $3 > "/home/lami20j/plop3" } ' plop lami20j@debian-acer:~$ cat plop3 1 3
Oui c'est ce que je fait actuellement. Je veux faire quelque chose de plus évoluée.
En effet, j'ai un répertoire dont le chemin est récupéré depuis une variable $Repertoire et mon nom de fichier de sortie est incrémenté à chaque lancement.
exemple:
Le problème est qu'en mettant des variables entre " " il ne retourne pas la valeur de la variable(c'est pas mieux sans " " non plus).
En cherchant un peu, j'ai vu qu'il y a possibilité de passer les variables en paramètres au niveau du begin je crois pour que awk puissent les interpréter dans le corps de la fonction.
Mais bon vous vous imaginez bien que mes tests n'ont pas fonctionné ...
En effet, j'ai un répertoire dont le chemin est récupéré depuis une variable $Repertoire et mon nom de fichier de sortie est incrémenté à chaque lancement.
exemple:
print > "$Repertoire/$nom_fic" où nom_fic =' basename plop .txt'_n°incrémenté".txt"
Le problème est qu'en mettant des variables entre " " il ne retourne pas la valeur de la variable(c'est pas mieux sans " " non plus).
print > "$Repertoire/$nom_fic"mettra ainsi le résultat dans le fichier de nom $Repertoire/$nom_fic et non dans le fichier plop .txt'_n°incrémenter_"txt à l'adresse $repertoire.
En cherchant un peu, j'ai vu qu'il y a possibilité de passer les variables en paramètres au niveau du begin je crois pour que awk puissent les interpréter dans le corps de la fonction.
Mais bon vous vous imaginez bien que mes tests n'ont pas fonctionné ...
Re,
c'est pas mieux sans " " non plus).
Oui, c'est mieux mais il faut faire comme ça ;-)
c'est pas mieux sans " " non plus).
Oui, c'est mieux mais il faut faire comme ça ;-)
lami20j@debian-acer:~$ cat plop 1 2 3 4 5 lami20j@debian-acer:~$ awk '{fic="plop2";print $1 " " $3 } ' plop 1 3 lami20j@debian-acer:~$ cat plop2 cat: plop2: No such file or directory lami20j@debian-acer:~$ awk '{fic="plop2";print $1 " " $3 > fic } ' plop lami20j@debian-acer:~$ cat plop2 1 3 lami20j@debian-acer:~$ awk '{rep="/home/lami20j/";fic="plop3";print $1 " " $3 > rep fic } ' plop lami20j@debian-acer:~$ cat plop3 1 3
Salut,
En fait tu as le choix.
Ou tu déclare ta variable avec l'option "-v" de awk :
Soit tu inclues directement tes variables du shell dans awk, mais en ayant pris soin de sortir de awk afin que celles-ci soient bien interprétées :
En fait tu as le choix.
Ou tu déclare ta variable avec l'option "-v" de awk :
awk -v var=MYVAR '{ print var }' ou : awk -v var=$MYVAR '{ print var }'
Soit tu inclues directement tes variables du shell dans awk, mais en ayant pris soin de sortir de awk afin que celles-ci soient bien interprétées :
var=MYVAR awk '{ print ' "$MYVAR" ' }'
J'ai essayé les différentes méthodes et il y en a aucune qui fonctionne.
Voici un extrait de mon code :
Voici un extrait de mon code :
#!/bin/ksh Repertoire=$1 DATE='date '+%y%m%d_%H%M%S'' DateV='date '+%y%m%d'' a=1 for fic_prov in $Repertoire/toto* do nom_fic='basename $fic_prov .txt'_"$a"_"txt" nom_fichier=$Repertoire/$nom_fic; awk 'BEGIN { FS = "|"; OFS = "|"; var= ' "$nom_fichier" '; } $1 ~ /^ACO$/ { $2 = "<ORG>" substr($2,length($2)-1,length($2)) ; $3= "<ORG>" substr($3,length($3)-1,length($3)) ; if(length($6)> 2) {$5= "<ORG>" substr($5,length($5)-2,length($5)) ; $6= "<ORG>" ; $10= "<START>" ; } if(length($6)< 2) {$5= "<ORG>" ;} } $1 ~ /^AC1$/ { $5= substr($5,0,length($5)-10) "<ORG>" substr($5,length($5)-2,length($5)) ; $7= "<START>" ; } { print > var }' $fic_prov a='expr $a + 1' done