When I try to run:
mv test.txt test2.txt test-files
Returns the error:
Move-Item : A positional parameter cannot be found that accepts argument '.\test-files\'.
At line:1 char:1
mv .\test.txt .\test2.txt .\test-files\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidArgument: (:) [Move-Item], ParameterBindingException
FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.MoveItemCommand
However, if I simply move the files one at a time with
mv test.txt test-files
mv test2.txt test-files
Then it moves the files, but this seems tedious if I had more than two files.
Is there any possible way to move multiple files at once to a specific location?
I've tried mv t*.txt test-files which works, but the files I want to actually move are index.html and style.css
CodePudding user response:
Note: As your error message reveals, mv
is a built-in alias of PowerShell's Move-Item
cmdlet (albeit only on Windows) - see the bottom section for why this is problematic.
Mathias R. Jessen provided the crucial pointer in a comment:
Multiple source paths must be passed as an array to Move-Item
's -Path
or -LiteralPath
parameter, which with directly specified paths requires separating them with ,
:
# Note the "," separating the source file names.
Move-Item -LiteralPath test.txt, test2.txt -Destination test-files
I've used named arguments above, i.e. I've preceded the arguments with the names of their target parameters.
PowerShell also supports positional parameter binding:
Move-Item test.txt, test2.txt test-files
This works, but note that it isn't fully equivalent to the above, because the first positional argument - test.txt, test2.txt
binds to the -Path
parameter, not -LiteralPath
:
- It doesn't make a difference here, because the file names at hand contain no wildcard characters.
- However, if your literal file names happen to contain
[
(e.g.,test[2].txt
), binding to-Path
wouldn't work as expected.
Discovery of whether a cmdlet's parameters support arrays:
You can discover whether a given parameter supports arrays by looking at its syntax diagrams, which are part of the brief help that is displayed when you pass the standard -?
switch; you can also display them in isolation with Get-Command -Syntax Move-Item
, for instance.
Using Get-Help
, you can even ask for details about specific parameters, using -Path
as an example:
PS> Get-Help Move-Item -Parameter Path
-Path <System.String[]>
Specifies the path to the current location of the items. The default is the current directory. Wildcard characters are permitted.
Required? true
Position? 0
Default value Current directory
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? true
<System.String[]>
denotes the parameter's type, which in this case is an array ([]
of System.String
([string]
) instances. In other words: []
following a parameter type implies that arrays are supported.
However, a given cmdlet can choose to also support passing multiple arguments individually to an array-typed parameter (of necessity only one parameter can support this per cmdlet), declared with a [Parameter()]
attribute whose ValueFromRemainingArguments
property is set to $true
:
An example is Write-Output
, whose (one and only positional) -InputObject
parameter is typed System.Management.Automation.PSObject[]
, but also has ValueFromRemainingArguments
set, so that the following two calls are equivalent:
# Single array argument that directly binds to -InputObject
Write-Output 1, 2, 3
# Multiple arguments that implicitly bind collected in an array to -InputObject
Write-Output 1 2 3
Note:
Unfortunately, the help does not reveal the presence of
ValueFromRemainingArguments
- neither in the parameter's detailed view withGet-Help ... -Parameter ...
, nor, typically, in a parameter's description.While programmatic discovery is possible, it is nontrivial:
# -> 'InputObject' (Get-Command Write-Output).Parameters.Values. Where({ $_.Attributes.ValueFromRemainingArguments -contains $true }).Name
Another example is Write-Host
, whose -Object
parameter behaves the same way. However, what is unusual about this parameter is that its type is -Object <System.Object>
, i.e. it doesn't explicitly reveal that it accepts arrays; however, it does; technically, this possible, due to System.Object
being capable of storing any data type, including arrays (collections). It is only the plural in the parameter's description that hints at the fact that -Object
accepts arrays: "Objects to display in the host."
Alias-naming considerations:
That mv
is a built-in alias for Move-Item
(on Windows only)[1] is problematic. More broadly, all aliases named for internal commands of a different shell (e.g., cmd.exe
's dir
) or, as in this case, for external programs (possibly a different platform's) are problematic, because the syntax of these commands / programs usually differs fundamentally from PowerShell's syntax.
- Case in point: the
mv
command in your question would work with the Unix-platform/bin/mv
utility.
It is better to use PowerShell own aliases, whose names are formed methodically from the approved verb part of the command being aliased, with each approved verb having its official abbreviation, e.g., m
for Move-
. The abbreviation for the noun part isn't standardized, but using the initial letter stands to reason, e.g. i
for Item
.
- In PowerShell (Core) 7 , you can discover the verb abbreviation via
Get-Verb
(output columnAliasPrefix
), e.g.,Get-Verb Move
; in Windows PowerShell you'll need to consult the "approved verb" link above.
And, indeed, mi
is another built-in alias for Move-Item
, as the following discovery command based on Get-Alias
reveals:
PS> Get-Alias -Definition Move-Item
CommandType Name Version Source
----------- ---- ------- ------
Alias mi -> Move-Item
Alias move -> Move-Item
Alias mv -> Move-Item # WINDOWS ONLY
The big advantage of such aliases - aside from not pretending to be something they're not - is that their methodic naming makes it easier to remember and possibly even guess their names; e.g.,
gc
forGet-Content
and, asGet-Alias -Definition Get-Alias
reveals,gal
forGet-Alias
.Name conflicts with external programs can still occur, but are less likely. E.g.
sc
forSet-Content
shadows the nativesc.exe
utility (for controlling services), which is the reason it was - somewhat controversially - removed from PowerShell (Core).
[1] On Unix-like platforms, the mv
alias isn't defined, so as not to shadow the external, platform-native /bin/mv
program.