Question [Function PS v2] Replace-String

Plus d'informations
il y a 13 ans 8 mois #7486 par Laurent Dardenne
[EDIT] Script mis à jour

Salut,
je vous propose une fonction avancée permettant une opération de cherche/remplace.
Elle dispose d'une aide intégrée (Replace-String -?) contenant de nombreux exemples (13).

Son principe se base sur les méthodes de remplacement String.Replace() ou sur Regex.Replace() (par défaut).
Le paramètrage se fait via une hastable respectant un format 'normalisé'.
Cette fonction permet :
-des remplacements multiples sur une même chaîne de caractères,
-des remplacements multiples sur des propriétés d'objets (les jokers sont supportés),
-d'exécuter une seule opération de recherche et de remplacement parmis une liste (la première qui réussit),
-de récupérer la valeur de la chaîne de remplacement à partir d'un scriptblock,
-de remplacer 1, plusieurs ou toutes les occurences trouvées,
-de débuter la recherche à une position donnée, par exemple à partir du 10 caractères,
-de préciser des options sur les regex [System.Text.RegularExpressions.RegexOptions],
-de récupérer le résultat d'exécution de chaque regex dans un objet personnalisé,
-d'utiliser des conversions de tableaux basée sur la variable $OFS,
-de générer des fichiers ou du code à partir de templates,
-de combiner l'ensemble de ces possibilités.

Voici quelques exemples, je vous laisse lire le détail dans la documentation.

Remplacement multiple dans une chaîne à l'aide d'expression régulière :
[code:1]
$S= \"Caractères : 33 \d\d\"
$h=@{}
$h.\"a\"=\"?\" # regex 1
$h.\"\d\"='X' # regex 2
Replace-String -i $s $h
#Voir le résultat dans la doc
[/code:1]
Remplacement multiple dans une chaîne, on utilise un délégué (MatchEvaluator):
[code:1]
$S= \"Caractères : 33\"
$h=@{}
$h.\"a\"=\"?\" # regex 1
$h.\"(?<Chiffre>\d)\"='${Chiffre}X' # regex 2
$h.\":\"={ Write-Warning \"Call delegate\"; return \"<$($args[0])>\"} # regex 3
$S|Replace-String $h
[/code:1]
Remplacement multiple dans une chaîne, on paramètre l'expression régulière :
[code:1]
$S= \"CAractères : 33\"
$h=@{}
$h.\"a\"=@{Replace=\"?\";StartAt=3;Options=\"IgnoreCase\"} #hastable normalisée.
#La recherche débute à partir du 3ième caractéres
$h.\"\d\"=@{Replace='X';Max=1} #Un seul remplacement
$S|Replace-String $h
[/code:1]
On récupère dans une variable une liste d'objets personnalisés détaillant les opérations :
[code:1]
$O=New-Object PSObject -Property @{Name=\"Test_ToDelete.tmp\";Path=\"C:\Temp\";Value=\"Fichier de tests.\";Type=\"File\"}
$G=New-Object PSObject -Property @{Name=\"TestRpls.ps1\";Path=\"D:\PS\Tests\";Value=\"Test-Object\";Type=\"File\"}
\"Test Replace-String -Property\"|Set-Content \"C:\Temp\Test_Replace-String-Property.txt\" -Force
$F=dir \"C:\Temp\Test_Replace-String-Property.txt\"
#On convertis les objets en [String]
#Les objets ne sont pas modifiés
$Result=$O,$G,$F|rpls @{\"Test\"=\"New\"} -ReplaceInfo
$Result[0]|fl
$Result|% {$_.Replaces|fl}
[/code:1]
Remplacement multiple de propriétés d'objets ( de type [string] en R/W), pour chaque objet reçu on effectue un seul traitement parmis plusieurs:
[code:1]
$AllObjects=dir Variable:
$AllObjects| Ft Name,Description|More
$h=@{}
$h.\"^$\"={\"Nouvelle description de la variable $($InputObject.Name)\"}
#PowerShell V2 FR
$h.\"(^Nombre|^Indique|^Entraîne)(.*)$\"='POWERSHELL $1$2'
#Les objets sont modifiés
$Result=$AllObjects|Replace-String $h -property \"Description\" -ReplaceInfo -Unique
$AllObjects| Ft Name,Description|More
[/code:1]
Remplacement multiple dans des fichiers :
[code:1]
#Paramètrage
$NumberVersion=\"1.2.1\"
$Version=\"# Version : $Numberversion\"
#La date est substituée une seule fois lors
#de la création de la hashtable.
$Modifications= @{
\"^\s*\#\s*Version\s*:«»(.*)$\"=$Version;
'^\s*\#\s*Date\s*:«»(.*)$'=\"# Date : $(Get-Date -format 'd MMMM yyyy')\"
}
$RunWinMerge=$False

#Fichiers de test :
# projets.developpez.com/projects/add-lib/files

cd \"G:\PS\Replace-String\TestReplace\"
#Cherche et remplace dans tous les fichiers .ps1 d'une arborescence, sauf les .bak
#Chaque fichier est recopié en .bak avant les modifications
Get-ChildItem *.ps1 -exclude *.bak -recurse|
Where-Object {!$_.PSIsContainer} |
ForEach-Object {
$CurrentFile=$_
$BackupFile=\"$($CurrentFile).bak\"
Copy-Item $CurrentFile $BackupFile

Get-Content $BackupFile|
Replace-String $Modifications|
#binding automatique
Set-Content -path $CurrentFile

#compare le résultat à l'aide de Winmerge
if ($RunWinMerge)
{Microsoft.PowerShell.Management\start-process \"C:\Program Files\WinMerge\WinMergeU.exe\" -Argument \"/maximize /e /s /u $BackupFile $CurrentFile\" -wait}
} #foreach
[/code:1]
Remplacement multiple basé sur une alternative, construite à partir d'un tableau de chaîne, couplée à un scriptblock :
[code:1]
$S=\"Un petit deux-roues, c'est trois fois rien.\"
$Alternatives=@(\"un\",\"deux\",\"trois\"«»)
#En regex '|' est le métacaractère
#pour les alternatives.
$ofs=\"|\"
$h=@{}
$h.$Alternatives={
switch ($args[0].Groups[0].Value) {
\"un\" {\"1\"; break}
\"deux\" {\"2\"; break}
\"trois\" {\"3\"; break}
}#switch
}#$s
$S|Replace-String $h
$ofs=\"\"
[/code:1]
Il est possible d'utiliser la librairie de regex du projet PSCX :
[code:1]
\"un deux deux trois\"|Replace-String @{$PSCX:RegexLib.RepeatedWord=\"Deux\"}
#renvoi
#un deux trois
[/code:1]
Le fichier joint contient le script de la fonction et un fichier de tests à compléter.
La fonction supporte le paramètre -Whatif, génére des erreurs non bloquante catégorisées et contient des traces pour le debug.

La pièce jointe Replace_String.zip est absente ou indisponible

<br><br>Message édité par: Laurent Dardenne, à: 22/08/10 16:24

Tutoriels PowerShell
Pièces jointes :

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 8 mois #7489 par Richard Lazaro
J'ai parcouru rapidement le codes.
Ben bravo, une grosse doc, lisible comme j'aime :]

Après, pourquoi n'utilises-tu pas la variable $PSCmdlet ?

Et puis j'aime pas trop les return de partout comme je te l'avais déjà dis ... mais j'ai une vision d'une programmation C#. Peut être qu'en PowerShell c'est admis d'avoir des return de partout.

Think-MS : (Get-Life).Days | %{ Learn-More }

\\&quot;Problems cannot be solved by the same level of thinking that created them.\\&quot; - Albert Einstein

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 8 mois #7490 par Richard Lazaro
Apres, c'est à tester, car l'utilisation à l'air bien compliqué.

Think-MS : (Get-Life).Days | %{ Learn-More }

\\&quot;Problems cannot be solved by the same level of thinking that created them.\\&quot; - Albert Einstein

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 8 mois #7493 par Laurent Dardenne
Salut,
Richard Lazaro écrit:

Ben bravo, une grosse doc, lisible comme j'aime :]

Merci pour ton retour.
J'essaie d'être cohérent, si parfois je critique le manque de documentation sur Powershell, je m'efforce de ne pas faire pareil :)
Richard Lazaro écrit:

Après, pourquoi n'utilises-tu pas la variable $PSCmdlet ?

Dans quel contexte ? As-tu un exemple ?
Richard Lazaro écrit:

Et puis j'aime pas trop les return de partout comme je te l'avais déjà dis ...

Effectivement, une seule fonction en fait un usage prononcé.
Ayant fait il y a pas mal de temps de l'assembleur, les différentes approches sont compilées en un simple saut (JMP ).
Je te l'accorde ce n'est pas académique, plutôt pragmatique, d'une part pour la relecture et le debuggage, d'autre part c'est un code \&quot;boule de neige\&quot;.Je suis parti d'un script de 4-5 lignes pour un besoin précis, et au fur et à mesure j'ai ajouté des fonctionnalites en ayant à l'esprit de disposer d'un outil aussi riche et puissant que l'opérateur -Split disponible sous la V2 (voir about_split.help.txt).
L'équipe de PowerShell n'a pas étendue cette conception pour l'opérateur -Replace, c'est dommage.
Pour en revenir à l'usage des return, cela reflète sûrement un codage sans spécicifations détaillées, une réécriture s'impose donc. Mais avant, je vais déjà voir ce qu'il donne en l'état.
Richard Lazaro écrit:

Apres, c'est à tester, car l'utilisation à l'air bien compliqué.

J'ai conscience que de prime abord cette fonction est difficile à saisir, pourtant il n'y a que 6 paramètres dont 3 switch (Le code peut peut-être l'être).
Par défaut on traite des [string] et on renvoi des [string].
Si on précise -Property ont traite des objets et on renvoi des objets.
Enfin si on précise -Replaceinfo on encapsule soit la string soit l'objet dans un objet personnalisé.
Ce dernier permet, comme on autorise plusieurs opérations de remplacement sur une même instance, de récupérer l'instance finale et le résultat (vrai/faux) de l'opération de remplacement.

Le problème est vraisemblablement dans l'usage du paramètre -Hashtable (Hashtable imbriquées).
Comme Replace-String propose des remplacements multiples, je n'ai pas trouvé d'autre approche permettant de regrouper toutes les informations de paramètrage de la méthode suivante :.
[code:1] Regex.Replace (String, MatchEvaluator, Int32, Int32)[/code:1]

La difficulté est peut être aussi dans les nombreuses combinaisons et comportements liés aux trois Switch.
J'ai peut-être pêché par gourmandise ;-)

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 8 mois #7496 par Richard Lazaro
J'ai relu le help plus attentivement, et ça va. Mais c'est vrai que de prime abord ça à l'air compliqué à l'utilisation mais ne fait ça va ^^

Je te parle de la variable $PsCmdlet pour le retour de donnée avec la méthode WriteObject, l'écriture de verbose avec la méthode WriteVerbose, etc ...

WriteError
ThrowterminatingError
WriteDebug

Think-MS : (Get-Life).Days | %{ Learn-More }

\\&quot;Problems cannot be solved by the same level of thinking that created them.\\&quot; - Albert Einstein

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 8 mois #7499 par Laurent Dardenne
Richard Lazaro écrit:

Je te parle de la variable $PsCmdlet pour le retour de donnée avec la méthode WriteObject

Question d'habitude je pense, pour celle-ci on avait vu qu'elle n'émettait pas les membres personnalisés.
Je vais tester un peu plus en profondeur.
Richard Lazaro écrit:

, l'écriture de verbose avec la méthode WriteVerbose, etc ...
WriteError
ThrowterminatingError
WriteDebug

Idem bien que j'utilise déjà Write-Error, ici aussi je vais regarder ce que ça donne. En passant le comportement du paramétre -WarningAction est bugé, voir MSConnect.

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.090 secondes
Propulsé par Kunena