[Shell] Remplacer des infos dans un fichier

Remplacer des infos dans un fichier [Shell] - Shell/Batch - Programmation

Marsh Posté le 25-03-2009 à 15:14:21    

Bonjour,
 
 
J'ai un fichier qui se présente comme ceci :
 
1|A
2|B
3|C
 
etc...
 
Comment remplacer une lettre (A, B ou C par exemple) dans le fichier par autre chose en faisant le moins d'étapes possibles ?
 
Actuellement je fais comme ceci :
- Je récupère le numéro de ligne (en fonction de la lettre)
- J'isole la ligne dans un fichier tmp
- Je supprime la ligne du fichier de départ
- Je remplace la lettre par une expression (dans le fichier tmp)
- Je copie la ligne obtenue à l'étape précédente dans le fichier de départ
- Et enfin je renomme le dernier fichier et supprime tous les tmp
 
J'appelle sed pour chaque étape et pour chaque étape j'ai au moins 1 fichier temporaire de créé. C'est pas forcément lourd mais c'est assez fastidieux...
 
C'est pourquoi je cherche une méthode plus simple pour remplacer directement dans le fichier ou éventuellement passer par UN SEUL fichier temp.
 
 
A vous... ;)
 
 
PS : C'est du bash (unix)

Reply

Marsh Posté le 25-03-2009 à 15:14:21   

Reply

Marsh Posté le 25-03-2009 à 15:50:53    

perl -pi -e 's/\|A/|<remplacement>/;' <tonfichier>

Reply

Marsh Posté le 25-03-2009 à 16:52:15    

Ok mais sed me fait la même chose (en 2 étapes mais en moins gourmand) avec sed -e 's/A/<remplacement>/g' <file>, mais si j'ai plusieurs 'A' dans mon fichier (mais d'index différent), ta commande remplace toutes les occurrences!
 
Il faudrait que je puisse passer un numéro de ligne ou bien l'index qui figure sur la ligne où la modification doit être effectuée.

Reply

Marsh Posté le 25-03-2009 à 16:53:47    

Tu ne veux supprimer que la première occurence du fichier ? Je ne comprends pas ce que tu cherches à faire (et l'avantage de perl, en plus d'avoir des regexp PCRE au lieu de POSIX, c'est qu'il modifie le fichier directement, ça te fait une étape de moins par rapport à sed... et côté gourmandise, comment dire... je doute que tu y vois une quelconque différence)


Message édité par Elmoricq le 25-03-2009 à 16:54:26
Reply

Marsh Posté le 25-03-2009 à 17:04:22    

Oui possible pour le côté ressource.
 
En fait dans mon fichier je peux avoir ça :
 
1|A
2|B
3|A
4|C
5|E
6|A
 
Et je veux remplacer la lettre A de l'index 3 avec le moins d'étapes possible et en évitant de multiplier les fichiers temporaires.

Reply

Marsh Posté le 25-03-2009 à 17:24:15    

nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == "3" && $2 ~ "A" ) { $2 = "NEW"; } print $0; }' <fichier>  >  <nouveau fichier>
mv <nouveau fichier> <fichier>

 

ou

 

perl -pi -e 's/3\|A/3|NEW/;' <fichier>

 

ou

 

sed -e 's/3\|A/3|NEW/;' <fichier> > <nouveau fichier>
mv <nouveau fichier> <fichier>

 

Message cité 1 fois
Message édité par Elmoricq le 25-03-2009 à 17:25:33
Reply

Marsh Posté le 25-03-2009 à 18:00:41    

Merci pour ta réponse Elmoricq !
 
La commande avec nawk me plait bien et fonctionne niquel :)

Reply

Marsh Posté le 26-03-2009 à 22:08:02    

Elmoricq a écrit :

nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == "3" && $2 ~ "A" ) { $2 = "NEW"; } print $0; }' <fichier>  >  <nouveau fichier>
mv <nouveau fichier> <fichier>
 
ou bien  
 
sed -e 's/3\|A/3|NEW/;' <fichier> > <nouveau fichier>
mv <nouveau fichier> <fichier>


On peut même le faire en laissant le système gérer le fichier temporaire
 

Code :
  1. #!/bin/sh
  2. exec 3<fichier
  3. rm -f fichier
  4. nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == "3" && $2 ~ "A" ) { $2 = "NEW"; } print $0; }' 0<&3  >fichier
  5. # ou bien
  6. sed -e 's/3\|A/3|NEW/;' 0<&3 >fichier


Message édité par Sve@r le 01-04-2009 à 11:38:13

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 30-03-2009 à 09:13:54    

Tu ne peux pas écrire dans le fichier que tu es en train de lire. Je peux me tromper mais je ne pense pas que le 0<&3 change quoi que ce soit au problème. Au final tu écris toujours dans le fichier que tu es en train de lire.
 
Edit : en fait tu as raison, ça marche. Le nouveau fichier a le même nom que l'ancien fichier mais un inode différent. Ca revient en quelque sorte à transformer le fichier initial en fichier temporaire, sauf qu'on n'a pas à s'embêter à trouver un nom qui va bien.

Message cité 1 fois
Message édité par matafan le 30-03-2009 à 09:57:48
Reply

Marsh Posté le 01-04-2009 à 11:36:57    

matafan a écrit :

Tu ne peux pas écrire dans le fichier que tu es en train de lire. Je peux me tromper mais je ne pense pas que le 0<&3 change quoi que ce soit au problème. Au final tu écris toujours dans le fichier que tu es en train de lire.


Arf... faut tester ses théories avant de les écrire  :sol:  
 

matafan a écrit :

Edit : en fait tu as raison, ça marche. Le nouveau fichier a le même nom que l'ancien fichier mais un inode différent. Ca revient en quelque sorte à transformer le fichier initial en fichier temporaire, sauf qu'on n'a pas à s'embêter à trouver un nom qui va bien.


Tu as décrit effectivement le fonctionnement auquel je pense. Effectivement la création du canal 3 (ou d'un autre) doit se concrétiser quelque part par un inode qui va bien.
Au départ j'avais pensé que tout se faisait en mémoire mais quand j'ai testé et que j'ai vu que ça fonctionnait aussi avec des fichiers qui dépassaient le giga, j'ai abandonné cette idée...
 
J'ai un peu modifié mon post précédent car j'avais dit, à tord, que ça se faisait sans fichier temporaire alors qu'en réalité, il y a bien un fichier quelque part mais simplement on ne s'en préoccupe pas.


Message édité par Sve@r le 01-04-2009 à 11:39:22

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 01-04-2009 à 11:36:57   

Reply

Marsh Posté le 01-04-2009 à 17:31:14    

Sur un korn shell ça me pond une erreur :
 

Code :
  1. ./script.sh[5]: 3: bad file unit number


 
Où 3 correspond au canal que je veux utiliser (comme dans l'exemple de Sve@r)
 
Une idée ?

Reply

Marsh Posté le 01-04-2009 à 18:01:53    

Le faire vieille mode dans un fichier temporaire avec un mv derrière ? [:opus dei]

Reply

Marsh Posté le 02-04-2009 à 10:34:51    

Oui c'est déjà ce que je fais :)
 
Mais ça m'aurait permis d'économiser une étape :)

Reply

Marsh Posté le 02-04-2009 à 11:17:17    

Il est moisi ton ksh, avec le miens ça marche bien (sous AIX).

Reply

Marsh Posté le 02-04-2009 à 14:34:10    

Tonio94 a écrit :

Sur un korn shell ça me pond une erreur :
 

Code :
  1. ./script.sh[5]: 3: bad file unit number


 
Où 3 correspond au canal que je veux utiliser (comme dans l'exemple de Sve@r)
 
Une idée ?

matafan a écrit :

Il est moisi ton ksh, avec le miens ça marche bien (sous AIX).



 
Je l'aurais pas dit ainsi mais effectivement, j'étais sceptique sur le fait que ça ne fonctionne pas en ksh. J'ai tenté de tester ce matin mais nous n'avons plus de machine tournant sous ksh. Cependant ksh se voulant compatible shell, c'était assez bizarre que ça ne fonctionnat pas.
 
Petit détail: il ne faut pas d'espace entre le chiffre et le "<". Donc
exec 3 < fichier    => ne fonctionne pas
exec 3< fichier     => marche nickel
 

Elmoricq a écrit :

Le faire vieille mode dans un fichier temporaire avec un mv derrière ? [:opus dei]

Tonio94 a écrit :

Oui c'est déjà ce que je fais :)
 
Mais ça m'aurait permis d'économiser une étape :)



 
Sans approfondir sur le fait que c'est dommage de s'embêter à faire soi-même ce que le système fait déjà, gérer un fichier temporaire doit s'accompagner de précautions surtout contre le multi processus. Si le fichier temporaire est un bête "toto.txt" MAIS que le script est lancé plusieurs fois en parallèle, il risque d'y avoir un sacré chaos au niveau des infos contenues dans "toto.txt".
Obligation donc de s'occuper du
- nom du fichier temporaire qui doit être associé au processus=> utilisation de la variable "$$"
- nettoyage du fichier temporaire en fin de tâche
- nettoyage du fichier temporaire en cas d'interruption intempestive du script
Et là, ce qui semblait au début un truc tout con devient de suite plus délicat à gérer....


Message édité par Sve@r le 02-04-2009 à 14:45:31

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 03-04-2009 à 15:56:13    

Et bien justement j'ai plusieurs exécutions de mon script en parallèle, et chaque exécution écrit dans le même fichier, pas forcément au même moment, mais ça peut arriver.
 
J'avais fait un oubli mais ça fonctionne bien sous mon ksh merci Sve@r :)
 
Par contre quel est le traitement effectué par exec ? Dans mon script je fais plusieurs nawk pour remplacer des références dans mon fichier, dois-je faire le exec et le rm avant chaque ou simplement au début de mon script (ce que j'imagine) ? Dsl pour la question bateau... :|

Reply

Marsh Posté le 03-04-2009 à 22:04:21    

Tonio94 a écrit :

Par contre quel est le traitement effectué par exec ?


Ca te crée un canal numéroté en input (exec 3<fichier) ou output (exec 4>result) associé au fichier sus nommé.
 
Ensuite, lire le canal input (read line 0<&3) ou écrire dans le canal output (echo truc 1>&4) aura une répercussion sur le fichier associé au canal
 

Tonio94 a écrit :

Dans mon script je fais plusieurs nawk pour remplacer des références dans mon fichier, dois-je faire le exec et le rm avant chaque ou simplement au début de mon script (ce que j'imagine) ? Dsl pour la question bateau... :|


Le rm est là pour nettoyer le fichier avant d'y écrire dedans mais en fait, c'en est même inutile. Le exec tu le fais juste avant d'avoir besoin de lire le fichier !!!


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 04-04-2009 à 20:29:46    

Non non, le rm est crucial, sans ça l'écriture se fait dans le fichier que tu es en train de lire (et tu finit probablement avec un fichier vide).
 
Ca veut dire aussi que tu ne peux pas utiliser cette méthode si tu as besoin de lire plusieurs fois le fichier initial, parce qu'après la première lecture le fd 3 est fermé, et à ce moment là les données s'envolent.

Reply

Marsh Posté le 07-04-2009 à 16:18:38    

Effectivement j'ai testé et vérifié ! C'est propre comme méthode mais pas forcément pratique quand on a besoin d'écrire souvent dans le fichier et encore moins quand le même script le fait en parallèle.

 

Petite question sur la ligne suivante :

 
Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '$var' ) { $2 = '$new_value'; } print $0; }' file > tmp.file
 

J'aimerais savoir comment remplacer le "$2" qui représente le deuxième élement de mon fichier file par une variable qui contiendra le numéro de colonne.

 

Dans ce genre là :

 
Code :
  1. column="$2" ou column=`echo $2`
  2. nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '$var' ) { $column = '$new_value'; } print $0; }' file > tmp.file
 

Car j'écris souvent cette même ligne et j'aimerais la mettre dans une fonction.

 


Merci.

Message cité 1 fois
Message édité par Tonio94 le 07-04-2009 à 16:19:15
Reply

Marsh Posté le 07-04-2009 à 16:53:02    

matafan a écrit :

Non non, le rm est crucial, sans ça l'écriture se fait dans le fichier que tu es en train de lire (et tu finit probablement avec un fichier vide).


T'as raison (je viens de tester). Ceci dit, si le exec crée un fichier temporaire contenant la copie du fichier source, je m'explique mal que ça ne fonctionne que si la source a disparu. Ou alors le fichier est créé lors du rm. Bref il y a un manque dans mon raisonnement. Toutefois ça marche avec le rm et finalement c'est le principal.
 

Tonio94 a écrit :

Effectivement j'ai testé et vérifié ! C'est propre comme méthode mais pas forcément pratique quand on a besoin d'écrire souvent dans le fichier et encore moins quand le même script le fait en parallèle.


Arf évidemment si tu cherches les limites de la méthode, tu les trouves vite. Ceci dit, je vois mal l'utilité d'un script qui va écrire en parallèle (sous-entendu plusieurs fois) le fichier cible (qui sera donc le même à chaque fois)  :pt1cable:  
 

Tonio94 a écrit :

Petite question sur la ligne suivante :
 

Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '$var' ) { $2 = '$new_value'; } print $0; }' file > tmp.file


 
J'aimerais savoir comment remplacer le "$2" qui représente le deuxième élement de mon fichier file par une variable qui contiendra le numéro de colonne.


Ben te suffit de mettre "col=2" dans ton BEGIN puis demander "$col" qui correspondra alors au 2° mot. Et si tu veux que la valeur "2" puisse arriver depuis l'extérieur (le shell) faut utiliser "-v" lors de l'appel à awk (fonctionne avec les awk évolués comme gawk mais sans garantie pour nawk)
 

Code :
  1. nawk -vcol=2 -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '$var' ) { $col = '$new_value'; } print $0; }' file > tmp.file


Message édité par Sve@r le 07-04-2009 à 16:57:59

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 07-04-2009 à 17:27:19    

en fait ce qui ce passe c'est ça :
 
1) Le "exec 3<fichier" ouvre le fichier et l'associe au fd 3
 
2) Le "rm fichier" supprime le fichier. Comme le fichier a encore des fd ouverts (notre fd 3), il est juste supprimé du répertoire, mais les données restent sur disque. D'ailleurs si on fait un du sur le répertoire qui contient le fichier, on voit que l'espace disque n'a pas été libéré, alors qu'on ne peut pourtant plus accéder au fichier par son nom.
 
3) Le "truc 0<&3 >fichier" crée un fichier "fichier". Ce fichier est un fichier comme un autre, qui n'a rien à voir avec le fichier initial, même s'il porte le même nom. D'ailleur sont numéro d'inode est différent du fichier initial. La commande lit les données du fd 3, qui sont les données "fantome" de notre fichier initial, qui n'existe plus mais qui a encore ses donées sur disque.
 
4) Quand truc a fini de lire le fd 3, le fd est fermé. A ce moment là il n'y a plus de fd associé au fichier initial, et ses données sont supprimées.
 
Donc il n'y a pas vraiment de fichier temporaire. Il y a juste les données du fichier initial, qui ne sont plus associées à un nom de fichier. C'est le mécanisme classique sous unix : quand un fichier est unlinké, il disparait du répertoire et on ne peut plus l'ouvir, mais ses données restent sur disque jusqu'à ce que le dernier fd associé au fichier ai été fermé.

Reply

Marsh Posté le 07-04-2009 à 17:40:51    

Merci t'es un chef Sve@r ! ;)

 

On peut déclarer ce qu'on veut comme variable dans le BEGIN ?

 

Car j'essaie avec une modification de 2 colonnes mais il ne me fait que la 2e :

 
Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; col1=2; col2=3 } { if ($1 == '$var' ) { $col1 = '$value1' && $col2 = '$value2'; } print $0; }' file > file.tmp
 

Et autre problème, quand je passe des valeurs numériques aucun soucis mais quand je passe une chaine de caractère à $value il ne m'écrit rien dans le fichier et écrase le contenu de la colonne (à l'emplacement de $var je précise).

 

Merci.

 


PS : Pour l'histoire du script lancé en parallèle, en fait il prend en paramètre des chemins différents donc chaque exécution n'analyse pas les mêmes fichiers, mais ils doivent par contre tous écrire dans un même fichier de log commun.

Message cité 1 fois
Message édité par Tonio94 le 07-04-2009 à 17:54:53
Reply

Marsh Posté le 07-04-2009 à 17:43:14    

Merci pour l'explication détaillée matafan :)

Reply

Marsh Posté le 08-04-2009 à 13:16:20    

matafan a écrit :


2) Le "rm fichier" supprime le fichier. Comme le fichier a encore des fd ouverts (notre fd 3), il est juste supprimé du répertoire, mais les données restent sur disque. D'ailleurs si on fait un du sur le répertoire qui contient le fichier, on voit que l'espace disque n'a pas été libéré, alors qu'on ne peut pourtant plus accéder au fichier par son nom.


Arf, non, pas chez-moi. Ma size a changé.
 

matafan a écrit :

3) Le "truc 0<&3 >fichier" crée un fichier "fichier". Ce fichier est un fichier comme un autre, qui n'a rien à voir avec le fichier initial, même s'il porte le même nom. D'ailleur sont numéro d'inode est différent du fichier initial.


Arf exact, le n° d'inode en fin de travail est toujours différent (alors que si on n'utilise pas de canal numéroté et qu'on s'amuse à effacer puis recréer le fichier, le n° d'inode ne change pas toujours)
 

matafan a écrit :

Donc il n'y a pas vraiment de fichier temporaire. Il y a juste les données du fichier initial, qui ne sont plus associées à un nom de fichier. C'est le mécanisme classique sous unix : quand un fichier est unlinké, il disparait du répertoire et on ne peut plus l'ouvrir, mais ses données restent sur disque jusqu'à ce que le dernier fd associé au fichier ai été fermé.


Joli !!! Et inversement si on ne fait pas le "rm", le nom de fichier n'est pas unlinké et est donc toujours associé à son contenu (ainsi que le canal 3) et donc écraser le fichier revient à perdre le contenu (et le canal 3 ne sert à rien). Excellent  :bounce:  
 
 

Tonio94 a écrit :

Merci t'es un chef Sve@r ! ;)
 
On peut déclarer ce qu'on veut comme variable dans le BEGIN ?
 
Car j'essaie avec une modification de 2 colonnes mais il ne me fait que la 2e :
 

Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; col1=2; col2=3 } { if ($1 == '$var' ) { $col1 = '$value1' && $col2 = '$value2'; } print $0; }' file > file.tmp


 
Et autre problème, quand je passe des valeurs numériques aucun soucis mais quand je passe une chaine de caractère à $value il ne m'écrit rien dans le fichier et écrase le contenu de la colonne (à l'emplacement de $var je précise).
 
Merci.


Hum. Je ne suis pas un pro de awk mais je suis un peu dubitatif sur ta façon de faire. Il faut bien comprendre que les éléments $1 $2 $3 ne sont pas des variables (comme le $1 du shell) mais des mots de ta ligne (donc des valeurs de travail de awk) et donc j'ai un doute sur le fait que tu aies le droit de dire arbitrairement "$1=truc" alors qu'a l'origine, $1 a été prévu pour contenir autre chose. Ptet que tes problèmes viennent de là...
 
Tu ne peux pas faire plutôt l'inverse, c.a.d. récupérer $1, $2, $3 dans des variables bien à toi que tu pourras changer à ta guise si l'algo l'impose ???


Message édité par Sve@r le 08-04-2009 à 13:17:15

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 08-04-2009 à 14:59:55    

En fait j'appelle souvent nawk de la même façon mais avec les valeurs et la position de colonne qui changent. Alors plutôt que de le répéter plusieurs fois dans mon code j'aimerais le mettre dans une fonction, dans ce style là :

 
Code :
  1. #!/bin/ksh
  2. function replace
  3. {
  4.   ref=$2
  5.   value=$3
  6.   nawk -F'|' 'BEGIN{ OFS = "|"; col='$1' } { if ($1 == '$ref' ) { $col = '$value'; } print $0; }' file > file.tmp
  7.   mv file.tmp file
  8. }
  9. replace 2 index1 2009
  10. # end
 

Écris de cette manière ça fonctionne (grâce à ton précèdent post), quand j'appelle la fonction replace le premier paramètre passé est "2" qui correspond au numéro de colonne à remplacer puis "index1" qui correspond à la réference/index (pour savoir à quelle ligne il faut remplacer) et enfin "2009" qui est la valeur à écrire à cet emplacement.

 

Ensuite j'ai deux problèmes :
- Lorsque à la place de 2009 je veux passer une chaine de caractère, par exemple "test", dans ce cas nawk m'écrase la valeur à remplacer dans le fichier 'file' mais ne m'écrit pas la chaine à écrire.
- Lorsque je veux remplacer 2 valeurs d'un coup. A ce moment là je passe en paramètre 2 numéros de colonnes (au lieu d'un) et 2 valeurs à remplacer. Comme je l'ai écris dans mon exemple de code précèdent. Mais il ne me fait qu'un remplacement sur les deux :

 
Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; col1=2; col2=3 } { if ($1 == '$var' ) { $col1 = '$value1' && $col2 = '$value2'; } print $0; }' file > file.tmp
 

(seulement col2 est mis à jour dans le file)

 


Je sais bien que $1, $2, $3, etc correspondent dans le shell aux arguments du script ou d'une fonction et que pour awk ils correspondent aux numéros des "colonnes". Mais dans mon cas la colonne où il y a la valeur à changer est souvent modifiée, dans ce cas je ne peux pas écrire en statique $1, $2, $3 dans ma ligne awk, il faut que je le passe en paramètre de ma fonction.

 

Je ne sais pas si c'est plus clair comme cela, en espérant que tu puisses me filer un coup de pouce ;) Merci

Message cité 1 fois
Message édité par Tonio94 le 08-04-2009 à 15:01:49
Reply

Marsh Posté le 09-04-2009 à 13:16:14    

Tonio94 a écrit :

En fait j'appelle souvent nawk de la même façon mais avec les valeurs et la position de colonne qui changent. Alors plutôt que de le répéter plusieurs fois dans mon code j'aimerais le mettre dans une fonction, dans ce style là :
 

Code :
  1. #!/bin/ksh
  2. function replace
  3. {
  4.   ref=$2
  5.   value=$3
  6.   nawk -F'|' 'BEGIN{ OFS = "|"; col='$1' } { if ($1 == '$ref' ) { $col = '$value'; } print $0; }' file > file.tmp
  7.   mv file.tmp file
  8. }
  9. replace 2 index1 2009
  10. # end


 
Écris de cette manière ça fonctionne (grâce à ton précèdent post), quand j'appelle la fonction replace le premier paramètre passé est "2" qui correspond au numéro de colonne à remplacer puis "index1" qui correspond à la réference/index (pour savoir à quelle ligne il faut remplacer) et enfin "2009" qui est la valeur à écrire à cet emplacement.


 
Je suis très étonné que cela fonctionne correctement vu que tu ne passes pas tes variables à awk. Tu appelles awk en laissant au shell le soin de remplacer "$value" par la valeur correspondante. Si tu rajoutes 'set -x" juste avant le awk, tu verras un truc ressemblant à ça

Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; col=2 } { if ($1 == index1 ) { $col = 2009; } print $0; }' file > file.tmp


 

Tonio94 a écrit :

Ensuite j'ai deux problèmes :
- Lorsque à la place de 2009 je veux passer une chaine de caractère, par exemple "test", dans ce cas nawk m'écrase la valeur à remplacer dans le fichier 'file' mais ne m'écrit pas la chaine à écrire.


Hé oui. Il faudrait qu'il y ait écrit $col='test' or le nawk reçoit l'instruction $col=test (sans quotte). Ceci étant issu de la remarque ci-dessus
 
Voici ce que moi j'ai écrit

Code :
  1. #!/bin/sh
  2. function replace
  3. {
  4.     awk -F'|' -vcol=$1 -vref=$2 -vval=$3 'BEGIN{ OFS = "|" } { if ($1 == ref ) { $col = val; } print $0; }' file
  5. }
  6.  
  7. replace 3 index1 test
  8. # end


 
Et ça fonctionne totalement sur un fichier de ce style

Code :
  1. index1|toto|titi|tutu


 
Je me retrouve en sortie avec un fichier de ce style

Code :
  1. index1|toto|titi|test


 

Tonio94 a écrit :

Je sais bien que $1, $2, $3, etc correspondent dans le shell aux arguments du script ou d'une fonction et que pour awk ils correspondent aux numéros des "colonnes". Mais dans mon cas la colonne où il y a la valeur à changer est souvent modifiée, dans ce cas je ne peux pas écrire en statique $1, $2, $3 dans ma ligne awk, il faut que je le passe en paramètre de ma fonction.


Oui mais t'as oublié de les passer en tant que variables à awk !!!
 

Tonio94 a écrit :

- Lorsque je veux remplacer 2 valeurs d'un coup. A ce moment là je passe en paramètre 2 numéros de colonnes (au lieu d'un) et 2 valeurs à remplacer. Comme je l'ai écris dans mon exemple de code précèdent. Mais il ne me fait qu'un remplacement sur les deux :
 

Code :
  1. nawk -F'|' 'BEGIN{ OFS = "|"; col1=2; col2=3 } { if ($1 == '$var' ) { $col1 = '$value1' && $col2 = '$value2'; } print $0; }' file > file.tmp


 
(seulement col2 est mis à jour dans le file)


Faudrait pouvoir passer un tableau à awk et ça, suis pas certain qu'on puisse faire.
 
Moi pour faire ça je ruse en écrivant une boucle dans la fonction pour traiter chaque remplacement

Code :
  1. #!/bin/sh
  2. function replace
  3. {
  4.     ref=$1; shift
  5.  
  6.     for item in $*
  7.     do
  8.         col=`echo $item |cut -f1 -d:`
  9.         val=`echo $item |cut -f2 -d:`
  10.  
  11.         exec 3<file
  12.         rm -f file
  13.         awk -F'|' -vcol=$col -vref=$ref -vval=$val 'BEGIN{ OFS = "|" } { if ($1 == ref ) { $col = val; } print $0; }' 0<&3 >file
  14.     done
  15. }
  16.  
  17. replace index1 2:x 3:y 4:z
  18. # end


 
Et ça fonctionne totalement sur un fichier de ce style

Code :
  1. index1|toto|titi|tutu


 
Je me retrouve en sortie avec un fichier de ce style

Code :
  1. index1|x|y|z


 
Bien entendu, tout ça sous bash+gawk Linux car j'ai pas ksh+nawk pour tester...


Message édité par Sve@r le 09-04-2009 à 13:17:21

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 22-04-2009 à 11:39:58    

Merci pour ta contribution c'est top ! je vais mettre tout ça en application ! (dsl pour le temps de réaction, retour de vacances!) ;)
 
Tant qu'on est sur awk, un truc tout simple qui ne fonctionne pas et je ne vois pas pourquoi :
 

Code :
  1. FILE=file.tmp
  2. VALUE=toto
  3. INFO=`nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '${VALUE}' ) { print $2; } }' $FILE`
  4. echo $INFO


 
Il n'y a rien dans $INFO.... :|
 
Alors que si je tappe la ligne nawk directement dans le shell en remplaçant les variables par leurs valeurs, ça fonctionne o_o
 
Contenu du fichier scanné :
toto-1|titi-1|tata-1
toto-2|titi-2|tata-2
etc.
 
 
Si tu as une idée je sèche, j'ai fait autrement avec sed en attendant mais c'est plus long :\

Message cité 1 fois
Message édité par Tonio94 le 22-04-2009 à 11:47:18
Reply

Marsh Posté le 27-04-2009 à 14:53:34    

Tonio94 a écrit :

Merci pour ta contribution c'est top ! je vais mettre tout ça en application ! (dsl pour le temps de réaction, retour de vacances!) ;)
 
Tant qu'on est sur awk, un truc tout simple qui ne fonctionne pas et je ne vois pas pourquoi :
 

Code :
  1. FILE=file.tmp
  2. VALUE=toto
  3. INFO=`nawk -F'|' 'BEGIN{ OFS = "|"; } { if ($1 == '${VALUE}' ) { print $2; } }' $FILE`
  4. echo $INFO


 
Il n'y a rien dans $INFO.... :|
 
Alors que si je tappe la ligne nawk directement dans le shell en remplaçant les variables par leurs valeurs, ça fonctionne o_o
 
Contenu du fichier scanné :
toto-1|titi-1|tata-1
toto-2|titi-2|tata-2
etc.
 
 
Si tu as une idée je sèche, j'ai fait autrement avec sed en attendant mais c'est plus long :\


 
T'as toujours pas appris à passer une variable à awk ? J'en ai pourtant parlé 2 fois !!!
 

Code :
  1. FILE=file.tmp
  2. VALUE=toto
  3. INFO=`nawk -F'|' -vval=$VALUE 'BEGIN{ OFS = "|"; } { if ($1 == val ) { print $2; } }' $FILE`
  4. echo $INFO


 
C'est pas bien de mettre ses variables en majuscule. Les majuscules sont réservées aux variables système (PATH, HOME, etc)


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 28-05-2009 à 14:59:55    

Autant pour moi :\
 
Merci encore Sve@r pour ta précieuse aide ! :)

Reply

Marsh Posté le 30-05-2009 à 19:44:34    

Tonio94 a écrit :

Autant pour moi :\
 
Merci encore Sve@r pour ta précieuse aide ! :)


 
Hey, 30 jours plus tard !!!???!!!
 
Enfin vieux motard...  :sol:


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 03-06-2009 à 13:58:14    

Sve@r a écrit :


 
Hey, 30 jours plus tard !!!???!!!
 
Enfin vieux motard...  :sol:


 
 
Disons qu'entre temps j'avais fait un truc avec sed - un peu crade - qui fonctionnait et je n'étais pas repassé par là.
 
Mais je pense que ce thread pourra en aider plus d'un ! ;)

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed