Zip files expand with backslashes on Linux, no subdirectories

It happens because some Windows tools apparently use backslashes (\) as separators where they should use forward slashes (/). Backslash in Unix can be a part of file or directory name.

.ZIP File Format Specification (version: 6.3.5 when I'm writing this, revised November 20, 2018) states: The name of the file, with optional relative path. The path stored MUST NOT contain a drive or device letter, or a leading slash. All slashes MUST be forward slashes / as opposed to backwards slashes \ for compatibility with Amiga and UNIX file systems etc. If input came from standard input, there is no file name field.

This file is mentioned by Microsoft in a document Mitigation: ZipArchiveEntry.FullName Path Separator:

Starting with apps that target the .NET Framework 4.6.1, the path separator used in the ZipArchiveEntry.FullName property has changed from the backslash (\) used in previous versions of the .NET Framework to a forward slash (/). [...]


The change brings the .NET implementation into conformity with section of the .ZIP File Format Specification and allows .ZIP archives to be decompressed on non-Windows systems.

Decompressing a zip file created by an app that targets a previous version of the .NET Framework on non-Windows operating systems such as the Macintosh fails to preserve the directory structure. For example, on the Macintosh, it creates a set of files whose filename concatenates the directory path, along with any backslash (\) characters, and the filename. As a result, the directory structure of decompressed files is not preserved.

Note the problem may exist if the archiver used some old version of .NET Framework or if it didn't use it at all but implemented its own (independent) approach to zip files.

One may experience the same problem with rar: Unrar creating files with backslashes in names instead of proper directory hierarchy.

You may find this question on Unix & Linux SE helpful: Convert a Windows-created ZIP to Linux (internal paths issue). My (somewhat experimental) approach is in this answer.

This is actually a bug in Microsoft.PowerShell.Archive:

...which will be resolved in this PR, slated for version 1.2.3:

In the meantime, this is a quick fix (credit):

for file in *\\*; do target="${file//\\//}"; mkdir -p "${target%/*}"; mv -v "$file" "$target"; done