Create a Clickable Folder Link in Powershell - powershell

I have a script that searches for a file name in a folder and gives a folder path. Does Powershell have a way to make these folder paths a clickable link? I want to bring up a list of file directory paths that I can click on.
Here is what the code looks like.
#Declare Variables.
$Software = #()
#Delcares Directories to search.
$Directories = #(
'\\Software\Unlicensed Software'
'\\Software\Licensed Software')
#Gets folder contents of directories at a depth of three.
Foreach($Directory in $Directories)
{
$Path = Get-ChildItem $Directory -Recurse -Depth 3| ?{ $_.PSIsContainer } | Select-Object FullName;
$Software += $Path
}
#Gets user string.
$target = Read-Host -Prompt 'Input a software name or part of a name. ("Exit" to quit)';
#Finds matches and adds them to Links.
while ($target -ne "exit")
{
$count = 0;
$Links = New-Object Collections.Generic.List[string];
Foreach ($line in $Software)
{
if($line -like "*$target*")
{
$Links.Add($line.FullName);
$count += 1;
}
#Stops code when results are greater than 100 entries.
if($count -gt 99)
{
Write-Output "Your search result yielded more than 100 entries. Try narrowing down your search."
Break
}
}
#Prints links.
ForEach($Link in $Links)
{
Write-Output $Link;
}
Write-Host `n$count" entries found`n";
#Asks users if they would like to continue.
$target = Read-Host -Prompt 'Input a software name or part of a name ("Exit" to quit)';
}
#Exits Program
Write-Host "Press any key to continue ...";
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");

This is a quite simple way you could use to select a folder an open it, it will display an Out-GridView until you cancel or close the grid and open the folder once you make a selection. If you're looking for something better looking and more complex you would have to code your own WinForm app.
$directories = Get-ChildItem $env:USERPROFILE -Directory |
Select-Object Name, FullName
do
{
$selection = $directories |
Out-GridView -PassThru -Title 'Choose a Folder'
if($selection){ Invoke-Item $selection.FullName }
} while($selection)

You could make a HTML File:
Add-Type -AssemblyName System.Web
[System.Web.HttpUtility]::HtmlDecode((gci | select Name,#{n='Fullpath'; e={(("<a href='file://" +$_.fullname+"'>"+$_.Fullname+'</a>' ))}} | ConvertTo-Html)) | out-file c:\file.html
iex c:\file.html

Related

Get missing file name in a sequence of files

In a folder i have many files, they are sequential, type ABC001.csv,ABC002.csv,ABC003.csv and so on. Sometimes this sequence breaks, then there are missing files and I need to identify which of the sequence are missing in the folder manually, we have more than 700files.
Does anyone here know a power shell script to help me with this task?
If all files to be counted always have a naming format ABC<3-digit number>.csv, then you could do this:
# get an array of integer sequence numbers from the files
$sequence = (Get-ChildItem -Path 'D:\Test' -Filter 'ABC*.csv' -File).BaseName |
Where-Object { $_ -match '(\d{3})$' } | ForEach-Object { [int]$matches[1] } |
Sort-Object -Unique
# get the missing numbers (if any) and output filenames to be collected in $missingFiles
$missingFiles = Compare-Object $sequence (1..($sequence[-1])) -PassThru | ForEach-Object {
'ABC{0:D3}.csv' -f $_ # reconstruct the filename with the missing number
}
# output on screen
if (#($missingFiles).Count) {
Write-Host "Files missing:" -ForegroundColor Yellow
$missingFiles
}
else {
Write-Host "All files are in sequence" -ForegroundColor Green
}
Of course, change the rootpath of the files (here 'D:\Test') to your own
In this example,
The script is located in the same location as a folder named "Temp".
The "Temp" folder had files ABC001.csv through ABC717.csv.
Files ABC123.csv and ABC555.csv were deleted from "Temp" folder.
for ($i = 1; $i -lt 718; $i++) {
$FileName = $PSScriptRoot + '\Temp\ABC{0:d3}.csv' -f $i
if(-not (Test-Path -Path $FileName -PathType Leaf)) {
Write-Host "Missing $FileName"
}
}
Output when ran:
Missing 123
Missing 555

Powershell: Dynamic Menu-based program to copy file

The purpose of this was to copy one (or more) files from one user selected dynamic location to another user selected dynamic location.
Write-Host "Choose where to copy FROM"
$1MainFolder = (Get-ChildItem "C:\Users\$env:USERNAME\FROM" -Directory | Sort-Object)
$menu = #{}
for ($i=1;$i -le $1MainFolder.count; $i++) {
Write-Host "$i. $($1MainFolder[$i-1].name)"
$menu.Add($i,($1MainFolder[$i-1].name))
}
[int]$ans = Read-Host 'Enter selection'
$selection = $menu.Item($ans)
# The directory after the selection is only one, however it's different for every user; this remains the same for "COPY TO"
$First = (Get-ChildItem "C:\Users\$env:USERNAME\FROM\$selection" -Directory)
# This directory is the same for every user; this remains the same for "COPY TO"
$DataFROM = (Set-Location $First\USERFILE)
Clear-Host
Write-Host "Choose where to copy TO"
$2MainFolder = (Get-ChildItem "C:\Users\$env:USERNAME\TO" -Directory | Sort-Object)
$menu2 = #{}
for ($i=1;$i -le $2MainFolder.count; $i++) {
Write-Host "$i. $($2MainFolder[$i-1].name)"
$menu2.Add($i,($2MainFolder[$i-1].name))
}
[int]$ans2 = Read-Host 'Enter selection'
$selection2 = $menu2.Item($ans2)
$Second = (Get-ChildItem "C:\Users\$env:USERNAME\TO\$selection2" -Directory)
$DataTO = (Set-Location $Second\USERFILE)
Clear-Host
That completes section one and two. The third section allows the user to then pick the file they want copied - this is a separate function which works well, however it errors in conjunction with this. I've since simplified the function to obtain the same error with:
# The Select-Object section being the simplified part...
Get-ChildItem $DataFROM | Select-Object "DATA.log*" | Copy-Item -Destination $DataTO
What I expect to happen is for the data to be copied from the initial dynamic location to the final dynamic destination.
What happens is that I get an error stating that it "Cannot bind argument to parameter 'Path' because it is an empty string." to which it points at the $DataTO variable.
This then lead me to disregard the third section and test the variables to which I found that if I use the following after the first two sections:
Get-ChildItem $DataFROM
# or
Get-ChildItem $DataTO
it returns back information from the last selected menu, disregarding the variables entirely. So then are the dynamic menus not to be used in this manner or perhaps I am using the menus wrong?
Would someone please advise where I've gone astray?
Update: I've since re-worked it to the following that fits my requested needs: (hopefully this will aid someone else needing something similar)
[array]$sMainFolder = Get-ChildItem "C:\Users\$env:USERNAME\DATA-TO_FROM" -Directory | ForEach-Object {
Get-ChildItem $_.FullName | ForEach-Object {
$found = Get-ChildItem $_.FullName | Where-Object { $_.Name -eq 'USERFILE' } | Select-Object -First 1
if ($found) {
[PSCustomObject]#{
RootFolder = [string]$_.FullName
Path = $found.FullName
}
}
}
}
[array]$dMainFolder = Get-ChildItem "C:\Users\$env:USERNAME\DATA-TO_FROM" -Directory | ForEach-Object {
$subs = Get-ChildItem $_.FullName
$found = $subs | ForEach-Object { Get-ChildItem $_.FullName } | Where-Object { $_.Name -eq 'USERFILE' } | Select-Object -First 1
if ($found) {
[PSCustomObject]#{
RootFolder = [string]$_.FullName
Path = $found.FullName
}
}
}
function ShowMenu ($Folders, $Message) {
Write-Host $Message
for ($i = 1; $i -le $Folders.count; $i++) {
$name = $Folders[$i - 1].RootFolder
Write-Host "$i. $name"
}
[int]$ans = Read-Host 'Enter Selection'
Return $Folders[$ans -1].Path
}
$DataFROM = ShowMenu $sMainFolder "Choose where to copy FROM"
$DataTO = ShowMenu $dMainFolder "Choose where to copy TO"
Clear-Host
Copy-Item "$DataFROM\*" $DataTO -Recurse -Force

Identify Empty Folders

I would like to identify a specific empty folder in our user profiles.
I have a text file containing all of our user names that I want the script to refer to. The script will loop each user directory and either output to file or screen and say if the directory is empty. Hidden files do not have to count!
Something similar
FOR /F %U IN (C:\UserList\UserList.TXT) DO *Find and List Empty Folder* \\Server\Share\%U\Target_Folder
Powershell solutions welcome!
This article on Technet provides the following Powershell code snippet to identify all empty folders:
$a = Get-ChildItem C:\Scripts -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName
Replace "C:\Scripts" with the root folder you want to search.
Update:
The following script will get you in the ballpark.
$content = Get-Content C:\Temp\FolderList.txt
foreach ($line in $content)
{
Write-Host $line -NoNewline
$testObject = Test-Path -Path $line
if ($testObject)
{
$folder = Get-Item -Path $line
$filesCount = $folder.GetFiles().Count
if ($filesCount.Equals(0))
{
Write-Host " - Empty folder"
}
else
{
Write-Host " - Contains files"
}
}
else
{
Write-Host " - Invalid path"
}
}

using a global variable in multiple functions powershell

I have this code :
$Count=0
Function DryRun-UploadFile($DestinationFolder, $File, $FileSource, $Count)
{
if($FileSource -eq $null){
$FileSource = $Folder
}
$path= [String]$FileSource+'\'+$File
$Size = get-item $Path
$Size = $Size.length
if($Size -lt 160000){
Write-Host "Passed"
}else{
$Count=$Count+1
}
}
function DryRun-PopulateFolder($ListRootFolder, $FolderRelativePath, $Count)
{
Write-Host "Uploading file " $file.Name "to" $WorkingFolder.name -ForegroundColor Cyan
if(!($File -like '*.txt')){
#Upload the file
DryRun-UploadFile $WorkingFolder $File $FileSource $Count
}else{
$Count=$Count+1
}
}
}
Function DryRun-Copy-Files{
$AllFolders = Get-ChildItem -Recurse -Path $Folder |? {$_.psIsContainer -eq $True}
#Get a list of all files that exist directly at the root of the folder supplied by the operator
$FilesInRoot = Get-ChildItem -Path $Folder | ? {$_.psIsContainer -eq $False}
#Upload all files in the root of the folder supplied by the operator
Foreach ($File in ($FilesInRoot))
{
#Notify the operator that the file is being uploaded to a specific location
Write-Host "Uploading file " $File.Name "to" $DocLibName -ForegroundColor Cyan
if(!($File -like '*.txt')){
#Upload the file
DryRun-UploadFile($list.RootFolder) $File $null $Count
}else{
$Count=$Count+1
}
}
#Loop through all folders (recursive) that exist within the folder supplied by the operator
foreach($CurrentFolder in $AllFolders)
{
DryRun-PopulateFolder ($list.RootFolder) $FolderRelativePath $Count
}
Write-output "Number of files excluded is: "$Count | Out-file DryRun.txt -append
}
I have removed some of my code for simplicity sake as it has nothing to do with my problem. My code goes through a file structure and counts up if the file is above 160000 bytes or is a txt file. run calling DryRun-Copy-Files.
And I have a variable called $count which I want to use in all the functions and then output what the count is to a file.
The problem is it only counts in the first function DryRun-Copy-Files not in the others
define the variable with global:
$global:count=0
and use it in the functions (don't explicit pass it)

Checking if files exist in folders

I have a task to monitor if files exist in certain folders for longer then 5 minutes. I need the script to only send emails if files exist older then 5 minutes. Right now the script will run and email me however I am having trouble getting the output formatted correctly.
I'd like the email to include the first column of the CSV, Company, the path and the output of get-childitem. I will be removing the write-host's since the script will run via a scheduled task. The CSV has the following format..
\\some\share
\\some\share2
\\some\share3
Though I would like to have it as..
Company1,\\some\share
Company2,\\some\share2
Company3,\\some\share3
So that I can include the first header in the email
Here's what I have for the code so far..
$SmtpClient = new-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
$SmtpClient.Host = "smtp.some.address"
$MailMessage.from = ($ServerName + "#someaddress.com")
$MailMessage.To.add("me#someaddress.com")
$MailMessage.Subject = "File Monitor"
$date = (get-date).AddMinutes(-5)
foreach($path in get-content "C:\Scripts\servers_path.txt") {
if ((Test-Path -path $path)) {
$item = get-childitem $path *.* | where-object {$_.LastWriteTime -lt $date}
$item | Select FullName, Name, LastWriteTime
if ($item) {
$MailMessage.Body += "$path `n"
$MailMessage.Body += "$item `n`n"
$MailMessage.Body += "---------------------------------------------------------------------------------------------------------`n"
}
}
elseif (!(Test-Path $path)) {
Write-Host "The folder," $path "does not exist" -fore red
}
}
$SmtpClient.Send($MailMessage)
Thank you
I think originally I was confusing your input as output in the question. How about something like this then? Assuming the following input data is contained in "c:\temp\data.txt".
Company1,\\some\share
Company2,\\some\share2
Company3,\\some\share3
Read the comments in code to get a sense for what is going on there. This code would go in place of your ForEach loop that you already have in theory. You would need to play with the output to get your desired outcome. If I am off base be as verbose as possible.
$date = (get-date).AddMinutes(-5)
# Import the data as a csv into PowerShell specifying the headers since they dont exist.
Import-csv -Path "c:\temp\data.txt" -Header "Company","Path" | ForEach-Object{
# Check if the path is valid for this item.
If(Test-Path $_.Path){
# This folder exists. Check the contents for certain files.
$data = Get-ChildItem $_.Path | Where-Object {$_.LastWriteTime -lt $date}
If($data){
# Output the company name and path on a separate line.
$message = "`r`n$($_.Company)`r`n$($_.Path)`r`n"
# Output each file on its own line.
$message += "$(($data | Select-Object -ExpandProperty Name) -Join "`r`n")"
# Append built string to the body of the email.
$MailMessage.Body += $message
}
} Else {
# Path does not exist. Have message stating such.
$MailMessage.Body += "`r`n$($_.Company)`r`n$($_.Path) does not exist `r`n"
}
}
If you wanted to return more properties like LastWriteTime we could do that if we replace this one line
$message += "$(($data | Select-Object -ExpandProperty Name) -Join "`r`n")"
With something like this:
$data | ForEach-Object{
$message += "Name: '{0}' - Last Write Time: '{1}'" -f $_.Name, $_.LastWriteTime
}