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
Related
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.
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
I have a PowerShell 5.1 script that should delete some files. However, when I try to delete them, neither del nor remove-item appear to be working. I have alredy set the execution policy to Unrestricted, and launched the ISE in admin mode. Even moving the file with Move-Item didn't work.
My code:
$startROMFolderPath = "C:\Users\VincentGuttmann\Desktop\retropie-mount\roms" #Pleae enter the path to your current ROM folder here, ending in the roms folder. Note that this script will only work if the ROMs are inside the RetroPie folder.
$deleteROMFolder = $startROMFolderPath + "\delete"
Write-Host #"
Hello!
Welcome to my ROM Sorting program!
There are three main ROM types:
The ROMs that are "known good", which are marked with this tag: [!],
the bad dumps, so "known bad", which are marked [b].
Then there are the ROMs where we don't know.
The ROMs that are known good will be left in the folder.
The bad dumps will be deleted right away, as well as the ROMs which are neither German nor English
If you haven't put in the paths to your current ROM folder,
please exit the program now by pressing enter and then "n" without quotation marks.
"#
$inputted = Read-Host -Prompt 'Have you changed the paths accordingly? Yes: (y), No: anything else than (y)'
$i = 0
if($inputted -eq "y")
{
$foldersOld = #()
Get-ChildItem $startROMFolderPath -Attributes Directory |
foreach {
$foldersOld += $_
}
foreach ($name in $foldersOld) {
$currentROMFolderPath = $StartROMFolderPath + "\" + $name
$ROMList = #()
Get-ChildItem $currentROMFolderPath -File |
foreach {
$currentROMPath = $currentROMFolderPath + "\" + $_
$containsBad = $_ -match "\[b.\]"
$containsGood = $_ -match "\[!\]"
$containsLang = $_ -match ".*(\([UE]+\))|(\(U\).*\(E.*\))|(\(E.*\).*\(U\))|(\(E.*\))|(\([UKNG4A]+\))|(\(Unl\))"
if($containsBad)
{
write-host "====================================="
Write-Host $_
Write-Host "contains bad"
write-host "====================================="
Remove-Item -Path $currentROMPath -Force
}
#Put "!$containsGood -and !$containsLang" into the next bracket if you want to delete all ROMS that are neither good nor in the correct Language.
#For just keeping ROMs in the corect language, plase use "!$containsLang"
elseif(!$containsGood -and !$containsLang)
{
write-host "====================================="
Write-Host $_
Write-Host "Contains no good and no lang"
write-host $currentROMPath
write-host "====================================="
Remove-Item -Path $currentROMPath -Force
}
}
}
}
else
{
exit
}
exit
1. Code Description alias how it is intended to work
User enters a path to a directory in PowerShell. Code checks if any folder within the declared directory contains no data at all. If so, the path of any empty folder will be shown on the prompt to the user and eventually removed from the system.
2. The Issue alias what I am struggling with
The code I just wrote doesn't count the depth of a folder hierarchy as I would expect (the column in the output table is blank). Besides that, the program works okay - I've still got to fix the issue where my code removes empty parent directories first and child directories later, which of course will cause an error in PowerShell; for instance, take
C:\Users\JohnMiller\Desktop\Homework
where Homework consists of Homework\Math\School Project and Homework\Computer Science\PowerShell Code. Note that all directories are supposed to be empty with the exception of PowerShell Code, the folder containing this script. (Side note: A folder is considered empty when no file dwells inside. At least that's what my code is based on for now.)
3. The Code
# Delete all empty (sub)folders in [$path]
[Console]::WriteLine("`n>> Start script for deleting all empty (sub)folders.")
$path = Read-Host -prompt ">> Specify a path"
if (test-path $path)
{
$allFolders = Get-ChildItem $path -recurse | Where {$_.PSisContainer -eq $True}
$allEmptyFolders = $allFolders | Where-Object {$_.GetFiles().Count -eq 0}
$allEmptyFolders | Select-Object FullName,#{Name = "FolderDepth"; Expression = {$_.DirectoryName.Split('\').Count}} | Sort-Object -descending FolderDepth,FullName
[Console]::WriteLine("`n>> Do you want do remove all these directories? Validate with [True] or [False].") #'#
$answer = Read-Host -prompt ">> Answer"
if ([System.Convert]::ToBoolean($answer) -eq $True)
{
$allEmptyFolders | Remove-Item -force -recurse
}
else
{
[Console]::WriteLine(">> Termination confirmed.`n")
exit
}
}
else
{
[Console]::WriteLine(">> ERROR: [$($path)] is an invalid directory. Program terminates.`n")
exit
}
The depth-count problem:
Your code references a .DirectoryName property in the calculated property passed to Select-Object, but the [System.IO.DirectoryInfo] instances output by Get-ChildItem have no such property. Use the .FullName property instead:
$allEmptyFolders |
Select-Object FullName,#{Name='FolderDepth'; Expression={$_.FullName.Split('\').Count}} |
Sort-Object -descending FolderDepth,FullName
Eliminating nested empty subfolders:
To recap your problem with a simple example:
If c:\foo is empty (no files) but has empty subdir. c:\foo\bar, your code outputs them both, and if you then delete c:\foo first, deleting c:\foo\bar next fails (because deleting c:\foo also removed c:\foo\bar).
If you eliminate all nested empty subdirs. up front, you not only declutter what you present to the user, but you can then safely iterative of the output and delete one by one.
With your approach you'd need a 2nd step to eliminate the nested empty dirs., but here's a depth-first recursive function that omits nested empty folders. To make it behave the same way as your code with respect to hidden files, pass -Force.
function Get-RecursivelyEmptyDirectories {
[cmdletbinding()]
param(
[string] $LiteralPath = '.',
[switch] $Force,
[switch] $DoNotValidatePath
)
$ErrorActionPreference = 'Stop'
if (-not $DoNotValidatePath) {
$dir = Get-Item -LiteralPath $LiteralPath
if (-not $dir.PSIsContainer) { Throw "Not a directory path: $LiteralPath" }
$LiteralPath = $dir.FullName
}
$haveFiles = [bool] (Get-ChildItem -LiteralPath $LiteralPath -File -Force:$Force | Select-Object -First 1)
$emptyChildDirCount = 0
$emptySubdirs = $null
if ($childDirs = Get-ChildItem -LiteralPath $LiteralPath -Directory -Force:$Force) {
$emptySubDirs = New-Object System.Collections.ArrayList
foreach($childDir in $childDirs) {
if ($childDir.LinkType -eq 'SymbolicLink') {
Write-Verbose "Ignoring symlink: $LiteralPath"
} else {
Write-Verbose "About to recurse on $($childDir.FullName)..."
try { # If .AddRange() fails due to exceeding the array list's capacity, we must fail too.
$emptySubDirs.AddRange(#(Get-RecursivelyEmptyDirectories -DoNotValidatePath -LiteralPath $childDir.FullName -Force:$Force))
} catch {
Throw
}
# If the last entry added is the child dir. at hand, that child dir.
# is by definition itself empty.
if ($emptySubDirs[-1] -eq $childDir.FullName) { ++$emptyChildDirCount }
}
} # foreach ($childDir ...
} # if ($childDirs = ...)
if (-not $haveFiles -and $emptyChildDirCount -eq $childDirs.Count) {
# There are no child files and all child dirs., if any, are themselves
# empty, so we only output the input path at hand, as the highest
# directory in this subtree that is empty (save for empty descendants).
$LiteralPath
} else {
# This directory is not itself empty, so output the (highest-level)
# descendants that are empty.
$emptySubDirs
}
}
Tips regarding your code:
Get-ChildItem -Directory is available in PSv3+, which is not only shorter but also more efficient than Get-ChildItem | .. Where { $_.PSisContainer -eq $True }.
Use Write-Host instead of [Console]::WriteLine
[System.Convert]::ToBoolean($answer) only works with the culture-invariant string literals 'True' and 'False' ([bool]::TrueString and [bool]::FalseString, although case variations and leading and trailing whitespace are allowed).
I have written a script which checks a service status, tests two paths and also tests a registry value. Currently I am getting output on the power shell console (that could be because I am using write-output command).
Is there any way to write the single one page output to a file?
I am struggling to find a way to out-file entire output to a file.
Below is the script.
$testpath = Test-Path "C:\test"
$testpath2 = test-path "C:\test"
$mcshieldk = Get-Service -Name mcshield | select Name
$internet = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\internet explorer").MkEnabled $hostname = hostname Write-Host "hostname of comuter is" $hostname if (Test-Path $Machinetype)
{}
else { Write-Host "internet is" $internet }
if ($testpath -eq $true -and $testpath2 -eq $true)
{ Write-Host "test and test1 folder exists" -ForegroundColor Green }
else{ Write-Host "folder does not exists" -ForegroundColor Red } if($mcshield.Name -eq "mcshield") { Write-Host "mcshield service exists" }
else { Write-Host "mcshield does not exists" }
Below is the console output
hostname of comuter is Server1
internet is Yes
test and test1 folder exists
mcshield does not exists
Swap out your Write-Host cmdlets or add in another line with the following:
"Your output text $YourVariable" | Out-File -FilePath "C:\Log.txt" -Append -Encoding utf8
This will append a string to the end of the log file C:\Log.txt. Note, missing the -Append parameter will cause the file to be overwritten.
You can also use the follow to give the same affect:
"Your output text $YourVariable" >> "C:\Log.txt"
But be carefully not to mix the two methods as you might get encoding errors in the text file. If you wish to overwrite the file with the second method use > instead of >>.