37

Is there a way (say PowerShell, or a tool) in Windows that can recurse over a directory and convert any Unix files to Windows files.

I'd be perfectly happy with a way in PowerShell to at least detect a Unix file.

It's easy do this for one single file, but I'm after something a bit more scalable (hence leaning towards a PowerShellish solution).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eddie Groves
  • 33,851
  • 14
  • 47
  • 48
  • 1
    http://superuser.com/questions/27060/batch-convert-files-for-encoding-or-line-ending-under-windows/ – Michael Maddox Jul 08 '10 at 11:33
  • For people thinking they need to do this because of a screwed up git repo, you may not. It's possible to fix this issue with git in other ways, like: http://stackoverflow.com/questions/1510798/trying-to-fix-line-endings-with-git-filter-branch-but-having-no-luck/1511273#1511273 – Michael Maddox Jul 09 '10 at 11:34

11 Answers11

47

Here is the pure PowerShell way if you are interested.

Finding files with at least one Unix line ending (PowerShell v1):

dir * -inc *.txt | %{ if (gc $_.FullName -delim "`0" | Select-String "[^`r]`n") {$_} }

Here is how you find and covert Unix line endings to Windows line endings. One important thing to note is that an extra line ending (\r\n) will be added to the end of the file if there isn't already a line ending at the end. If you really don't want that, I'll post an example of how you can avoid it (it is a bit more complex).

Get-ChildItem * -Include *.txt | ForEach-Object {
    ## If contains UNIX line endings, replace with Windows line endings
    if (Get-Content $_.FullName -Delimiter "`0" | Select-String "[^`r]`n")
    {
        $content = Get-Content $_.FullName
        $content | Set-Content $_.FullName
    }
}

The above works because PowerShell will automatically split the contents on \n (dropping \r if they exist) and then add \r\n when it writes each thing (in this case a line) to the file. That is why you always end up with a line ending at the end of the file.

Also, I wrote the above code so that it only modifies files that it needs to. If you don't care about that you can remove the if statement. Oh, make sure that only files get to the ForEach-Object. Other than that, you can do whatever filtering you want at the start of that pipeline.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
  • 1
    By default PowerShell works in "Unicode". I'm no expert on text encoding, but I haven't run into problems with the defaults yet. If you wish, you can explicitly set an encoding for the Get-Content and Set-Content commands with the -Encoding parameter. Get-Help Get-Content -Parameter Encoding – JasonMArcher Apr 08 '09 at 23:31
  • @PeterSeale Set-Content or Out-File have an -Encoding parameter that can be used to set the file encoding type. – Steven Murawski Jun 26 '14 at 14:00
  • 1
    Might also want to add the -recurse flag to search nested folders as well. I.e. Get-ChildItem -Path .\ -recurse | ForEach-Object ... – Mark Feb 19 '21 at 00:21
14

This seems to work for me.

Get-Content Unix.txt | Out-File Dos.txt
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Keith G.
  • 149
  • 1
  • 2
14

There is dos2unix and unix2dos in Cygwin.

Miserable Variable
  • 28,432
  • 15
  • 72
  • 133
  • 2
    I'd recommend this technique as the unix utils will do a better job maintaining the original file encoding (UTF-8, ASCII, etc). I've had problems with PS in the past when I intended to keep ASCII files ASCII. – Peter Seale Apr 08 '09 at 19:56
  • or msys and then you can use the utilities from cmd. – Pod Jun 24 '09 at 12:12
  • 3
    I use powershell to list the files and then pipe it to dos2unix.exe like this: `dir -Recurse -File -Exclude .git | % { dos2unix --u2d --skipbin $_ }` – orad Feb 07 '14 at 03:42
  • @orad if you are already using the unix command dos2unix why not use unix find as well? – Miserable Variable Feb 08 '14 at 09:55
  • Or download Win32 binary [here](https://waterlan.home.xs4all.nl/dos2unix.html#DOS2UNIX) and run it directly in `cmd.exe`/PowerShell! – Franklin Yu Mar 14 '18 at 20:40
  • or when installing git, select the option to add unix commands to path. Or use gitbash. It is all msys2. I would use something official. – TamusJRoyce Mar 18 '22 at 12:35
7

Download Vim, open your file, and issue

:se fileformat=dos|up

Batch for multiple files (all *.txt files in C:\tmp - recursive):

:args C:\tmp\**\*.txt
:argdo se fileformat=dos|up
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
soulmerge
  • 73,842
  • 19
  • 118
  • 155
  • can you do this for a folder or as a batch job? – ninesided Apr 07 '09 at 04:48
  • Or download Eclipse, open the file and convert line delimeters to Unix. vim is no doubt a great tool and I use it every day. But don't you think it is a bit of overkill to use for converting endofline? – Miserable Variable Apr 07 '09 at 13:58
  • 4
    It's just the first thing that came to my mind, it's on every box I own/administer. Btw: are you actually suggesting using eclipse (85MB) and doing it file-by-file instead of using vim (8.5MB) and doing it all at once? – soulmerge Apr 07 '09 at 14:44
2

Converting to Windows text could be as simple as:

(Get-Content file) | Set-Content file

Use the following (with negative lookbehind). Without -nonewline, set-content puts an extra `r`n at the bottom. With the parentheses, you can modify the same file. This should be safe on doing to the same file twice accidentally.

function unix2dos ($infile, $outfile) {
    (Get-Content -raw $infile) -replace "(?<!`r)`n","`r`n" |
    Set-Content -nonewline $outfile
}

The reverse would be this, Windows to Unix text:

function dos2unix ($infile, $outfile) {
    (Get-Content -raw $infile) -replace "`r`n","`n" |
    Set-Content -nonewline $outfile
}

Here's another version for use with huge files that can't fit in memory. But the output file has to be different.

Function Dos2Unix ($infile, $outfile) {
  Get-Content $infile -ReadCount 1000 | % { $_ -replace '$',"`n" } |
  Set-Content -NoNewline $outfile
}

Examples (input and output file can be the same):

dos2unix dos.txt unix.txt
unix2dos unix.txt dos.txt
unix2dos file.txt file.txt

If you have Emacs, you can check it with esc-x hexl-mode. Notepad won't display Unix text correctly; it will all be on the same line. I have to specify the path for set-content, because -replace erases the pspath property.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
js2010
  • 23,033
  • 6
  • 64
  • 66
2

You can use Visual Studio. Menu FileAdvanced Save Options....

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
i_am_jorf
  • 53,608
  • 15
  • 131
  • 222
1

The result of testing the code given in a previous answer in a loop with 10,000 files, many of them more than 50 KB in size:

The bottom line is the PowerShell code is very inefficient/slow/unusable for large files and large number of files. It also does not preserve BOM bytes. I found unix2dos 7.2.3 to be the fastest and most practical solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Arvind
  • 93
  • 8
  • This has to do with the fact that Get-Content is very slow (and inefficient), especially on larger files. – bluuf Jan 04 '18 at 11:47
  • This ought to be converted to a comment (for the appropriate or closest matching answer) - this does not answer the question. – Peter Mortensen Apr 08 '21 at 13:00
1

If Cygwin isn't for you, there are numerous stand-alone executables for unix2dos under Windows if you google around, or you could write one yourself. See my similar (opposite direction for conversion) question here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ninesided
  • 23,085
  • 14
  • 83
  • 107
0

It works for me:

 Get-ChildItem -Recurse -File | % { $tmp = Get-Content $_; $tmp | Out-File "$_" -Encoding UTF8 }
Dmitri
  • 1
  • 2
0

Building on js2010's answer I've created this script:

$excludeFolders = "node_modules|dist|.vs";
$excludeFiles = ".*\.map.*|.*\.zip|.*\.png|.*\.ps1"

Function Dos2Unix {
    [CmdletBinding()]
    Param([Parameter(ValueFromPipeline)] $fileName)

    Write-Host -Nonewline "."

    $fileContents = Get-Content -raw $fileName
    $containsCrLf = $fileContents | %{$_ -match "\r\n"}
    If($containsCrLf -contains $true)
    {
        Write-Host "`r`nCleaing file: $fileName"
        Set-Content -Nonewline -Encoding utf8 $fileName ($fileContents -replace "`r`n","`n")
    }
}

Get-Childitem -File "." -Recurse |
  Where-Object {$_.PSParentPath -notmatch $excludeFolders} |
  Where-Object {$_.PSPath -notmatch $excludeFiles} |
  foreach { $_.PSPath | Dos2Unix }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GeekyMonkey
  • 12,478
  • 6
  • 33
  • 39
-1

Opening a file with Unix line endings in WordPad and saving it will rewrite all the line endings as DOS. It is a bit laborious for large numbers of files, but it works well enough for a few files every once in a while.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jdmichal
  • 10,984
  • 4
  • 43
  • 42