Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy-Item with "\*" fails if no subdirectory is present #12805

Closed
Herr-Sepp opened this issue May 27, 2020 · 7 comments
Closed

Copy-Item with "\*" fails if no subdirectory is present #12805

Herr-Sepp opened this issue May 27, 2020 · 7 comments
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-No Activity Issue has had no activity for 6 months or more WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc.

Comments

@Herr-Sepp
Copy link

Herr-Sepp commented May 27, 2020

If i want to copy content from a directory without subdirectorys to a NEW folder the Documentations says unter Example 3:

If the Path includes *, all the directory's file contents, without the subdirectory trees, are copied to the new destination directory. For example:

Copy-Item -Path "C:\Logfiles*" -Destination "C:\Drawings\Logs" -Recurse

But this only works if under Path ("C:\Logfiles") are subdirectorys.

Steps to reproduce

C:\Logfiles exists and contains only files
C:\Drawings exists

Copy-Item -Path "C:\Logfiles\*" -Destination "C:\Drawings\Logs" -Recurse

Expected behavior

Directory "Logs" is created under "C:\Drawings" with all Files from "C:\Logfiles"

Actual behavior

Powershell copy one File from Path ("C:\Logfiles") to "C:\Drawings" and name it "Logs" 
All other Files are ignored

If you create inside C:\logfiles a directory everything works as it should.

Environment data

Powershell 7.0.1
@Herr-Sepp Herr-Sepp added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label May 27, 2020
@mklement0
Copy link
Contributor

I agree that the behavior is counter-intuitive.

What happens is that if the -Destination argument is a path that doesn't yet exist, it is interpreted as a file, and it is the last of the input files that ends up in that file.

The workaround is to make sure that the target path exists as a directory beforehand.

You can mitigate the problem somewhat if you append a \ or / to the -Destination path so as to signal that the target path is meant to be a directory, in which case you'll at least get an error if no such directory exists yet.

Although it would technically be a breaking change, it would make sense to default to assuming a container (directory) as the target path in case multiple paths / a wildcard-based path is specified as input, possibly complemented with a new switch such as -CreateOnDemand so as to create the container (directory) on demand.

@Herr-Sepp
Copy link
Author

Thanks for declaration.
But why does it work if Path has subdirectorys?

I don't think a new switch is necessary.
It is documented that a new folder is created if the destination is not available (Copy-Item Doc)

If Copy-Item -Path "C:\Logfiles*" -Destination "C:\Drawings\Logs" worked as expeced, it would match the current documentation.

Also you don`t need the flag "-Recurse" for "Copy all files to a new folder" as described in the note of example 3.
Should I make a new documentation issue for that or wait for further feedback here?

@mklement0
Copy link
Contributor

Also you don't need the flag "-Recurse" for "Copy all files to a new folder" as described in the note of example 3

Yes, please open a documentation issue for that.
The short of it is:

  • C:\Logfiles\* with -Recurse copies all files and all subdirectories and their subtrees.

  • C:\Logfiles\* without -Recurse, copies all files and all subdirectories as empty directories.

If you want to copy files only, you need to use something like Get-ChildItem C:\Logfiles -File | Copy-Item ...

@mklement0
Copy link
Contributor

mklement0 commented Jul 13, 2020

It is documented that a new folder is created if the destination is not available

This applies if the source path is a directory or at least has directories among the items that a wildcard expression resolves to.
If you explicitly pass multiple -Path / -LiteralPath arguments, it is the type of the first item that determines whether a non-existent destination path is considered a file or a directory.

For a single directory and file, respectively, it makes sense to create a directory / a file (after all, Copy-Item file.txt file1.txt should not create a directory named file1.txt and copy file.txt into it).

The current behavior gets messy with multiple source paths, however:

  • All-files source paths make Copy-Item consider a non-existing destination a _ single file_.

    • This means that whatever file happens to be the last among the sources becomes the - only destination file.
  • All-directories source paths make Copy-Item consider a non-existing destination a _ single directory_.

    • The first directory among the source path is copied (without -Recurse, this means little more than creating the destination directory, without contents)
    • All subsequent directories cause errors(!) and their contents are ignored: Copy-Item: Container cannot be copied onto existing leaf item.
  • Mixed-type sources resulting from wildcard expressions make Copy-Item also consider a non-existing destination a single directory.

    • This means that all files among the source are copied to the new destination directory, and, if -Recurse is specified, the contents of the first among the source directories is copied; again, any subsequent directories cause an error.

    • Note: Wildcard resolution results in any directories among the matches being listed first, which causes the interpretation of the destination as a directory; by contrast, if you explicitly pass multiple paths as an array to -Path / -LiteralPath, the first array element's type determines how the destination is interpreted (as stated above); therefore, if you do something like Copy-Item someFile.txt, someSubdir -Destination foo, foo is created as a file (as a copy of someFile.txt) and the subsequent attempt to copy directory someSubDir to file foo then fails.


I don't think a new switch is necessary.

The proposed -Create[DestinationAsContainer]OnDemand switch (name negotiable - I'm struggling to come up with one that is both succinct and descriptive) wasn't meant to address the issues above: it was meant as a convenience switch that allows you to unequivocally specify that the destination path should be a directory, to be created on demand, that the source items should be copied into. For instance, Copy-Item file.txt foo -CreateDestinationContainerOnDemand would allow you to create directory foo on demand rather than creating a file named foo in the absence of a preexisting foo directory. The same would apply to Copy-Item somedir foo -Recurse -CreateDestinationContainerOnDemand, which would copy somedir as a whole into foo/ rather than copying its contents to a not-yet-existing foo directory.

What was meant to address the above issues was the following suggestion:

it would make sense to default to assuming a container (directory) as the target path in case multiple paths / a wildcard-based path is specified as input

To flesh this out a bit:

  • If literally multiple -Path / -LiteralPath source paths are given, always assume that the destination is a directory.

  • If a single -Path argument is given, and that argument is a wildcard expression (e.g., *.txt) rather than a literal (e.g., file.txt), always assume that the destination is a directory.

  • Otherwise, as currently (single file or single directory), assume that the destination is a file or directory, respectively.

Given how the current behavior with multiple source items is both useless and obscure, it's hard to imagine that existing code relies on it, which would make this proposal an acceptable Bucket 3: Unlikely Grey Area change.

@iSazonov iSazonov added WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc. WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module labels Jan 16, 2021
@iSazonov iSazonov added the Area-FileSystem-Provider specific to the FileSystem provider label Nov 29, 2021
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

1 similar comment
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

@microsoft-github-policy-service microsoft-github-policy-service bot added Resolution-No Activity Issue has had no activity for 6 months or more labels Nov 16, 2023
Copy link
Contributor

This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-No Activity Issue has had no activity for 6 months or more WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc.
Projects
None yet
Development

No branches or pull requests

3 participants