web-dev-qa-db-fra.com

Remplacer CRLF à l'aide de PowerShell

NDLR: A en juger par les commentaires ultérieurs de l'OP, l'essentiel de cette question est: Comment pouvez-vous convertir un fichier avec des fins de ligne CRLF (style Windows) en LF uniquement ( Fichier de type Unix) dans PowerShell?

Voici mon script PowerShell:

 $original_file ='C:\Users\abc\Desktop\File\abc.txt'
 (Get-Content $original_file) | Foreach-Object {
 $_ -replace "'", "2"`
-replace '2', '3'`
-replace '1', '7'`
-replace '9', ''`
-replace "`r`n",'`n'
} | Set-Content "C:\Users\abc\Desktop\File\abc.txt" -Force

Avec ce code, je peux remplacer 2 par 3, 1 par 7 et 9 par une chaîne vide. Je ne parviens pas à remplacer le saut de ligne retour chariot par le saut de ligne uniquement. Mais cela ne fonctionne pas.

26
Angel_Boy

Vous n'avez pas spécifié la version, je suppose que vous utilisez Powershell v3.

Essaye ça:

$path = "C:\Users\abc\Desktop\File\abc.txt"
(Get-Content $path -Raw).Replace("`r`n","`n") | Set-Content $path -Force

NDLR: Comme le souligne mike z dans les commentaires, Set-Content Ajoute un CRLF de fin, ce qui n'est pas souhaité. Vérifiez avec: 'hi' > t.txt; (Get-Content -Raw t.txt).Replace("`r`n","`n") | Set-Content t.txt; (Get-Content -Raw t.txt).EndsWith("`r`n"), ce qui donne $True.

Notez que cela charge tout le fichier en mémoire, vous pouvez donc souhaiter une solution différente si vous souhaitez traiter des fichiers volumineux.

MISE À JOUR

Cela pourrait fonctionner pour la v2 (désolé nulle part pour tester):

$in = "C:\Users\abc\Desktop\File\abc.txt"
$out = "C:\Users\abc\Desktop\File\abc-out.txt"
(Get-Content $in) -join "`n" > $out

NDLR: Notez que cette solution (maintenant) écrit dans un fichier différent et n'est donc pas équivalente au (toujours imparfait) ) v3 solution. (Un fichier différent est ciblé pour éviter l'écueil qu'Ansgar Wiechers souligne dans les commentaires: en utilisant > tronque le fichier cible avant l'exécution commence). Plus important encore, cependant: cette solution aussi ajoute un CRLF de fin, ce qui n'est pas souhaité. Vérifiez avec 'hi' > t.txt; (Get-Content t.txt) -join "`n" > t.NEW.txt; [io.file]::ReadAllText((Convert-Path t.NEW.txt)).endswith("`r`n"), ce qui donne $True.

Même réserve quant au chargement en mémoire.

26
Andrew Savinykh

Il s'agit d'une réponse à l'état de l'union à partir de Windows PowerShell v5.1/PowerShell Core v6.2.0 :

  • La réponse malheureuse d'Andrew Savinykh , bien qu'elle soit acceptée, est, au moment de la rédaction de cet article, fondamentalement défectueuse j'espère qu'il sera corrigé - il y a suffisamment d'informations dans les commentaires - et dans l'historique des modifications - pour le faire).

  • Réponse utile d'Ansgar Wiecher fonctionne bien , mais nécessite une utilisation directe du .NET Framework (et lit l'intégralité du fichier en mémoire, bien que cela puisse être modifié). L'utilisation directe du .NET Framework n'est pas un problème en soi, mais est plus difficile à maîtriser pour les novices et difficile à retenir en général.

  • Une version future de PowerShell Core aura un
    Convert-TextFile applet de commande avec un -LineEnding paramètre pour permettre la mise à jour sur place des fichiers texte avec un style de nouvelle ligne spécifique, comme en cours de discussion sur GitHub .

Dans PSv5 + , des solutions natives PowerShell sont désormais possibles , car Set-Content prend désormais en charge -NoNewline switch, qui empêche l'ajout indésirable d'un saut de ligne natif de la plate-forme[1] :

# Convert CRLFs to LFs only.
# Note:
#  * (...) around Get-Content ensures that $file is read *in full*
#    up front, so that it is possible to write back the transformed content
#    to the same file.
#  * + "`n" ensures that the file has a *trailing LF*, which Unix platforms
#     expect.
((Get-Content $file) -join "`n") + "`n" | Set-Content -NoNewline $file

Ce qui précède repose sur Get-Content la capacité de lire un fichier texte qui utilise toute combinaison de CR uniquement, CRLF et LF uniquement nouvelles lignes ligne par ligne.

Mises en garde :

  • Vous devez spécifier l'encodage de sortie à correspondre au fichier d'entrée dans l'ordre pour le recréer avec le même encodage. La commande ci-dessus ne spécifie PAS un codage de sortie; pour ce faire, utilisez -Encoding; sans-Encoding:

    • Dans Windows PowerShell , vous obtiendrez "ANSI" codage, le codage hérité à 8 octets de votre système, tel que Windows-1252 sur les systèmes anglais américain.
    • Dans PowerShell Core, vous obtiendrez le codage UTF-8 sans une nomenclature .
  • Le contenu du fichier d'entrée ainsi que sa copie transformée doivent tenir dans la mémoire dans son ensemble, ce qui peut être problématique avec gros fichiers d'entrée.

  • Il existe un risque de corruption de fichiers , si le processus de réécriture dans le fichier d'entrée est interrompu.


[1] En fait, s'il y a plusieurs chaînes à écrire, -NoNewline ne place pas non plus de saut de ligne entre eux; dans le cas présent, cependant, cela n'est pas pertinent, car seule la chaîne un est écrite.

21
mklement0

Solution alternative qui n'ajoutera pas un faux CR-LF:

$original_file ='C:\Users\abc\Desktop\File\abc.txt'
$text = [IO.File]::ReadAllText($original_file) -replace "`r`n", "`n"
[IO.File]::WriteAllText($original_file, $text)
15
Ansgar Wiechers

Ajout d'une autre version basée sur l'exemple ci-dessus par @ ricky89 et @ mklement0 avec quelques améliorations:

Script à traiter:

  • * Fichiers .txt dans le dossier actuel
  • remplacer LF par CRLF (terminaisons de ligne Unix vers Windows)
  • enregistrer les fichiers résultants dans le sous-dossier CR-to-CRLF
  • testé sur des fichiers de 100 Mo et plus, PS v5;

LF-à-CRLF.ps1:

# get current dir
$currentDirectory = Split-Path $MyInvocation.MyCommand.Path -Parent

# create subdir CR-to-CRLF for new files
$outDir = $(Join-Path $currentDirectory "CR-to-CRLF")
New-Item -ItemType Directory -Force -Path $outDir | Out-Null

# get all .txt files
Get-ChildItem $currentDirectory -Force | Where-Object {$_.extension -eq ".txt"} | ForEach-Object {
  $file = New-Object System.IO.StreamReader -Arg $_.FullName
  # Resulting file will be in CR-to-CRLF subdir
  $outstream = [System.IO.StreamWriter] $(Join-Path  $outDir $($_.BaseName + $_.Extension))
  $count = 0 
  # read line by line, replace CR with CRLF in each by saving it with $outstream.WriteLine
  while ($line = $file.ReadLine()) {
        $count += 1
        $outstream.WriteLine($line)
    }
  $file.close()
  $outstream.close()
  Write-Host ("$_`: " + $count + ' lines processed.')
}
1
Rod

Les éléments suivants pourront traiter rapidement de très gros fichiers.

$file = New-Object System.IO.StreamReader -Arg "file1.txt"
$outstream = [System.IO.StreamWriter] "file2.txt"
$count = 0 

while ($line = $file.ReadLine()) {
      $count += 1
      $s = $line -replace "`n", "`r`n"
      $outstream.WriteLine($s)
  }

$file.close()
$outstream.close()

Write-Host ([string] $count + ' lines have been processed.')
0
ricky89