使用数组按名称过滤文件
Filter files by name with an Array
我试图在文件夹中查找与数组中的任何条目匹配的所有文件,以便我可以将它们移动到不同的文件以进行组织。我在这里可以使用 PowerShell 2.0;我不确定这是否让我失望,我在学校学习了 PowerShell 4.0。
# Declared variables & array
$files = get-childitem "Y:\Downloads\"
$start = "Y:\Downloads\"
$end = "Y:\Downloads\sorted"
$fileEXT = "*.jokes.*","*.laughter.*","*.comedy.*","*.humour.*"
# Moving Files
#foreach ($file in $files) {
# if ($file.Name -contains $fileEXT[0-9]) {
# Move-Item $start $end
# }
#}
foreach ($element in $fileEXT) {
if ($files.Name -contains $element) {
Move-Item $start $end
}
}
#$start | Where-Object { $fileEXT -contains $_.Name } | Move-Item to $end
试试这个(不要忘记删除 -whatif)
注:
- $_.Base contains file name without extension
- use !$_.PSIsContainer for take only files
代码:
$fileEXT = "test","laughter","comedy","humour"
$start = "Y:\Downloads\"
$end = "Y:\Downloads\sorted"
#solution 1
Get-ChildItem $start | where {$Name=$_.BaseName; !$_.psiscontainer -and ($fileEXT | where {$name -like "*$_*"}).Count -gt 0 } |
move-item -Destination $end -WhatIf
#solution 1b (powershell 3)
Get-ChildItem $start -file | where {$Name=$_.BaseName; ($fileEXT | where {$name -like "*$_*"}).Count -gt 0 } |
move-item -Destination $end -WhatIf
#Solution 2 with regex
$fileEXT = "test|laughter|comedy|humour"
Get-ChildItem $start | where {!$_.psiscontainer -and $_.BaseName -match $fileEXT} |
move-item -Destination $end -WhatIf
#Solution 2b with regex (powershell 3), better solution gived by @TessellatingHeckler
$fileEXT = "test|laughter|comedy|humour"
Get-ChildItem $start -file | where { $_.BaseName -match $fileEXT} |
move-item -Destination $end -WhatIf
Like mentioned, you're using the wrong operator. The -contains
运算符允许您检查数组是否包含特定值(不是值的一部分):
'ab', 'cd', 'ef' -contains 'cd' # returns true
'ab', 'cd', 'ef' -contains 'c' # returns false
您可能将运算符与 String.Contains()
方法混淆了,后者允许您检查字符串是否包含特定子字符串:
'abcdef'.Contains('cd')
虽然都不允许检查字符串是否包含任何子字符串列表。您的方法需要两个循环,因为您需要将每个文件与每个过滤器元素进行比较,并且需要 -like
运算符进行通配符比较。 Break 来自内部循环,因此如果一项与多个过滤器元素匹配,您就不会尝试将其移动两次。
foreach ($file in $files) {
foreach ($element in $fileEXT){
if ($file.Name -like $element){
Move-Item $file.FullName $end
break
}
}
}
不过,使用嵌套循环将两个数组相互比较的效果不会很好。更好的方法是只将搜索词放入数组中并从中构造一个 regular expression,这样您就可以一次性检查所有过滤器元素:
$words = 'jokes', 'laughter', 'comedy', 'humour'
$re = '\.(' + ($words -join '|') + ')\.'
Get-ChildItem 'Y:\Downloads' |
Where-Object { $_.Name -match $re } |
Move-Item -Destination 'Y:\Downloads\sorted'
附带说明一下,虽然您可以使用单个字符串定义正则表达式,但我不建议这样做。除非您打算将其作为 one-off 脚本(甚至可能不会)。
<strike>$re = '\.(jokes|laughter|comedy|humour)\.'</strike> # don't do this
制作$words
一个数组并连接数组以形成实际的正则表达式使维护更容易,并且允许您将单词放在一个单独的文件中(将数据与代码分开):
jokes
laughter
comedy
humour
并让您的脚本读取该文件:
$words = Get-Content 'C:\wordlist.txt'
我试图在文件夹中查找与数组中的任何条目匹配的所有文件,以便我可以将它们移动到不同的文件以进行组织。我在这里可以使用 PowerShell 2.0;我不确定这是否让我失望,我在学校学习了 PowerShell 4.0。
# Declared variables & array
$files = get-childitem "Y:\Downloads\"
$start = "Y:\Downloads\"
$end = "Y:\Downloads\sorted"
$fileEXT = "*.jokes.*","*.laughter.*","*.comedy.*","*.humour.*"
# Moving Files
#foreach ($file in $files) {
# if ($file.Name -contains $fileEXT[0-9]) {
# Move-Item $start $end
# }
#}
foreach ($element in $fileEXT) {
if ($files.Name -contains $element) {
Move-Item $start $end
}
}
#$start | Where-Object { $fileEXT -contains $_.Name } | Move-Item to $end
试试这个(不要忘记删除 -whatif)
注:
- $_.Base contains file name without extension
- use !$_.PSIsContainer for take only files
代码:
$fileEXT = "test","laughter","comedy","humour"
$start = "Y:\Downloads\"
$end = "Y:\Downloads\sorted"
#solution 1
Get-ChildItem $start | where {$Name=$_.BaseName; !$_.psiscontainer -and ($fileEXT | where {$name -like "*$_*"}).Count -gt 0 } |
move-item -Destination $end -WhatIf
#solution 1b (powershell 3)
Get-ChildItem $start -file | where {$Name=$_.BaseName; ($fileEXT | where {$name -like "*$_*"}).Count -gt 0 } |
move-item -Destination $end -WhatIf
#Solution 2 with regex
$fileEXT = "test|laughter|comedy|humour"
Get-ChildItem $start | where {!$_.psiscontainer -and $_.BaseName -match $fileEXT} |
move-item -Destination $end -WhatIf
#Solution 2b with regex (powershell 3), better solution gived by @TessellatingHeckler
$fileEXT = "test|laughter|comedy|humour"
Get-ChildItem $start -file | where { $_.BaseName -match $fileEXT} |
move-item -Destination $end -WhatIf
Like -contains
运算符允许您检查数组是否包含特定值(不是值的一部分):
'ab', 'cd', 'ef' -contains 'cd' # returns true
'ab', 'cd', 'ef' -contains 'c' # returns false
您可能将运算符与 String.Contains()
方法混淆了,后者允许您检查字符串是否包含特定子字符串:
'abcdef'.Contains('cd')
虽然都不允许检查字符串是否包含任何子字符串列表。您的方法需要两个循环,因为您需要将每个文件与每个过滤器元素进行比较,并且需要 -like
运算符进行通配符比较。 Break 来自内部循环,因此如果一项与多个过滤器元素匹配,您就不会尝试将其移动两次。
foreach ($file in $files) {
foreach ($element in $fileEXT){
if ($file.Name -like $element){
Move-Item $file.FullName $end
break
}
}
}
不过,使用嵌套循环将两个数组相互比较的效果不会很好。更好的方法是只将搜索词放入数组中并从中构造一个 regular expression,这样您就可以一次性检查所有过滤器元素:
$words = 'jokes', 'laughter', 'comedy', 'humour'
$re = '\.(' + ($words -join '|') + ')\.'
Get-ChildItem 'Y:\Downloads' |
Where-Object { $_.Name -match $re } |
Move-Item -Destination 'Y:\Downloads\sorted'
附带说明一下,虽然您可以使用单个字符串定义正则表达式,但我不建议这样做。除非您打算将其作为 one-off 脚本(甚至可能不会)。
<strike>$re = '\.(jokes|laughter|comedy|humour)\.'</strike> # don't do this
制作$words
一个数组并连接数组以形成实际的正则表达式使维护更容易,并且允许您将单词放在一个单独的文件中(将数据与代码分开):
jokes laughter comedy humour
并让您的脚本读取该文件:
$words = Get-Content 'C:\wordlist.txt'