Breaking out of a for loop and then deleting a temporary txt file - for-loop

I am a beginner and know just enough to get my self in trouble and completely confused. I am creating a small utility to read data send from a serial device. It uses a user input com port number, and then keeps the com port open with a for loop. the loop will then read the data out put a txt file to desktop and then convert txt to csv. I would like to break out of this for loop after the data has been read and csv has been created, and then delete the txt file (it is only a temporary file). I have tried using a remove item inside the for loop and it deletes the txt file but the csv creates with no data. I have also tried using a start sleep for 10 seconds but this doesn't work either. the rest of the script works like I want it just creates 2 files on the desktop.
Write-Host "Select Serial Port"
$portName = Read-Host -Prompt 'Input Serial Device Name'
Write-Host "Using $portName..."
$port= new-Object System.IO.Ports.SerialPort $portName, 9600, None, 8, one
$port.Open()
Write-Output $port
for(;;)
{
if ($port.IsOpen)
{
$data = $port.ReadLine()
Write-Output $data >> C:\Users\desktop\SBS.txt
import-csv SBS.txt -delimiter "`," | export-csv SBS2003_Results.csv
Start-Sleep -Seconds 15
Remove-Item path C:\Users\desktop\SBS.txt
}
}

Related

Trying to write a powershell script that shows all locked files with computer names instead of IP address

The task given was to create a way for our staff to see who has the file open that they want to use, as Windows says it is either locked and doesn't name the person who has it locked, or it displays the person who made the file but not the person who currently has it open.
I can look it up in Computer Management on the fileserver, but were are hoping to speed up this for the end users.
I've written this powershell script on our fileserver and it works perfectly, I have this running every 5 minutes in Task Scheduler with administrative permissions:
get-smbopenfile -ClientUserName * |select clientcomputername,clientusername,path | Out-File -Encoding utf8 "S:\LockedFiles.txt" -width 300
The output looks like this:
clientcomputername clientusername path
------------------ -------------- ----
IPADDRESS DOMAIN\USERNAME S:\FOLDER\FILE.FILEEXTENSION
What I really want to do now is get the computer name rather than the IP address, just in case staff are logged into multiple machines at the same time.
I wondered if ClusterNodeName or PSComputerName would provide this, but the returned data is always blank.
I thought about this and below is one option (the first line is pseudocode), but as I see it that would mean recursively altering the piped data or reading in piped data, which I'm not even sure how to do.
$ipaddress = IPADDRESS
$Workstation = [System.Net.Dns]::GetHostByName($ipaddress)
Write-Host $Workstation.HostName
Anyone have any ideas on how I can do this? Is there a better way?
I assume you're looking to add a new property to your output object that has the resolved DNS Name from the IP Address found in the ClientComputerName property. For this you use Resolve-DnsName to attempt the name resolution and a Try Catch in case it fails to capture the exception message. For the export I would recommend you to use Export-Csv.
Get-SmbOpenFile -ClientUserName * | ForEach-Object {
$dnsName = try {
(Resolve-DnsName $_.ClientComputerName -ErrorAction Stop).NameHost
}
catch {
[ComponentModel.Win32Exception]::new($_.Exception.NativeErrorCode).Message
}
[pscustomobject]#{
ClientIpAddress = $_.ClientComputerName
ResolvedHostName = $dnsName
ClientUserName = $_.ClientUserName
Path = $_.Path
}
} | Export-Csv "S:\LockedFiles.csv" -Encoding utf8 -NoTypeInformation

Adding values from multiple computers with custom headers in a single csv

I need to accomplish the scenario below and for that I have to create a couple of powershell scripts to accomplish it.
The environment: Windows servers and Windows clients
Scenario
1- Create a script to be run in a specific time every day (with Task Scheduler) on windows clients. This script will push the current computer's hostname and IP address to a csv file with a specific headers (let's call these "Hostnames" and "IP Address"). These header shouldn't be changed as these scripts run from multiple computers at that time and all computers' data should be appended to each header, not overwrite them as this operation continues.
2- From a server, (after 15 mins) as fetching this "computer" list (csv), there should be a ping check for each of them using their IP addresses. If pings are successful on these remote computers, it should say "This computer is up" next to the each computer name and its IP address. If pings are unsuccessful, it should say "This computer is down" next to the each computer name and its IP address. Above these "status" information, there should be another header (let's say "IsAlive"). This header should be added to the csv as well.
So with this setup, I could be able to learn which computers are UP at a specific time and after I trigger some actions them, I could be able to learn if they're still up or down.
To be honest, I couldn't take a long way for it. I started to write a script about the first step but I couldn't combine the headers with values adding under them.
$header="Hostname"
$outputfile="\\10.10.10.40\reports\upcomputers.csv"
Add-Content $outputfile -Value $header
$hostname >> $outputfile
If I use this script (even if with one header), it's adding "NULL" after each alphabet of hostname and it doesn't append the other hostname under the "Hostname" header.
Additionally, I have no idea where to start adding the third header (IsAlive) and add each Test-NetConnection query's output as checking their IP addresses. I request you to show me a way to start with this section as well.
Finally, my output should be like that;
For the first step;
For the second step;
Thank you for your help and information
Stated on the main body of the request
Stage 2:
The easy way of doing this using PSCustomobject. Please find the sample code below:
$outputcsv = "C:\PowerShell\pingstatus.csv"
$hostlist = import-csv "C:\PowerShell\hostlist.csv"
$result = foreach($line in $hostlist){
$pingtest = Test-Connection -ComputerName $line.Hostname -Quiet -Count 1 -ErrorAction SilentlyContinue
if($pingtest) {
$OutputMessage = "This computer is up"
}
else {
$OutputMessage = "This computer is down"
}
[pscustomobject][ordered]#{
HostName = $line.Hostname
IPAddress = $line.IPaddress
IsAlive = $OutputMessage
}
}
$result | Export-csv -Path $outputcsv -NoTypeInformation
The Hostname and IPAddress input will be taken as input.
Note: Your input csv file should contain Hostname IPaddress as header.
Stage1:
why not?
$outputfile="\\10.10.10.40\reports\upcomputers.csv"
$serverDetails = [PSCustomObject]#{
Hostname = "$env:COMPUTERNAME"
IPAddress = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration | where {$_.DHCPEnabled -ne $null -and $_.DefaultIPGateway -ne $null}).IPAddress | Select-Object -First 1
}
$serverDetails | Export-csv $outputfile -Append -NoTypeInformation
There are multiple ways to get IP address a computer, I used Get-WMIObject. You may use other simple ways like Test-Connection or Get-NetIPAddress.
To learn more: Please see
[PsCustomObject]: https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.3

PowerShell update variable while script is running

How do i update a variable in PowerShell while the script is running state?
I have a situation where the script monitors the size of a disk continuously and compares it with a number in a text file on a shared drive (say Z:\quota\software-share-size.txt). If the number in text file is greater than disk size it monitors, then it sends out an email to expand the disk to new size as mentioned in text file. But once the script starts, its not pulling in the new number from file and i dont want to stop and start the script to load new content from the text file. Help please
Maybe this can help you :
while($true)
{
#Here i pull my disk (C:) infomations (i use localhost for my computer, but you can use an IP or a file with multiple IP)
$diskinfo = Get-WmiObject Win32_LogicalDisk -ComputerName localhost | where {$_.DeviceId -eq 'C:'}
#Here you pull only the freespace value with Gb format (default is byte)
$freespace = $diskinfo.freespace/1Gb
$freespace = [int]$freespace
#here you pull the "limit number" off your file, must be in Gb and only the number is written in the file.
$limit=Get-Content -Path "B:\Dev\filewithsizeingo.txt"
$limit = [int]$limit
if ($freespace -gt $limit) #The free diskspace is greater than the limit
{
Write-Host "Diskfreespace is Above Limit" -f Green
}
elseif ($freespace -lt $limit) #The free diskspace is inferior than the limit
{
Write-Host "Free diskspace below limit" -f Red
#break
#code mail sending
}
Start-Sleep 1
}
Because it's a loop, you can modify the filewithsizeingo.txt without stoping the script, and the script will refresh the free diskspace and limit value at each loop.
In the elseif statement, you can insert a break and code the sending of the email (which I do not know yet), dont forget the break or it will send a mail every second.
I hope it helps, or at least it gave you fresh's ideas (i'm a beginner with powershell, the code CAN be improved).

Update files on FTP server folder hierarchy with local files from a single folder

I have a little-big problem. I need to copy/overwrite JPG files from my local FOLDER to server FOLDERS.
Is there a way to search and match JPG files on SERVER with my files on LOCAL and overwrite them in server folders? I do it manually and it takes lot of time.
There are 50 000 JPGs on server and I need to overwrite 20 000 of them in short time.
Many thanks for answers!!
There's no magic way to do your very specific task. You have to script it.
If you are on Windows, it's rather trivial to write a PowerShell script for this, using WinSCP .NET assembly and its Session.EnumerateRemoteFiles method:
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Set up session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Ftp
HostName = "ftp.example.com"
UserName = "username"
Password = "password"
}
$remotePath = "/remote/path";
$localPath = "C:\local\Path";
# Connect
Write-Host "Connecting..."
$session = New-Object WinSCP.Session
$session.SessionLogPath = "upload.log"
$session.Open($sessionOptions)
# Enumerate remote files
$fileInfos =
$session.EnumerateRemoteFiles(
$remotePath, "*.*", [WinSCP.EnumerationOptions]::AllDirectories)
# And look for a matching local file for each of them
foreach ($fileInfo in $fileInfos)
{
$localFilePath = (Join-Path $localPath $fileInfo.Name)
if (Test-Path $localFilePath)
{
Write-Host ("Found local file $localFilePath matching remote file " +
"$($fileInfo.FullName), overwriting..."
# Command-out this line with # for a dry-run
$session.PutFiles($localFilePath, $fileInfo.FullName).Check()
}
else
{
Write-Host ("Found no local file matching remote file " +
"$($fileInfo.FullName), skipping..."
}
}
Write-Host "Done"
Save the script to a file (SortOutFiles.ps1), extract a contents of WinSCP .NET assembly package along with the script, and run it like:
C:\myscript>powershell -ExecutionPolicy Bypass -File SortOutFiles.ps1
Connecting...
Found local file C:\local\path\aaa.txt matching remote file /remote/path/1/aaa.txt, overwritting...
Found local file C:\local\path\bbb.txt matching remote file /remote/path/2/bbb.txt, overwritting...
Found local file C:\local\path\ccc.txt matching remote file /remote/path/ccc.txt, overwritting...
Done
You can first dry-run the script by commenting out the line with $session.PutFiles call.
(I'm the author of WinSCP)
download "Filezilla"... Upload your local files (all 50000 images).. If a image is already there in server,, it will ask you options.. select 'overwrite' and use 'apply for all'...

How to check the folder on the appearance in it of any new file?

Help please. I can not find a solution. (Windows platform)
I need to:
Scan the folder
If you receive any new file.
Process the file.
Another method to detect "new files" is the archive attribute. Whenever a file is created or changed, this attribute is set by windows.
Whenever you process a file, unset it's archive attribute (attrib -a file.ext).
The advantage is, you don't depend on any timing.
To list "new" (or changed) files, use dir /aa (dir /a-a will list processed files)
for more infos see dir /? and attrib /?
Without knowing exactly what you're trying to execute, this is all I can provide. You would theoretically run this as a scheduled task every 1 hour:
foreach ($file in (Get-ChildItem "C:\TargetDirectory" | where {$_.lastwritetime -gt (Get-Date).AddHours(-1)})) {
# Execute-Command -Target $file
}
You could use the FileSystemWatcher class to monitor the folder for new files.
It can easily be used from PowerShell as well:
$FSW = New-Object System.IO.FileSystemWatcher
Then use Register-ObjectEvent to "listen" for events raised from it
FileSystemWatcher is a utility I have recently learned and will definitely use in the future. The best part is that it relies on .net eventing, so you don't need to build an external triggering structure.
Here is an example of how I am using this in a 24/7 production environment (the full script receives an xml, processes it, and inserts the results into SQL in under 3 seconds).
Function submit-resultFile {
#Actual file processing takes place here
}
Function create-fsw {
Register-ObjectEvent $fsw Created -SourceIdentifier "Spectro FileCreated" -Action {
$name = $Event.SourceEventArgs.Name
$File = $Event.SourceEventArgs.Fullpath
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Verbose "The file '$name' was $changeType at $timeStamp" -fore green
submit-ResultFile -xmlfile $file
}
}
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $watchFolder, $watchFilter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
$xmlFiles = Get-ChildItem -Path $ResultsDirectory -Filter *.xml
foreach ($file in $xmlfiles)
{
submit-ResultFile -xmlfile $File.FullName
}
Create-fsw
# Register a new File System Watcher
Several important points to be aware of:
- if files exist in that location before the FSW is created they WILL NOT trigger an "objectevent", so in my script you'll observe that I begin by running a sweep for existing files.
when FSW does trigger you want it to process only 1 file at a time. Since the next file creation event will generate a new "objectevent". Structuring a FSW to work on multiple files per trigger will eventually result in a crash.

Resources