Pass the files as full paths to the Zip
function, using their .FullName
property (PSv3+ syntax):
Zip C:\Users\Admin\Desktop\TEST.zip $Files.FullName
The problem is that, in Windows PowerShell, the [System.IO.FileInfo]
instances returned by Get-ChildItem
situationally[1] stringify to their file names only, which is what happened in your case, so your Zip
function then interpreted the $toBeZipped
values as relative to the current location, which is C:\Program Files\7-Zip
at that point.
That said, it's better not to use Set-Location
in your function altogether, so that in the event that you do want to pass actual relative paths, they are correctly interpreted as relative to the current location:
Function Zip {
Param
(
[Parameter(Mandatory)] # make sure a value is passed
[string]$zipFile
,
[Parameter(Mandatory)] # make sure a value is passed
[string[]]$toBeZipped
)
# Don't change the location, use & to invoke 7z by its full path.
$null = & "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped
# You may want to add error handling here.
}
[1] When Get-ChildItem
output stringifies to file names only:
Note:
If Get-ChildItem
output is to be passed to other file-processing cmdlets, say Rename-Item
, the problem can be bypassed by providing input to them via the pipeline, which implicitly binds to the target cmdlet's -LiteralPath
parameter by full path - see this answer for more information.
The related Get-Item
cmdlet output always stringifies to the full path, fortunately.
In PowerShell (Core) v6.1+, Get-ChildItem
too always stringifies to the full path, fortunately.
The following therefore only applies to Get-ChildItem
in Windows PowerShell:
The problem is twofold:
Even PowerShell's built-in cmdlets bind file / directory arguments (parameter values - as opposed to input via the pipeline) not as objects, but as strings (changing this behavior is being discussed in GitHub issue #6057).
Therefore, for robust argument-passing, you need to ensure that your Get-ChildItem
output consistently stringifies to full paths, which Get-ChildItem
does not guarantee - and it's easy to forget when name-only stringification occurs or even that you need to pay attention to it at all.
Always passing the .FullName
property values instead is the simplest workaround or, for reliable operation with any PowerShell provider, not just the filesystem, .PSPath
.
[System.IO.FileInfo]
and [System.IO.DirectoryInfo]
instances output by a Get-ChildItem
command stringify to their file names only, if and only if:
If one or more literal directory paths are passed to -LiteralPath
or -Path
(possibly as the 1st positional argument) or no path at all is passed (target the current location); that is, if the contents of directories are enumerated.
and does not also use the -Include
/ -Exclude
parameters (whether -Filter
is used makes no difference).
By contrast, whether or not the following are also present makes no difference:
-Filter
(optionally as the 2nd positional argument, but note that specifying a wildcard expression such as *.txt
as the 1st (and possibly only) positional argument binds to the -Path
parameter)
-Recurse
(by itself, but note that it is often combined with -Include
/ -Exclude
)
Example commands:
# NAME-ONLY stringification:
Get-ChildItem | % ToString # no target path
Get-ChildItem . | % ToString # path is literal dir.
Get-ChildItem . *.txt | % ToString # path is literal dir., combined with -Filter
# FULL PATH stringification:
Get-ChildItem foo* | % ToString # non-literal path (wildcard)
Get-ChildItem -Recurse -Include *.txt | % ToString # use of -Include
Get-ChildItem file.txt | % ToString # *file* path
Out-Null
to help get yourself more information as to what is happening. – FlowGet-ChildItem
call, (very) unfortunately - I've tried to summarize the rules in my answer. – Alben