web-dev-qa-db-fra.com

Utilisez Powershell pour remplacer la sous-section du résultat d'expression régulière

En utilisant Powershell, je sais comment rechercher un fichier pour une chaîne compliquée à l'aide d'une expression régulière, et remplacer cela par une valeur fixe, comme dans l'extrait de code suivant:

Get-ChildItem  "*.txt" |
Foreach-Object {
    $c = ($_ | Get-Content)
    $c = $c -replace $regexA,'NewText'
    [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}

Maintenant, j'essaie de comprendre comment remplacer un sous-paragraphe de chaque correspondance d'une expression régulière. Cela peut-il être fait en une seule étape comme ci-dessus? Ou devez-vous extraire chaque correspondance de l'expression rationnelle la plus grande, la rechercher et la remplacer, puis coller le résultat dans le texte d'origine?

Pour clarifier avec un exemple, supposons que dans le texte de test suivant, je souhaite trouver uniquement les instances numérotées 14xx comme "TEST = * 1404" dans le texte suivant et remplacer le 14xx par 16xx?

A 2180 1830 12 0 3 3 TEST=C1404
A 900 1830 12 0 3 3 TEST=R1413
A 400 1830 12 0 3 3 TEST=R1411
A 1090 1970 12 0 3 3 TEST=U1400
A 1090 1970 12 0 3 3 TEST=CSA1400
A 1090 1970 12 0 3 3 TEST=CSA1414
A 1090 1970 12 0 3 3 TEST=CSA140
A 1090 1970 12 0 3 3 TEST=CSA14001
A 1090 1970 12 0 3 3 TEST=CSA17001

C'est à dire. J'aimerais que le texte résultant soit le suivant, où vous remarquerez que seules les 6 premières lignes devraient changer:

A 2180 1830 12 0 3 3 TEST=C1604
A 900 1830 12 0 3 3 TEST=R1613
A 400 1830 12 0 3 3 TEST=R1611
A 1090 1970 12 0 3 3 TEST=U1600
A 1090 1970 12 0 3 3 TEST=CSA1600
A 1090 1970 12 0 3 3 TEST=CSA1614 <- Second instance of '14' shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA140 <- Shorter numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA14001 <- Longer numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA17001

Le regex suivant semble faire le travail de trouver les plus grandes chaînes où j'ai besoin de faire des remplacements, mais je ne sais pas quelle fonctionnalité dans Powershell (replace?) Utiliser pour remplacer simplement la sous-chaîne des résultats. Aussi, n'hésitez pas à suggérer une meilleure expression régulière si cela peut vous aider.

$regexA = "\bTEST=\b[A-Za-z]+14\d\d\r"

Je préfère ne pas avoir à coder en dur une liste exhaustive des éléments qui peuvent se trouver entre le '=' et les nombres, comme 'R', 'C', "CSA", etc.

Je travaille sur quelque chose depuis environ une heure où j'obtiens toutes les correspondances pour l'expression régulière, recherche en leur sein pour remplacer 14 par 16, puis exécute replace sur le texte d'origine avec les anciennes et nouvelles valeurs, par ex. replace($myText,"TEST=CSA1400","TEST=CSA1600"), mais cela ne couvre pas très bien les cas spéciaux, et j'ai l'impression de me diriger vers le lapin.

18
SSilk

Vous devez regrouper les sous-expressions que vous souhaitez conserver (c'est-à-dire les mettre entre parenthèses) puis référencer les groupes via les variables $1 et $2 dans la chaîne de remplacement. Essayez quelque chose comme ceci:

$regexA = '( TEST=[A-Za-z]+)14(\d\d)$'

Get-ChildItem '*.txt' | ForEach-Object {
    $c = (Get-Content $_.FullName) -replace $regexA, '${1}16$2' -join "`r`n"
    [IO.File]::WriteAllText($_.FullName, $c)
}
25
Ansgar Wiechers

Voici un exemple utilisant un délégué scriptblock (parfois appelé évaluateur):

$regex = [regex]'( TEST=\D+)14(\d{2})\s*$'
$evaluator = { '{0}16{1}' -f $args[0].Groups[1..2] }
filter set-number { $regex.Replace($_, $evaluator) }

foreach ($file in Get-ChildItem  "*.txt")
 {
   ($file | get-content) | set-number | Set-Content $file.FullName
 }

Il est sans doute plus complexe que l'opérateur -replace, mais vous permet d'utiliser des opérateurs PowerShell pour construire le texte de remplacement, afin que vous puissiez faire tout ce que vous pouvez mettre dans un bloc de script.

2
mjolinor

Essaye ça:

Get-ChildItem  "*.txt" |
Foreach-Object {
  $c = $_ | Get-Content | Foreach {$_ -replace '(?<=TEST=\D+)14(?=\d{2}(\D+|$))','16'}
  $c | Out-File $_.FullName -Enc Ascii
}
1
Keith Hill