4
votes

Extraire la chaîne du fichier texte via Powershell

J'ai essayé d'extraire certaines valeurs de plusieurs lignes dans un fichier .txt avec PowerShell.

$Regex = [Regex]::new("(?<=Equal)(.*)(?=OR")           
$Match = $Regex.Match($String)

Voici ce que je veux:

server01
server02
server03 test

J'ai du code pour l'instant:

Host
Class
INCLUDE vmware:/?filter=Displayname Equal "server01" OR Displayname Equal "server02" OR Displayname Equal "server03 test"


4 Réponses :


4
votes

Vous pouvez utiliser

$newfile = 'file.txt'
$file = 'newtext.txt'
$regex = '(?<=Equal\s*")[^"]+'
Get-Content $file | 
     Select-String $regex -AllMatches | 
     Select-Object -Expand Matches | 
     ForEach-Object { $_.Value } |
     Set-Content $newfile

Voir le démo regex .

Voir plus de façons d'extraire plusieurs correspondances ici . Cependant, votre problème principal est le modèle regex. Le motif (? correspond à:

  • (? - un emplacement précédé de Equal et de plus de 0 espaces blancs, puis d'un " li>
  • [^ "] + - consomme 1+ caractères autres que les guillemets doubles.

Demo:

server01
server02
server03 test

Output:

$String = "Host`nClass`nINCLUDE vmware:/?filter=Displayname Equal ""server01"" OR Displayname Equal ""server02"" OR Displayname Equal ""server03 test"""
[regex]::matches($String, '(?<=Equal\s*")[^"]+') | Foreach {$_.Value}

Voici un extrait complet de la lecture du fichier dans , obtention de toutes les correspondances et enregistrement dans un fichier:

[regex]::matches($String, '(?<=Equal\s*")[^"]+')


4 commentaires

Pour info, si les correspondances peuvent s'étendre sur plusieurs lignes, remplacez Get-Content $ file | par Get-Content $ file | Out-String | ou, si vous utilisez PowerShell v3 ou une version plus récente, Get-Content $ file -Raw |


s'il y a plusieurs INCLUDE vmware: /? filter Que s'est-il passé? J'ai ouvert une nouvelle question. stackoverflow.com/questions/54646160/…


@Arbelac Pouvez-vous expliquer le problème?


@Arbelac Essayez $ regex.matches ($ String.Split ("` r`n "). Where ({$ _. Contains ('"')}) [0]) .groups.where {$ _. name -eq 1} .value | sc "c: \ temp \ result.txt" ou, remplacez la condition de sélection de ligne par une condition plus sûre, .where ({$ _ -match '"[^ "] +" '})



1
votes

Vous pouvez modifier votre regex pour utiliser un groupe de capture, indiqué par les parenthèses. Les contre-obliques échappent simplement aux guillemets. Cela vous permet de capturer simplement ce que vous recherchez, puis de le filtrer davantage. Le groupe de capture ici est automatiquement nommé 1 car je n'ai pas fourni de nom. Le groupe de capture 0 correspond à l'intégralité de la correspondance, citations comprises. Je suis passé à la méthode Matches car elle englobe toutes les correspondances pour la chaîne alors que Match capture uniquement la première correspondance.

$regex = [regex]'\"(.*?)\"'    
$regex.matches($string).groups.where{$_.name -eq 1}.value | sc "c:\temp\export.txt"

Si vous souhaitez exporter les résultats, vous pouvez effectuer les opérations suivantes: p >

$regex = [regex]'\"(.*?)\"'    
$regex.matches($string).groups.where{$_.name -eq 1}.value


1 commentaires

+1 pour une belle généralisation. Quibbles: Pas besoin de \ -escape ". La syntaxe .where {...} fonctionne certainement, mais pour moi la forme la plus verbeuse < code> .where ({...}) est préférable pour des raisons conceptuelles, afin que personne ne soit tenté d'utiliser .where {...} (notez l'espace), qui En passant, l'alias sc pour Set-Content a été, pour le meilleur ou pour le pire, supprimé de PowerShell Core .



1
votes

Une lecture alternative du fichier directement avec Select-String en utilisant le bon RegEx de Wiktor:

> Get-Content .\NewFile.txt
server01
server02
server03 test

Exemple de sortie:

Select-String -Path .\file.txt -Pattern '(?<=Equal\s*")[^"]+' -AllMatches|
    ForEach-Object{$_.Matches.Value} | Set-Content NewFile.txt


0 commentaires

2
votes

Une autre option (PSv3 +), combinant [regex] :: Matches () avec l'opérateur -replace pour une solution concise:

Factor Command
------ -------
1.00   [regex]::Matches($str, '".*?"').Value -replace '"' # this answer
2.85   [regex]::Matches($str, '\"(.*?)\"').Groups.Where({$_.name -eq '1'}).Value # AdminOfThings'
6.07   [regex]::matches($String, '(?<=Equal\s*")[^"]+') | Foreach {$_.Value} # Wiktor's
8.35   $str | Select-String -Pattern '(?<=Equal\s*")[^"]+' -AllMatches | ForEach-Object{$_.Matches.Value} # LotPings'

Regex ". *?" correspond à tous les jetons fermés "..." ; .Value les extrait et -replace '"' supprime les caractères " .

Cela peut ne pas être évident , mais cela se trouve être la solution la plus rapide parmi les réponses ici, sur la base de mes tests - voir en bas.


En passant: ce qui précède serait encore plus idiomatique avec PowerShell si le L'opérateur -match - qui ne recherche que une (une) correspondance - avait une variante nommée, disons, -matchall , afin que l'on puisse écrire: p>

Time-Command -Count 1e3 { [regex]::Matches($str, '".*?"').Value -replace '"' },
   { [regex]::matches($String, '(?<=Equal\s*")[^"]+') | Foreach {$_.Value} },
   { [regex]::Matches($str, '\"(.*?)\"').Groups.Where({$_.name -eq '1'}).Value },
   { $str | Select-String -Pattern '(?<=Equal\s*")[^"]+' -AllMatches | ForEach-Object{$_.Matches.Value} } |
     Format-Table Factor, Command

Voir cette suggestion de fonctionnalité sur GitHub.


Lecture facultative: comparaison des performances

Pragmatiquement parlant, toutes les solutions ici sont utiles et peuvent être assez rapides, mais il peut y avoir des situations où les performances doivent être optimisées.

En règle générale, l'utilisation de Select-String (et du pipeline en général) entraîne une pénalité de performances - tout en offrant élégance et streaming efficace en mémoire

De plus, les appels répétés de blocs de script (par exemple, {$ _. Value} ) ont tendance à être lents - en particulier dans un pipeline avec ForEach-Object ou Where-Object , mais aussi - dans une moindre mesure - avec les méthodes de collecte .ForEach () et .Where () (PSv4 +).

Dans le domaine des regexes, vous payez une pénalité de performance pour les expressions de type look-behind de longueur variable (par exemple (? ) et l'utilisation de groupes de capture (par exemple, (.*?)).

Voici une comparaison des performances à l'aide de la fonction Time-Command , avec une moyenne de 1000 exécutions :

# WISHFUL THINKING (as of PowerShell Core 6.2)
$str -matchall '".*?"' -replace '"'

Exemples de minutages de mon MacBook Pro; les heures exactes ne sont pas importantes (vous pouvez supprimer l'appel Format-Table pour les voir) , mais les performances relatives sont reflétées dans la colonne Facteur , du plus rapide au plus lent.

$str = @'
Host
Class
INCLUDE vmware:/?filter=Displayname Equal "server01" OR Displayname Equal "server02" OR Displayname Equal "server03 test"
'@ 

[regex]::Matches($str, '".*?"').Value -replace '"'


0 commentaires