Reputation: 18068
I try to move directories with their contents. Names of the directories are letters followed by digits:
a2
,a2321
, sdadsa2321321
, so the regex would be [a-zA-Z]+\d+
. However it doesn't work.
$SourceDirectoryPath = "C:/directory/[a-zA-Z]+\d+"
$TargetFilePath = "C:/directory/target"
New-Item -ItemType "directory" -Path $TargetFilePath
Move-Item -Path $SourceDirectoryPath -Destination $TargetFilePath -Force
If I replace [a-zA-Z]+\d+
with simple wildcards like a*
it moves moves multiples directories, this proves that [a-zA-Z]+\d+
is the only incorrect part of the script.
Question: What is the correct form of the regex [a-zA-Z]+\d+
in Powershell? This regex is fully correct in Java, but for some reason it doesn't work here.
Upvotes: 2
Views: 206
Reputation: 437638
If I replace
[a-zA-Z]+\d+
with simple wildcards likea*
it moves moves multiples directories, this proves that[a-zA-Z]+\d+
is the only incorrect part of the script.
Indeed: The -Path
parameter of file-related cmdlets can only accept wildcard expressions (see about_Wildcards
), not regular expressions (regexes) (see about_Regular_Expressions
).
While distantly related, the two types of expressions are syntactically different: wildcard expressions are conceptually and syntactically simpler, but far less powerful - and not powerful enough for your use case. See AdminOfThings' comment on the question for a quick intro.
Also note that many PowerShell cmdlets conveniently also accept wildcards in other types of arguments (unrelated to the filesystem), such as Get-Command *job*
allowing you to find all available commands whose name contains the word job
.
By contrast, use of regexes always requires a separate, explicit operation (unless a command is explicitly designed to accept regexes as arguments), via operators such as -match
and -replace
, cmdlets such as Select-String
, or the switch
statement with the -Regex
option.
In your case, you need to filter the directories of interest from among all subdirectories, by combining the Where-Object
cmdlet with -match
, the regular-expression matching operator; the syntactically simplest form is to use an operation statement (a cleaner alternative to passing a script block { ... }
in which $_
must be used to refer to the input object at hand), as shown in the following command:
# Define the *parent* path of the dirs. to move.
# The actual dirs. must be filtered by regex below.
$SourceDirectoryParentPath = 'C:/directory'
$TargetFilePath = 'C:/directory/target'
# Note: If you add -Force, no error occurs if the directory already exists.
# New-Item produces output, a System.IO.DirectoryInfo in this case.
# To suppress the output, use: $null = New-Item ...
New-Item -ItemType "directory" -Path $TargetFilePath
# Enumerate the child directories of the parent path,
# and filter by whether each child directory's name matches the regex.
Get-ChildItem -Directory $SourceDirectoryParentPath |
Where-Object Name -match '^[a-z]+\d+$' |
Move-Item -Destination $TargetFilePath -Force
Note that I've changed regex [a-zA-Z]+\d+
to ^[a-z]+\d+$
, because:
PowerShell's regex matching is case-insensitive by default, so [a-z]
covers both upper- and lowercase (English) letters.
The -match
operator performs substring matching, so you need to anchor the regex with ^
(match at the start) and $
match at the end in order to ensure tha the entire input string matches your expression.
Also note that I've used a single-quoted string ('...'
) rather than a double-quoted one ("..."
), which is preferable for regex literals, so that no confusion arises between what characters are seen by the regex engine, and which characters PowerShell itself may interpolate, beforehand, notably $
and `
.
Upvotes: 0
Reputation: 61068
To use regex matching on files and folders with Get-ChildItem
, you will need to use the Where-Object
clause.
This should do it:
$SourceDirectoryPath = 'C:\directory'
$TargetFilePath = 'C:\directory\target'
# create the target path if it does not exist yet
if (!(Test-Path -Path $TargetFilePath -PathType Container)) {
$null = New-Item -Path $TargetFilePath -ItemType Directory
}
Get-ChildItem -Path $SourceDirectoryPath -Directory |
Where-Object { $_.Name -match '^[a-z]+\d+$' } |
ForEach-Object { $_ | Move-Item -Destination $TargetFilePath -Force }
Upvotes: 0
Reputation: 1782
Maybe this is what you want:
$sourceDir = 'D:\source'
$destDir = 'D:\destination'
$pattern = '^.*[a-zA-Z]+\d+$'
$baseDir = Get-ChildItem -Path $sourceDir -Recurse -Directory
foreach( $directory in $baseDir ) {
if( $directory.Name -match $pattern ) {
Move-Item -Path $directory.FullName -Destination $destDir -Force
}
}
Upvotes: 1