I am just starting to use Powershell )
I need to compare the file name with a list of file names to exclude some files from further transfer .
This script I wrote :
$E_list= #("**a8e4022a-41c4-4480-b627-02fd6ca03413**","**042d7f01-46da-4f64-b0a8-1fa90e2386d8**")
Foreach($file in(Get-ChildItem $path))
{$sname=$file.Name.SubString(0,36)
if($sname -**notcontains** $E_list)
{write-output $file.name}
}
This is what I get :
042d7f01-46da-4f64-b0a8-1fa90e2386d8.20220807T043155.239-0500.2B2DAA77BB5DBC120EC7.log
042d7f01-46da-4f64-b0a8-1fa90e2386d8.20220807T053153.074-0500.7BBEE35804E5257E9322.log
042d7f01-46da-4f64-b0a8-1fa90e2386d8.20220807T063152.902-0500.CFE11DD033449E5C566C.log
042d7f01-46da-4f64-b0a8-1fa90e2386d8.20220807T073153.266-0500.62C3B67C51A3298D7E41.log
514e18c3-73ec-4203-bd6b-b7088f35f86a.20220807T011525.607-0500.28EC734139367578396D.log
77414eee-054c-4251-b564-d7dc8f18c074.20220807T012552.070-0500.98E353CC0CB50F1901C2.log
77414eee-054c-4251-b564-d7dc8f18c074.20220807T022335.696-0500.570D615179EC19BA5454.log
77414eee-054c-4251-b564-d7dc8f18c074.20220807T023742.213-0500.42759D279D05B3F5476F.log
77414eee-054c-4251-b564-d7dc8f18c074.20220807T042724.518-0500.340A0A2454D6C72CFDEA.log
a8e4022a-41c4-4480-b627-02fd6ca03413.20220807T073747.827-0500.B7411E2EF3038A32BF17.log
a8e4022a-41c4-4480-b627-02fd6ca03413.20220807T074255.366-0500.CE5DB99320C88F4CA300.log
a8e4022a-41c4-4480-b627-02fd6ca03413.20220807T074756.774-0500.7F5A11C5C8B6C9FF5E68.log
a8e4022a-41c4-4480-b627-02fd6ca03413.20220807T075255.354-0500.C460A6E0122A9DD787E6.log
bb57d78f-9bfa-41c0-9a78-f4709a7e02ba.20220807T012609.428-0500.942CD4B421572B911155.log
d80daa0d-124b-4061-86b8-d76e5c02d48a.20220807T011529.679-0500.A13D8D9EE5A5628F4C02.log
d80daa0d-124b-4061-86b8-d76e5c02d48a.20220807T012603.506-0500.4BA46E891A4DDA1D55B3.log
d80daa0d-124b-4061-86b8-d76e5c02d48a.20220807T023744.215-0500.3D5F1792A1FEDA5C2AAB.log
d80daa0d-124b-4061-86b8-d76e5c02d48a.20220807T040642.210-0500.637614FAEF716E613EC7.log
dbe57367-16c9-4115-ad75-0f33449b56e4.20220807T043150.243-0500.FE8A3AF63EDF427D33FC.log
Basically all files in this folder.
I expect files marked bold not to appear in the output.
but somehow they are.
I will appreciate your help.
You can use the -Exclude parameter of Get-ChildItem to greatly simplify and fix the code:
$E_list= 'a8e4022a-41c4-4480-b627-02fd6ca03413*', '042d7f01-46da-4f64-b0a8-1fa90e2386d8*'
Get-ChildItem $path\* -Exclude $E_list | ForEach-Object { $_.Name }
As for what you have tried:
As commenters noted, you need to swap the parameters for -notcontains - i.e. $E_list -notcontains $name. Alternatively, use the -notin operator, i.e. $name -notin $E_list.
The * in the $E_list strings and around -notcontains don't do any good. The -contains and -notcontains operators don't support wildcard matching. For that purpose you'd have to use the -like or -notlike operators. The -Exclude parameter of Get-ChildItem also supports wildcards.
Related
A bit different from the others. I'm retrieving an arraylist of files for processing (basically handling DLL registration on a local machine), and I need my script to properly handle multiple DLLs with the same name. The select -Unique doesn't work, since technically the files aren't duplicates - each has its own unique full path.
I need this script to retrieve all DLLs in a folder (as well as sub-folders), but only return the last instance of each named file. For example if I have files:
C:\Path\Update1\GRM.DLL
C:\Path\Update1\HTCP.DLL
C:\Path\Update2\GRM.DLL
C:\Path\Update3\GRM.DLL
The script should return the objects for Update3\GRM.DLL and Update1\HTCP.DLL.
[System.Collections.ArrayList]$dlls = #(Get-ChildItem -Path $PSScriptRoot -Recurse | Where-Object
{$_.Extension -eq ".dll" -and $_.FullName -notmatch 'dll_Old'})
Edit: Got it going with this, but it's selecting the first instance that shows up, and I need the last. In this example, that means it's snagging Update1/GRM.DLL instead of Update3/GRM.DLL
$dlls = #(Get-ChildItem -Path $PSScriptRoot -Recurse | Where-Object {$_.Extension -eq ".dll" -and $_.FullName -notmatch 'dll_Old'}) | Select-Object -Unique
Use a hashtable to keep track of the last file seen for a specific file name:
$files = #{}
Get-ChildItem -Path $PSScriptRoot -File -Recurse -Filter *.dll |Where-Object FullName -notmatch 'dll_Old' |ForEach-Object {
$files[$_.Name] = $_
}
$uniqueFiles = $files.Values
Mathias R. Jessen's helpful answer is probably the best (fastest) solution in this case, but here's an alternative based on the Group-Object cmdlet:
Get-ChildItem -LiteralPath $PSScriptRoot -Recurse -Filter *.dll |
Where-Object FullName -notmatch dll_Old |
Group-Object Name |
ForEach-Object { $_.Group[-1] }
Group-Object Name groups all matching files by their .Name property.
ForEach-Object { $_.Group[-1] } then extracts the last (-1) member from each resulting group.
Note that Group-Object will implicitly sort the groups by the grouping property, so the resulting list of file-info objects (System.IO.FileInfo, as output by Get-ChildItem) will be sorted by file name.
Alright, been working on this for hours and researching like crazy, but still not getting something to work. I need a string[] object created from get-childitem to pass to the Copy-Item -exclude parameter.
The hurdle is that I need to do recursion and need to have relative paths, so this is what I came up with:
$((Get-ChildItem -Path $Dest -Recurse -File).FullName.TrimStart($Dest))
This results in a clean list of existing files in $dest that are presented with a relative path to $dest. The problem is, if I add this to the copy-item -exclude parameter it seems to ignore it. Further research online suggests that copy-item will ignore the -exclude parameter if it is not of type string[].
If I check the type returned by the above command, I get System.Object[]. I expect it to be System.String[] or just plain String[].
How do I convert the output of the above command to a string array?
The full command using copy-item, for clarity, is:
Copy-Item -Path (Join-Path $src "*") -Destination $dest -Recurse -Force -Exclude $((Get-ChildItem -Path $Dest -Recurse -File).FullName.TrimStart($Dest))
My end goal is to copy files recursively without overwriting existing files.
To get a string[] from the names of get-childitem cmdlet use the following
[string[]]$files = (Get-ChildItem).Name
This will do what it appears you say that you want? But, I think that may not be everything to your question.
$((Get-ChildItem -Path $Dest -Recurse -File).FullName.Replace("$Dest",'.'))
#mklement0 is right, -Exclude and -Include support file name patterns (i.e. "*.txt") and not an array of explicit paths.
This sounds like an awful lot like an XY Problem ;-)
If you simply want to copy files recursively without overwriting them, use Robocopy with the Mirror switch. e.g.:
robocopy C:/Source C:/Dest /mir
EDIT:
By default Copy-Item will always overwrite the files on copy, and there is no switches to get around this. I usually recommend Robocopy as it really simplifies things like this and is very "robust" and reliable.
If your requirements are for a "pure" PowerShell version, then you have to break the scrip out into two parts, 1. Get a list of all the files 2. Iterate through the filer and test to see if they are already in the destination before copying.
$SrcPath = "C:/Source"
$DestPath = "C:/Dest"
$SrcFiles = Get-ChildItem $SrcPath -Recurse
#Iterate through files testing:
$SrcFiles | ForEach-Object {
#Calculate Destination File/Folder name/path
$DestObj = $_.FullName.Replace($SrcPath, $DestPath)
if(Test-Path -LiteralPath $DestObj)
{
#File already Exists
Write-Host "File already Exists: $DestObj"
}
else
{
#File Does not exist - Copy
Write-Host "File Does not Exist Copy: $DestObj"
Copy-Item -Path $_ -Destination $DestObj
}
}
I put together some PowerShell code to search for PDFs where the name matches any of the filenames (in an array of values named $overlayTargetFilenames containing Name.pdf Name2.pdf Name3.pdf) but with an exact match:
$matchedDrawings = Get-ChildItem $basePath -Directory -Force -Exclude 'Undesired 1','Undesired 2' | Get-ChildItem -Directory -Force | Get-ChildItem -Force -Recurse -Include '*.pdf' | where {$_.FullName -like '* A Target Subfolder\*' -and $_.FullName -notmatch "\\OLD" -and $_.Name -iin $overlayTargetFilenames} | sort($_.basename) | ForEach-Object {$_.FullName}
What I'm trying to do is figure out how to do a similar search for PDFs where the array of values has wildcards, such as how to make -iin (which is case insensitive "IN" operator, reverse argument order of -icontains) work like -ilike does for wildcards.
How must I modify the code above to work if the $overlayTargetFilenames had values like Name_* Name-2_* More-Name3_* -- looking for any basenames starting with those different searches? The sought names are generated earlier by the code, and the list size varies wildly.
I've tried -iin and -ilike.
You can do a regex match on values piped into a Foreach-Object. For values that may have special regex characters, you can use the Escape() method from the .NET Regex class. The result will be a string with all the escape characters needed for regex.
$Collection | Foreach-Object {
$regex = [Regex]::Escape($_)
}
No, the -in operator does not support wildcards; it basically enumerates and checks similar to -eq. If you want wildcards, you need to iterate yourself and use -like.
Sorry for the dump question as I am a beginner in PowerShell. We have a lot of coming files into a directory, and they are always increasing and decreasing in that directory because it is moved to another directory after we are done from using them.
We have a priority file list in a txt file which has only partial name of the file name.
For example, for the file name:
2017-06-5---666-12-05-01.txt
In the priority list, I have the partial name as ---666---.
How can I check if the files in the folder are already in the priority list by using Powershell?
I wrote the below script since I need only the files which are older than a given time. But it is not working.
Get-ChildItem -path $Struct.Path -filter $Struct.filter |
Where-Object {$_.CreationTime -lt $time} |
Where-Object {$_.Name -contains "*$PriorityList*"} |
ForEach-Object { $counterP++}
I have updated your code and now it is working perfectly
Get-ChildItem -path $Struct.Path -filter $Struct.filter |
Where-Object {$_.CreationTime -lt $time} | ForEach-Object { If($_.name -
Match $Priorfilter) { $counterP++ }
First of all, the file name in your example doesn't match the partial name. So let's assume that pattern is "666". If you have several patterns, you can join them in one filter:
$PriorityList = "---666---","---555---"
$PriorityFilter = $PriorityList -join '|'
Then use this filter to check Name property:
Get-ChildItem -path $Struct.Path -filter $Struct.filter |
Where-Object {$_.CreationTime -lt $time} |
Where-Object {$_.Name -match $PriorityFilter |
ForEach-Object { $counterP++}
The -contains operator works with a collection as left operand and do not accept wildcards for matching.
ls *.gif | Foreach { $newname = $_.Name -replace '\[','' -replace '\]',''
write-host $_.Name $newname
move-Item -Path $_.Name -Destination $newname; }
ls *.gif
So while trying to help someone rename files with [], I found out move-item doesn't work in a loop. It seems to work just fine outside the loop.
Ideas?
Update: Based on the comment below, I want to clarify this: The special characters in the file names require you to use -LiteralPath parameter. -Path cannot handle those characters. Outside a loop, -Path works since you are escapting the special characters using `. This isn't possible when walking through a collection.
In a loop, you need to use -LiteralPath parameter instead of -Path.
-LiteralPath <string[]>
Specifies the path to the current location of the items. Unlike Path, the value of
LiteralPath is used exactly as it is typed. **No characters are interpreted as
wildcards. If the path includes escape characters, enclose it in single quotation
marks.** Single quotation marks tell Windows PowerShell not to interpret any
characters as escape sequences.
SO, this will be:
GCI -Recurse *.txt | % { Move-Item -LiteralPath $_.FullName -Destination "SomenewName" }
If you use the pipeline binding feature of PowerShell, you can make this much simpler and eliminate the need for the explicit Foreach-Object e.g.:
ls *.gif | Move-Item -Destination {$_ -replace '\[|\]',''} -WhatIf
This works because the LiteralPath parameter is set up to bind ByPropertyName. However you may wonder, where does it get a property by the name of "LiteralPath" from on the output of Get-ChildItem (alias ls). Well it doesn't find that property name, however the LiteralPath parameter has an alias of PSPath defined which does exist on each object output by Get-ChildItem. That's how it binds to the LiteralPath paramter. The other speed tip here is that because the Destination parameter is also pipeline bound (ByPropertyName), you can use a scriptblock to provide the value. And inside that scriptblock you have access to the pipeline object.
Inside the scriptblock, this uses the -replace operator to come up with the new name based on the original full name. While I could have used $_.FullName or even $_.Name in this case (assuming you want to essentially rename the files within the same dir), I use just $_. Since -replace is a string operator, it will coerce $_ to a string before using it. You can see what this would be by executing:
ls *.gif | Foreach {"$_"}
Which is the full path in this case but you have to be careful because you don't always get the full path e.g.:
ls | Foreach {"$_"}
displays just the filename. In your examples (rename to same dir) this doesn't matter but in other cases it does. It is probably a good practice just to be explicit and use $_.Name or $_.FullName in a script but when hacking this stuff out at the console, I tend to use just $_. The saying: it's a sharp stick, don't poke your eye out applies here. :-)
You can find the "official" informations about the role of "[" in Path strings on this Microsoft article.
Or look in google for : Windows PowerShell Tip of the Week : "Taking Things (Like File Paths) Literally".
The only tip wich was not clear for me is that Rename-Item does not support LiteralPath, and that we can use Move-Item to rename files or directories.
JP
This worked for me (atleast in my situation.
Move-Item -literalpath $_.FullName -Destination ( ( ( (Join-Path -Path (Get-Location) -ChildPath $_.BaseName) -replace "\[","`[") -replace "\]","`]") )
Had hundreds of movies and it associated subtitles stored without folders. Decided to put each of the movies and subtitles in their own folders
Full Code
Get-ChildItem -File | %
{
if(Test-Path -LiteralPath ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName ))
{
Move-Item -literalpath $_.FullName -Destination ( ( ( (Join-Path -Path (Get-Location) -ChildPath $_.BaseName) -replace "\[","`[") -replace "\]","`]") )
}
else
{
New-Item ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName ) -ItemType Directory
Move-Item $_ -Destination ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName )
}
}