Powershell - Search for files from list - Trouble displaying just the match - powershell

I have a script that I use to search for hundreds of files across multiple drives from a list. It works fine as it catches all the matches. The only issue is I need to see the file it matched along with the extension.
A bit of the back story...
We have programs that share the same name as a Copybook. Not too uncommon in the mainframe world. When searching for a file, I have to Wildcard the search in order to catch all the same name files (Minus the extension). I then have to manually search for the hits to determine if they are copybooks or programs.
When I try to add any logic to the script below, it displays the entire array of file names and not just the actual match.
Would anyone be able to assist in capturing and displaying just the matched file along with it's extension? Maybe it's location also?
Regards,
-Ron
#List containing file names must be wilcarded FILE.*
#Parent folder (Where to begin search)
$folder = 'C:\Workspace\src'
#Missing Artifacts Folder (Where Text file resides)
$Dir2 = 'C:\Workspace\Temp'
#Text File Name
$files=Get-Content $Dir2\FilesToSearchFor.txt
cd \
cd $folder
Write-Host "Folder: $folder"
# Get only files and only their names
$folderFiles = (Get-ChildItem -Recurse $folder -File).Name
foreach ($f in $files) {
#if ($folderFiles -contains $f) {
if ($folderFiles -like $f) {
Write-Host "File $f was found." -foregroundcolor green
} else {
Write-Host "File $f was not found!" -foregroundcolor red
}
}

Instead of testing whether the entire list of file names contains the target file name ($folderFiles -like $f), load all files into a hashtable and then test if the target file name exists as a key with ContainsKey():
$fileTable = #{}
Get-ChildItem -Recurse $folder -File |ForEach-Object {
# Create a new key-value entry for the given file name (minus the extension) if it doesn't already exist
if(-not $fileTable.ContainsKey($_.BaseName)){
$fileTable[$_.BaseName] = #()
}
# Add file info object to the hashtable entry
$fileTable[$_.BaseName] += $_
}
foreach($f in $files){
if($fileTable.ContainsKey($f)){
Write-Host "$($fileTable[$f].Count) file(s) matching '$f' were found." -ForegroundColor Green
foreach($file in $fileTable[$f]){
Write-Host "File with extension '$($file.Extension)' found at: '$($file.FullName)'"
}
}
else {
Write-Host "No files found matching '$f'"
}
}
Since the $fileTable contains not just the name, but also a reference to the original file info objects with that names as returned by Get-ChildItem, you can easily access relevant metadata (like the Extension property) now

Related

Trouble linking commands together

So i'm pretty new to powershell and I'm trying to list all contents of a directory(on my vm) while stating if each is a reg file or directory along with it's path/size.
the code I have is:
#!/bin/bash
cd c:\
foreach ($item in get-childitem -Path c:\) {
Write-Host $item
}
########
if(Test-Path $item){
Write-Host "Regular File" $item
}
else {
Write-Host "Directory" $item
}
I can get all of the contents to print, but when I try to state whether file/directory, only one .txt file says "Regular File" next to it. I've been at it for hours on end and get figure it out. Also, my output doesn't state "directory" next to directories...
Here is an example of how you can enumerate the files and folders on your C Drive one level deep with their current size (if it's a folder, look for the files inside and get a sum of it's Length). Regarding trying to "state whether file / directory", you don't need to apply any logic to it, FileInfo and DirectoryInfo have an Attributes property which gives you this information already.
Get-ChildItem -Path C:\ | & {
process {
$object = [ordered]#{
Attributes = $_.Attributes
Path = $_.Name # change to $_.FullName for the Path
Length = $_.Length / 1mb
}
if($_ -is [IO.DirectoryInfo]) {
foreach($file in $_.EnumerateFiles()) {
$object['Length'] += $file.Length / 1mb
}
}
$object['Length'] = [math]::Round($object['Length'], 2).ToString() + ' Mb'
[pscustomobject] $object
}
}
If you want something more complex, i.e. seeing the hierarchy of a directory, like tree does, with the corresponding sizes you can check out this module.

Powershell - Find a specific file by name recursively in folder\folders

doing some scripting fun with looking for specific file by input.
i want to search specific file by name for example: "abc*" with the prefix either it will be abc.txt, or abc.doc, or what ever...
search recursively in folders, and print if it exists or not.
here is the script i've got so far:
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
Function Get-ItemName {
$Global:ItemName = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a Filename', 'File Name')
}
Get-ItemName
$itemList = Get-ChildItem "\\path_of_folder_with_many_files" -Recurse
$checkItems = 'true'
foreach ($item in $itemList) {
if ((Test-Path -LiteralPath $item) -eq $true -and (Name -like $ItemName)) {
Write-Host "$item - Found" -ForegroundColor Greed
}
elseif ((Test-Path -LiteralPath $item) -eq $false) {
Write-Host "$item - Not Found" -ForegroundColor Red
$checkItems = 'false'
}
}
if ($checkItems -eq $false){
echo ""
Write-Host "Some files was not found, Build Failed" -ForegroundColor Red
exit 1
}
but all it does is printing list of the files and " - Not Found".
and i do have files named "abc.txt" and "abc.doc" and many many more files.
oh yeah and the reason the "find files" script is so long, its because i want to use this powershell script later on a build, and crush it if files doesnt exists.
any ideas whats wrong here?
Either have your function return what was entered in the box and use that or remove that function completely as I have done below:
Add-Type -AssemblyName Microsoft.VisualBasic
$itemName = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a Filename', 'File Name')
if (![string]::IsNullOrWhiteSpace($itemName)) {
# only search for files if there was anything entered in the box
$itemList = #(Get-ChildItem "\\path_of_folder_with_many_files" -Filter "*$itemName*" -File -Recurse)
# now do something with the array of FileInfo objects
# for demo just show how many files were found using the $itemName
Write-Host "Found $($itemList.Count) files with search name '$itemName'"
}
else {
write-host "Nothing was entered in the inputbox.."
}
As you can see, you don't need to use Test-Path, because all files returned by Get-ChildItem of course do exist.
Parameter -Filter here is used with wildcards to search for files with $itemName in their name
Adding switch -File makes sure only files are searched, not folders aswell
Surrounding Get-ChildItem with #() forces the result to be an array, so you can work with the .Count property

Powershell - Referencing part of file name string

I am trying to extract the first part of a file name, then move to a corresponding folder with powershell. The filename looks something like "X02348957 RANDOM WORDS HERE" - I'm trying to get powershell to only look at the X02348957. The X#'s are also different lengths, so I can't just do it based on a location variable (like read everything 8 spaces to the left - the number won't always be eight spaces).
Here is my code in progress so far;
# Retrieve list of files
# $sDir = Source Directory
$sDir = "U:\Test\"
# Generate a list of all files in source directory
$Files = (Get-ChildItem $sDir)
# $tDir = Root Target Directory
$tDir = "N:\USERS\Username\General\Test\"
# Loop through our list of file names
foreach($File in $Files)
{
# $wFile is the working file name
$wFile = $File.BaseName
# Here is where the code to make it only move based on the first part of file name would go (the X#)?
# dFold = the destination folder in the format of \\drive\folder\SubFolder\
$dFold = "$tDir$wFile"
# Test if the destination folder exists
if(Test-Path $dFold -PathType Container)
{
# If the folder exists then move the file
Copy-Item -Path $sDir$File -Destination $dFold
# Denote what file was moved to where
Write-Host $File "Was Moved to:" $dFold
Write-Host
}
# If the folder does not exist, leave file alone
else
{
# Denote if a file was not moved
Write-Host $File "Was not moved!"
}
}
If I understand the issue, then:
$firstPart = $wFile.Split(' ')[0]
If you feel the need to use a regex:
$wFile -match '^(?<FirstPart>x\d+)'
$firstPart = $matches.FirstPart

How to alter WinSCP script for recursively searching for text to print just a folder of the first match and exit

I want to be able to look for a text string in all folders on a remote FTP server. Once found, I need to know the name of the folder which contains the text document BUT not the name of the file.
How can I make changes to "Action on match" to make this script (WinSCP Extension Search recursively for text in remote directory / Grep files over SFTP/FTP protocol) run silently (without displaying anything in the terminal) and once the match is found simply stop and display ONLY the name of the folder (containing the file with the text string)? Also is it possible to display the result in red? Here is the "Action on match" portion of the script (I tried to include the whole thing but cannot do it for some reason).
I can do this with the help of the latest WinSCP custom Search for Text button (please see .ps1 script below which makes this search function possible). However instead of just stopping upon match, the search goes on until the last folder. What is worse is that in order to find the result I need to scroll all the way back up and check every entry. Only one of them will have the name of my folder listed so that is a long procedure.
{
# Action on match
# Modify the code below if you want to do another task with
# matching files, instead of grepping their contents
Write-Host ("File {0} matches mask, searching contents..." -f $fileInfo.FullName)
$tempPath = (Join-Path $env:temp $fileInfo.Name)
# Download file to temporary directory
$sourcePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName)
$transferResult = $session.GetFiles($sourcePath, $tempPath)
# Did the download succeeded?
if (!$transferResult.IsSuccess)
{
# Print error (but continue with other files)
Write-Host $transferResult.Failures[0].Message
}
else
{
# Search and print lines containing "text".
# Use -Pattern instead of -SimpleMatch for regex search
$matchInfo = Select-String -Path $tempPath -SimpleMatch $text
# Print the results
foreach ($match in $matchInfo)
{
Write-Host ($fileInfo.FullName + ":" + $match.LineNumber + ":" + $match.Line)
}
# Delete temporary local copy
Remove-Item $tempPath
}
}
{
# Action on match
# Modify the code below if you want to do another task with
# matching files, instead of grepping their contents
$tempPath = (Join-Path $env:temp $fileInfo.Name)
# Download file to temporary directory
$sourcePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName)
$transferResult = $session.GetFiles($sourcePath, $tempPath)
# Did the download succeeded?
if (!$transferResult.IsSuccess)
{
# Print error (but continue with other files)
Write-Host $transferResult.Failures[0].Message
}
else
{
# Use -Pattern instead of -SimpleMatch for regex search
$matchInfo = Select-String -Path $tempPath -SimpleMatch $text
Remove-Item $tempPath
if ($matchInfo)
{
# Print a path to the file's folder and exit
Write-Host ($fileInfo.FullName -replace "/[^/]*$")
exit
}
}
}

Script to compare existence of files from one folder to another where archive flag is true (file attributes O)

I am constructing a powershell script to query the existence of archived files within the folder that the files are archived to.
EMC's disk extender moves the file from the live storage to archive storage and leaves a stub/link to the file.
You can still list the archive stub in the directory tree and the file still appears to be the same size but in reality it is an archive stub probably 1k in size.
The file attribute(s) of an archive stub give the value O
I need to query the files in the live folder where attribute of the file = O
then compare the list to my archive directory on another server outputting the files that do not exist on my archive where an archive stub exists on the live storage.
basically I need to know how to query the file attribute to retrieve O and do a compare.
This function (taken from here) will return either true or false, depending on your O attribute
function Get-FileAttribute{
param($file,$attribute)
$val = [System.IO.FileAttributes]$attribute;
if((gci $file -force).Attributes -band $val -eq $val){$true;} else { $false; }
}
Get-FileAttribute "C:\PST\T.dll" "Offline"
You can use something like this:
function Get-FileAttribute{
param($file,$attribute)
$val = [System.IO.FileAttributes]$attribute;
if((gci $file -force).Attributes -band $val -eq $val){$true;} else { $false; }
}
# Get items both from folder and from archive
$folder = "C:\pst"
$archive = "C:\pst"
$itemsFromFolder = get-childitem $folder -recurse
$itemsFromArchive = get-childitem $archive -recurse
# Filter items from folder to have only offline files
$offlineItemsFromFolder = New-Object 'System.Collections.Generic.List[System.IO.FileInfo]'
foreach($item in $itemsFromFolder)
{
$isOffline = Get-FileAttribute $item.FullName "Offline"
if ($isOffline)
{
$offlineItemsFromFolder.Add($item)
}
}
# Compare each offline item from folder with item from archive
foreach($item in $offlineItemsFromFolder)
{
$result = $itemsFromArchive | Where-Object {$_.FullName -eq $item.FullName.Replace($folder, $archive)}
# Do some processing depending on $result being $true or $false
}