Delete directories with files containing specific string in PowerShell - powershell

Trying with Windows PowerShell to delete all directories that contain a XML-file with a specific content. Getting as far as listing all the files containing the correct pattern with
ls -r -Filter *.xml | ?{ $_ | Select-String -Pattern "ACC_cont"}
but can´t get the paths from the output and delete the directory and all it´s content. There might be more than one direcotry with a XML-file with the right pattern, and I like to delete them all.

Try the following:
Get-ChildItem -Recurse -Filter *.xml |
Select-String -List -Pattern "ACC_cont" |
Remove-Item -Recurse -LiteralPath { Split-Path -Parent $_.Path } -WhatIf
-WhatIf previews the operation; remove it to perform actual deletion.
Select-String directly accepts file-info objects output by Get-ChildItem from the pipeline.
-List makes Select-String stop after the 1st match in a file, which improves efficiency.
The match-info objects output by Select-String have a .Path property that contains the input file path, so Split-Path -Parent $_.Path gets a matching file's director path.
Passing Split-Path -Parent $_.Path inside a script block ({ ... }) to Remove-Item's -LiteralPath parameter is instance of a delay-bind script block that provides parameter values derived dynamically from each input object, so that Remove-Item -Recurse removes every directory in which a matching XML file was found.

Related

Powershell how to get-content across several subfolders

I'm working on a script to output some data from multiple files based on a string search. It outputs the string found, followed by the following six characters. I can get this to work for an exact location. However, I want to search across files inside multiple subfolders in the path. Using the below script, I get PermissionDenied errors...
[regex] $pattern = '(?<=(a piece of text))(?<chunk>.*)'
Get-Content -Path 'C:\Temp\*' |
ForEach-Object {
if ($_ -match $pattern) {
$smallchunk = $matches.chunk.substring(0, 6)
}
}
"$smallchunk" | Out-File 'C:\Temp\results.txt'
If I change -Path to one of the subfolders, it works fine, but I need it to go inside each subfolder and execute the get-content.
e.g., look inside...
C:\Temp\folder1\*
C:\Temp\folder2\*
C:\Temp\folder3\*
And so on...
Following up on boxdog's suggestion of Select-String, the only limitation would be folder recursion. Unfortunately, Select-String only allows the searching of multiple files in one directory.
So, the way around this is piping the output of Get-ChildItem with a -Recurse switch into Select-String:
$pattern = "(?<=(a piece of text))(?<chunk>.*)"
Get-ChildItem -Path "C:\Temp\" -Exclude "results.txt" -File -Recurse |
Select-String -Pattern $pattern |
ForEach-Object -Process {
$_.Matches[0].Groups['chunk'].Value.Substring(0,6)
} | Out-File -FilePath "C:\Temp\results.txt"
If there's a need for the result to be saved to $smallchunk you can still do so inside the loop if need be.
Abraham Zinala's helpful answer is the best solution to your problem, because letting Select-String search your files' content is faster and more memory-efficient than reading and processing each line with Get-Content.
As for what you tried:
Using the below script I get PermissionDenied errors...
These stem from directories being among the file-system items output by Get-ChildItem, which Get-Content cannot read.
If your files have distinct filename extensions that your directories don't, one option is to pass them to the (rarely used with Get-Content) -Include parameter; e.g.:
Get-Content -Path C:\Temp\* -Include *.txt, *.c
However, as with Select-String, this limits you to a single directory's content, and it doesn't allow you to limit processing to files fundamentally, if extension-based filtering isn't possible.
For recursive listing, you can use Get-ChildItem with -Recurse, as in Abraham's answer, and pipe the file-info objects to Get-Content:
Get-ChildItem -Recurse C:\Temp -Include *.txt, *.c | Get-Content
If you want to simply limit output to files, whatever their name is, use the -File switch (similarly, -Directory limits output to directories):
Get-ChildItem -File -Recurse C:\Temp | Get-Content

Advice with powershell script syntax

I am writing a powershell script to perform the following:
Within a folder Folder > Subfolder1 > Subfolder2 there are 30+ subfolders.zipin which there is another subfolder with 200 HRML files.
I would like to search for a keyword WTSE in the HTML files and any files containing such keyword would be moved to another folder.
My script looks as follows at the moment:
Get-childitem C:\Users\XXXXX\Desktop\Folder\ -filter *.html -recurse | select-string 'WTSE'|foreach-object -process{move-item} C:\Users\XXXXX\Desktop\Folder2`
You're almost there. The problem is with the part after ForEach-Object.
Since you are not searching for a string using regex, I would suggest adding the -SimpleMatch to the Select-String cmdlet.
Try below:
$sourceFolder = 'C:\Users\XXXXX\Desktop\Folder'
$destination = 'C:\Users\XXXXX\Desktop\Folder2'
(Get-ChildItem -Path $sourceFolder -Filter '*.html' -Recurse | Select-String -Pattern 'WTSE' -SimpleMatch) |
Move-Item -Destination $destination
The Move-Item cmdlet can take an array of paths and these can also accepts pipeline input, so there is no need to use ForEach-Object here.
Note I'm using brackets around the first part (Get-ChildItem ... -SimpleMatch). This prevents the error that the process cannot open the file because it is in use

Searching Logs for Filename From List in a Directory w/ Powershell

I'm trying to search text file content in a log directory for matching file names that exist in another directory.
I know I can do a Get-ChildItem $Path -file -name and get a list returned. I also know how to perform a Get-Content ... | Select-String -Pattern
However, I don't know how to feed the file list to the -Pattern.
What I've tried without success:
# Delete all Files in C:\Data\Uploads older than 90 day(s)
$Path = "C:\the_path"
$LogPath = "C:\logs"
Get-Content $LogPath + "\*.log" | Select-String -Pattern (Get-ChildItem $Path -name)
But I know this is just a blind attempt because Get-ChildItem is returning an iterative and not a usable pattern.
How can I do what I'm attempting to do and that is take a list of file names and recursively search for them in a directory of log files? #wishingitwasgrep
Select-String essentially is PowerShell's implementation of grep. Except it can't recurse by itself. That's where Get-ChildItem comes into play.
Get-ChildItem -Path "$LogPath\*.log" -Recurse |
Select-String -Pattern (Get-ChildItem $Path -Name) -SimpleMatch
You can make the statement a little less verbose by using aliases as well as positional instead of named parameters (not recommended for use in scripts, though).
ls "$LogPath\*.log" -r | sls (ls $Path -n) -s
If you want a regular expression match instead of a simple string match remove the -SimpleMatch switch.
You're close, but here's something that should work:
#(Get-Content -Path C:\logs\*.log) |
Where-Object { $_ -in #(Get-ChildItem -Path C:\the_path -Name) }
Now you have a list of files.
How can I do what I'm attempting to do and that is take a list of file names and recursively search for them in a directory of log files?
$List = Get-Content -Path 'C:\LogList.txt'
$LogList = #(Get-ChildItem -Path 'C:\Logs' -Recurse |
Where-Object { $_.Name -in $List })
This assumes your LogList.txt has a newline separated list of log file names with an extension (such as MyLog.txt). $LogList will then have an array of System.IO.FileInfo objects which you can utilize to do whatever you want with these files. For example:
$LogList | Remove-Item

Using Remove-Item cmdlet but excluding sub-directory

I want to remove the following files from the source, however in the source there is a sub-directory that contains files with similar names. When I run the following command it is deleting files in the sub-directory with similar file name. Is there a way to just delete the files from the source and not the sub-directory?
Example: test_1_file, test_2_file, test_3_file exists in each directory, TestFolder and TestFolder/sub
$source = testfolder
remove-item -Path $source -filter test_*_file -recurse -force
It's usually easiest to pipe the output of Get-ChildItem cmdlet into Remove-Item. You then can use the better filtering of Get-ChildItem as I think -Recurse in Remove-Item has some issues. You can even use Where-Object to further filter before passing to Remove-Item
$source = testfolder
Get-ChildItem -Path $source -Filter test_*_file -Recurse |
Where-Object {$_.Fullname -notlike "$source\sub\*"} |
Remove-Item -Force
If the files to delete:
are all located directly in $source
and no other files / directories must be deleted:
Remove-Item -Path $source/test_*_file -Force
No need for -Recurse (as #Bill_Stewart notes).
Note: For conceptual clarity I've appended the wildcard pattern (test_*_file) directly to the $source path.
Using a wildcard expression separately with -Filter is generally faster (probably won't matter here), but it has its quirks and pitfalls.

XCOPY deployment script - how to include certain files?

I need to copy only certain parts of a folder using Powershell, specifically this list:
$files = #("MyProgram.exe",
"MyProgram.exe.config",
"MyProgram.pdb",
".\XmlConfig\*.xml")
In human readable form: 3 specific MyProgram.* files under root of target folder and all XML files under XmlConfig folder which itself is under root of source path (..\bin\Release\ in my case). XmlConfig folder must be created in destination, if it does not exist.
What I have tried:
(1) I tried the following, but it did not work, i.e. no folder or files were created at the destination path:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\" -Include $files
(2) When -Include is removed, whole folder structure is successfully created, including subfolders and files:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\"
It must be something wrong with my understanding of how -Include filter works:
(3) I tested an assumption that -Include needs an array of wildcards, but this did not work either:
$files = #("*MyProgram.exe*",
"*MyProgram.exe.config*",
"*MyProgram.pdb*",
"*.\XmlConfig\*.xml*")
Please advise on how to properly do Copy-Item in my case.
UPDATE (based on below answers):
I am looking for a generic implementation that takes an array of strings. It opens the possibility to put all necessary files/paths in one place, for easy editing, so that a non-Powershell knowledgeable person can understand and modify it as required. So in the end it would be single script to perform XCOPY deployments for any project, with input file being the only variable part. For above example, the input would look like this (saved as input.txt and passed as an argument to the main script):
MyProgram.exe
MyProgram.exe.config
MyProgram.pdb
.\XmlConfig\*.xml
I would prefer wildcards approach, since not many people know regex.
i don't know what is wrong with filter but you can still do
$files | % { copy-item ..\bin\release\$_ -Destination .\test}
if you want to preserve directoty structure you'll have to weak this a little, like :
$sourcedir="c:\temp\test"
$f=#("existing.txt","hf.csv";"..\dir2\*.txt")
$f |%{
$source=ls (join-Path $sourcedir $_) |select -expand directoryname
if ("$source" -like "$sourcedir*"){
$destination=$source.Substring($sourcedir.Length)+".\"
}
else{
$destination=$_
}
copy-item $sourcedir\$_ -Destination $destination -WhatIf
}
AFAICT -Include works only with file names or directory names and not combinations i.e. paths. You can try something like this:
$files = 'MyProgram\.exe|MyProgram\.exe\.config|MyProgram\.pdb|XmlConfig\\.*?\.xml'
Get-ChildItem ..\bin\release -r | Where {!$_.PSIsContainer -and ($_.FullName -match $files)} |
Copy-Item -Dest .\test
With wildcards you could do it this way:
$files = #('*MyProgram.exe','*MyProgram.exe.config','*MyProgram.pdb','*\XmkConfig\*.xml')
Get-ChildItem ..\bin\release -r |
Foreach {$fn=$_.Fullname;$_} |
Where {!$_.PSIsContainer -and ($files | Where {$fn -like $_})} |
Copy-Item -Dest .\test