Reputation: 79
I'm trying to use PowerShell to batch copy and rename files.
The original files are named AAA001A.jpg, AAB002A.jpg, AAB003A.jpg, etc.
I'd like to copy the files with new names, by stripping the first four characters from the filenames, and the character before the period, so that the copied files are named 01.jpg, 02.jpg, 03.jpg, etc.
I have experience with Bash scripts on Linux, but I'm stumped on how to do this with PowerShell. After a couple of hours of trial-and-error, this is as close as I've gotten:
Get-ChildItem AAB002A.jpg | foreach { copy-item $_ "$_.name.replace ("AAB","")" }
(it doesn't work)
Upvotes: 2
Views: 6573
Reputation: 440586
Note:
* While perhaps slightly more complex than abelenky's answer, it (a) is more robust in that it ensures that only *.jpg
files that fit the desired pattern are processed, (b) shows some advanced regex techniques, (c) provides background information and explains the problem with the OP's approach.
* This answer uses PSv3+ syntax.
Get-ChildItem *.jpg |
Where-Object Name -match '^.{4}(.+).\.(.+)$' |
Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 } -WhatIf
To keep the command short, the destination directory is not explicitly controlled, so the copies will be placed in the current dir. To ensure placement in the same dir. as the input files, use
Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2)
inside { ... }
.
-WhatIf
previews what files would be copied to; remove it to perform actual copying.
Get-ChildItem *.jpg
outputs all *.jpg
files - whether or not they fit the pattern of files to be renamed.
Where-Object Name -match '^.{4}(.*).\.(.+)$'
then narrows the matches down to those that fit the pattern, using a regex (regular expression):
^...$
anchors the regular expression to ensure that it matches the whole input (^
matches the start of the input, and $
its end)..{4}
matches the first 4
characters (.
), whatever they may be.(.+)
matches any nonempty sequence of characters and, due to being enclosed in (...)
, captures that sequence in a capture group, which is reflected in the automatic $Matches
variable, accessible as $Matches.1
(due to being the first capture group)..
matches the character just before the filename extension.\.
matches a literal .
, due to being escaped with \
- i.e., the start of the extension.(.+)
is the 2nd capture group that captures the filename extension (without the preceding .
literal), accessible as $Matches.2
.Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 }
then renames each input file based on the capture-group values extracted from the input filenames.
Generally, directly piping to a cmdlet, if feasible, is always preferable to piping to the Foreach-Object
cmdlet (whose built-in alias is foreach
), for performance reasons.
In the Copy-Item
command above, the target path is specified via a script-block argument, which is evaluated for each input path with $_
bound to the input file at hand.
Note: The above assumes that the copies should be placed in the current directory, because the script block outputs a mere filename, not a path.
Join-Path
inside the -Destination
script block.Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2)
As for what you've tried:
Inside "..."
(double-quoted strings), you must use $(...)
, the subexpression operator, in order to embed expressions that should be replaced with their value.
Irrespective of that, .replace ("AAB", "")
(a) breaks syntactically due to the space char. before (
(did you confuse the [string]
type's .Replace()
method with PowerShell's -replace
operator?), (b) hard-codes the prefix to remove, (c) is limited to 3 characters, and (d) doesn't remove the character before the period.
The destination-location caveat applies as well: If your expression worked, it would only evaluate to a filename, which would place the resulting file in the current directory rather than the same directory as the input file (though that wouldn't be a problem, if you ran the command from the current dir. or if that is your actual intent).
Upvotes: 2
Reputation: 17492
try this:
Get-ChildItem "C:\temp\Test" -file -filter "*.jpg" | where BaseName -match '.{4,}' |
%{ Copy-Item $_.FullName (Join-Path $_.Directory ("{0}{1}" -f $_.BaseName.Substring(4, $_.BaseName.Length - 5), $_.Extension)) }
Upvotes: -1
Reputation: 64730
Not Powershell, but Batch File:
(since someone wants to be ultra-pedantic about comments)
@echo off
setlocal enabledelayedexpansion
for %%a in (*.jpg) do (
::Save the Extension
set EXT=%%~xa
::Get the Source Filename (no extension)
set SRC_FILE=%%~na
::Chop the first 4 chars
set DST_FILE=!SRC_FILE:~4!
::Chop the last 1 char.
set DST_FILE=!DST_FILE:~,-1!
:: Copy the file
copy !SRC_FILE!!EXT! !DST_FILE!!EXT! )
Upvotes: 1
Reputation: 64730
In Powershell:
(without nasty regexs. We hates the regexs! We does!)
Get-ChildItem *.jpg | Copy-Item -Destination {($_.BaseName.Substring(4) -replace ".$")+$_.Extension} -WhatIf
Details on the expression:
$_.BaseName.Substring(4) :: Chop the first 4 letters of the filename.
-replace ".$" :: Chop the last letter.
+$_.Extension :: Append the Extension
Upvotes: 2