I am using a powershell to query a file on a remote machines C-drive and if the file exist with status 'imaging completed' it should run other checks.
$filetofind = Get-Content C:\Image.log
#Get the list down to just imagestatus and export
foreach ($line in $filetofind)
{
$file = $line.trim("|")
echo $file >> C:\jenkins\imagestatus.txt
}
But when I run below commands I am getting the error.
Can anyone help ?
Get-Content : Cannot find path 'C:\Image.log' because it does not exist.
At line:18 char:15
+ $filetofind = Get-Content C:\Image.log
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Image.log:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Test-Path will check if a file exists, and Select-String can be used to search the file for a string, using the -Quiet param will make the command return True if the string is found, rather than returning each line in the text file that includes the string.
Then using both commands in simple if statements to check their status:
$file = "C:\Image.log"
$searchtext = "imaging completed"
if (Test-Path $file)
{
if (Get-Content $file | Select-String $searchtext -Quiet)
{
#text exists in file
}
else
{
#text does not exist in file
}
}
else
{
#file does not exist
}
EDIT:
To check the file on multiple computers you need to use a foreach loop to run the code against each computer separately. The below assumes you have one hostname per line in hostnames.txt.
$hostnames = Get-Content "C:\hostnames.txt"
$searchtext = "imaging completed"
foreach ($hostname in $hostnames)
{
$file = "\\$hostname\C$\GhostImage.log"
if (Test-Path $file)
{
if (Get-Content $file | Select-String $searchtext -quiet)
{
Write-Host "$hostname: Imaging Completed"
}
else
{
Write-Host "$hostname: Imaging not completed"
}
}
else
{
Write-Host "$hostname: canot read file: $file"
}
}
Related
I have the following PowerShell script:
param([switch]$Elevated)
function Test-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated) {
# tried to elevate, did not work, aborting
} else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated ' -f ($myinvocation.MyCommand.Definition))
}
exit
}
function UpdateHosts {
param ($hostName)
Write-Host $hostName
try {
$strHosts = (Get-Content C:\WINDOWS\system32\drivers\etc\hosts -Raw)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
} catch {
Write-Error "Unable to read hosts file"
Write-Error $_
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+ $hostName","$ipAddress $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
} catch {
Write-Error "Unable to write hosts file"
Write-Error $_
exit
}
}
$ipAddress = "127.0.0.1"
UpdateHosts -hostName local.pap360.com
Sometimes, when I run it, I get the following error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process.
When I open up C:\WINDOWS\system32\drivers\etc\hosts in Notepad it's then blank. ie. all the data I had in it is wiped.
My question is... how can I prevent this from happening?
Like if Set-Content can't access the hosts file to write to it then how is it able to wipe it's contents? And why isn't the catch block working?
Here's the full error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by
another process.
At C:\path\to\test.ps1:36 char:92
+ ... $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\WINDOWS\system32\drivers\etc\hosts:String) [Set-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.SetContentCommand
I also don't understand why it's so intermittent. Is there some Windows process that opens the hosts file up for 1s once a minute or some such?
First of all, check if your Firewall or AV software isn't restricting access to the file.
If that is not the case and 'some' other process is currently locking the hosts file, perhaps add a test before reading or writing the file can help:
function Test-LockedFile {
param (
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName', 'FilePath')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$Path
)
$file = [System.IO.FileInfo]::new($Path)
# old PowerShell versions use:
# $file = New-Object System.IO.FileInfo $Path
try {
$stream = $file.Open([System.IO.FileMode]::Open,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None)
if ($stream) { $stream.Close() }
return $false # file is not locked
}
catch {
return $true # file is locked
}
}
Then use like this:
function UpdateHosts {
param ($hostName)
Write-Host $hostName
$path = 'C:\WINDOWS\system32\drivers\etc\hosts'
# test if the file is readable/writable
# you can of course also put this in a loop to keep trying for X times
# until Test-LockedFile -Path $path returns $false.
if (Test-LockedFile -Path $path) {
Write-Error "The hosts file is currently locked"
}
else {
try {
$strHosts = (Get-Content $path -Raw -ErrorAction Stop)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
}
catch {
Write-Error "Unable to read hosts file:`r`n$($_.Exception.Message)"
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+\s+$hostName", "$ipAddress $hostName" |
Set-Content -Path $path -Force -ErrorAction Stop
}
catch {
Write-Error "Unable to write hosts file:`r`n$($_.Exception.Message)"
exit
}
}
}
I've written a function to check if an excel file is being used/locked by another process/user in a shared network drive, and if used, to pause the script and keep checking till it's available as the next action is to move it out of it's folder. However when I'm using System.IO to read the file, it does not open the file. I've tested on my local drive and this does open the file, but does this not work in Network Drives?
$IsLocked = $True
Function Test-IsFileLocked {
[cmdletbinding()]
Param (
[parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName','PSPath')]
[string[]]$Path
)
Process {
while($isLocked -eq $True){
If ([System.IO.File]::Exists($Path)) {
Try {
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
$FileStream.Close()
$FileStream.Dispose()
$IsLocked = $False
} Catch {
$IsLocked = $True
echo "file in use, trying again in 10 secs.."
Start-Sleep -s 10
}
}
}
}
}
This is where the code does not pick up/open the excel file in my function
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
This is where the program calls the function.Loop through a folder of items in the network drive and if the item matches the pattern, then the function will be called to check if the file is in use:
$DirectoryWithExcelFile = Get-ChildItem -Path "Z:\NetworkDriveFolder\"
$DestinationFolder = "Z:\DestinationFolder\"
$pattern = "abc"
foreach($file in $DirectoryWithExcelFile){
if($file.Name -match $pattern){
Test-IsFileLocked -Path $file
$destFolder = $DestinationFolder+$file.Name
Move-item $file.FullName -destination $destFolder
break
}
}
You have to place the close and dispose in the finally part of the try so if it throws an exception it disposes of the lock. There's no guarantee it's a file lock exception either so you are better to throw the exception, catch the exception you're expecting or write it out
$IsLocked = $True
Function Test-IsFileLocked {
[cmdletbinding()]
Param (
[parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName','PSPath')]
[string]$Path
)
Process {
while($isLocked -eq $True){
If ([System.IO.File]::Exists($Path)) {
Try {
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
$IsLocked = $False
} Catch [System.IO.IOException] {
$IsLocked = $True
echo "file in use, trying again in 10 secs.."
Start-Sleep -s 10
} Catch {
# source https://stackoverflow.com/questions/38419325/catching-full-exception-message
$formatstring = "{0} : {1}`n{2}`n" +
" + CategoryInfo : {3}`n" +
" + FullyQualifiedErrorId : {4}`n"
$fields = $_.InvocationInfo.MyCommand.Name,
$_.ErrorDetails.Message,
$_.InvocationInfo.PositionMessage,
$_.CategoryInfo.ToString(),
$_.FullyQualifiedErrorId
$formatstring -f $fields
write-output $formatstring
} finally {
if($FileStream) {
$FileStream.Close()
$FileStream.Dispose()
}
}
}
}
}
}
Edit:
Path should be a string, not a string array unless you have multiple paths in which case rename to $Paths and loop through them $Paths| % { $Path = $_; #do stuff here }
I am trying to unzip a folder and take the count of the files that exist in the folder and check the file count =32 else send an email with present file names and execute the infacmds for present files and wait for 32 conditions to satisfy.
In this script I want to check the file count and also the column count for the existing files and if the column count -neq 280 it has to send email error.
where the column code is like this, which i have to introduce in main code :
$columnCount = ( ( Get-Content "C:\Users\xs15169\Desktop\temp\OEC2_CFLOW.txt" | Select-Object -First 1 ) -Split ',' ).Count
echo "Column count is: $columnCount"
The below code which I have tried(#looks for a file with below type format .zip and it will unzip the files and will take count):
#unzip commands will be here
#LogWrite "Starting of process"
Function LogWrite
{
Param ([string]$logstring)
Add-content $log -value "$(Get-Date) $logstring"
}
try {
LogWrite "Start unzipping $zipfilename"
Remove-Item $unzipfolderdelete | Where { ! $_.PSIsContainer }
#Unzip $zipfilename $unzipfolder
Set-Alias sz "C:\Program Files\7-Zip\7z.exe"
sz x $zipfilename -o"$unzipfolder" -y
LogWrite "$zipfilename unzipped to $unzipfolder successfully"
$filecount= (dir $unzipfolder | measure).Count
if ($filecount -eq 32)
{
#LogWrite "File Count 32
#LogWrite "Executed informatica scripts for condition filecount equal to 32"
#LogWrite "End of if"
}
elseif ($filecount -ne 0)
{
LogWrite "File Count " + $filecount
$list = Get-ChildItem $unzipfolder
$incompletefilenames =""
ForEach($n in $list){
$incompletefilenames = $incompletefilenames + $n.Name + "<br>`n"
}
LogWrite $incompletefilenames
LogWrite "Sending email for filecount not equal to 32"
$Params = #{
email setup
}
Send-MailMessage #Params
LogWrite "Successfully emailed available file names"
#Execute Informatica scripts
LogWrite "Executed informatica scripts for condition filecount not equal to 32"
LogWrite "End of elseif"
}
else
{
LogWrite "File Count " + $filecount
$Params = #{
email setup
}
Send-MailMessage #Params
LogWrite "End of else"
}
}
To do this you must have some paths figured out.
Try this:
Set-Alias sz "C:\Program Files\7-Zip\7z.exe"
$zipFile = "C:\temp\10 yard Fight.zip"
# Get Name of file without extension or path
$zipFolderToCreate = (gci $zipFile).BaseName
# Get path of zip
$path = (gci $zipFile).Directory.FullName
sz x -o"$path\*" "$zipFile" -r
$filecount= (dir "$path\$zipFolderToCreate" | measure).Count
I have the below function running in a logon script, which checks whether the user has the current version of IT Self Help.exe. If the current version is not present, then it should be copied onto the desktop from the $appsource folder:
function UrgentSupportApp {
$ErrorActionPreference = "Stop"
trap {Log-Error $_ $MyInvocation.MyCommand; Return}
$desktop = $env:USERPROFILE + '\Desktop\'
$apptarget = $desktop + 'IT Self Help.exe'
$appsource = '\\file\Administration\Unused\Apps\IT Support App\IT Self Help.exe'
# Remove the old version of the app "IT Help Request.exe"
$oldapps = Get-ChildItem $desktop -Filter *"Help Request.exe"
if ($oldapps.count -gt 0) {Remove-Item $oldapps.PSPath -Force}
# Copy the new version over if it is not already present
$currentversion = (Get-Command $appsource).FileVersionInfo.FileVersion
if (Test-Path $apptarget) {
if ((Get-Command $apptarget).FileVersionInfo.FileVersion -ne $currentversion) {
Copy-Item $appsource $desktop -Force ##### Line 981 #####
}
} else {
Copy-Item $appsource $desktop -Force
}
}
function Log-Error {
param (
$error,
[string]$sub,
$detail
)
$ErrorActionPreference = "Stop"
trap {Log-Error $_ $MyInvocation.MyCommand; Return}
$filename = "\\file\administration\Unused\LogonScriptErrors\$username - $sub - $computername - $(Get-Date -Format ddMMyyyy-HHmmss).log"
New-Item $filename -ItemType File -Value "Message: `r`n`t $($error.Exception.Message) `r`n `r`nPosition: `r`n`t $($error.InvocationInfo.PositionMessage) `r`n `r`nSub: `r`n`t $sub `r`n `r`nDetail: `r`n`t $detail"
}
For a couple of users, I am seeing this error come through on line 981, char 22 (see the comment above):
Could not find file 'C:\Users\USER.NAME\Desktop\IT Self Help.exe'.
At \\DC\NETLOGON\mainlogon.ps1:981 char:22
+ Copy-Item <<<< $appsource $desktop -Force
However
The file clearly can be found, as it made it through the fisrt If condition If (Test-Path $apptarget).
If the file couldn't be found, why would the script complain on that line, where we are not even looking for it?
What is this error trying to tell me? If the file could not be found, surely the script would just continue into the Else statement
I have the following Powershell code:
function readConfigData
{
$workingDir = (Get-Location).Path
$file = ""
if ($Global:USE_LOCAL_SERVER)
{
$file = $workingDir + '\Configs\Localhost.ini'
}
else
{
$file = $workingDir + '\Configs\' + $env:COMPUTERNAME + '.ini'
}
Write-Host 'INIFILE: ' $file
if (!$file -or ($file = ""))
{
throw [System.Exception] "Ini fil är inte satt."
}
if (!(Test-Path -Path $file))
{
throw [System.Exception] "Kan inte hitta ini fil."
}
}
readConfigData
How should I declare the local variable $file that can be passed to the function Test-Path.
My local variable $file get populated but then when I place it as argument to other function it's like it is out of scope.
I read the about scopes article but wasn't able to figure it out.
Currently I get the error:
INIFILE: D:\Projects\scripts\Configs\HBOX.ini Test-Path : Cannot bind argument to
parameter 'Path' because it is an empty string. At
D:\Projects\freelancer.com\nero2000\cmd script to
powershell\script.ps1:141 char:27
+ if (!(Test-Path -Path $file))
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.TestPathCommand
if (!$file -or ($file = ""))
should be replaced by
if (!$file -or ($file -eq ""))
You assign $file to an empty string in the first if clause and therefore your variable is empty in the Test-Path call.
Edit: Also there are some alternatives: How can I check if a string is null or empty in PowerShell?
you could either use
if([string]::IsNullOrEmpty($file))
or even just
if(!$file)
As others have mentioned, you are unintentionally assigning a blank string to $file in your first if (!$file ... statement. That is really the root of your problem.
However, instead of:
if (!$file -or ($file = ""))
You could use this forumula, which I find explains itself better:
if([String]::IsNullOrEmpty($file))
I would define a function Get-ConfigFile to retrieve the config and add a switch for local server:
function Get-ConfigFile
{
Param(
[switch]$UseLocalServer
)
$workingDir = (Get-Location).Path
if ($UseLocalServer.IsPresent)
{
Join-Path $workingDir '\Configs\Localhost.ini'
}
else
{
Join-Path $workingDir ('\Configs\{0}.ini' -f $env:COMPUTERNAME)
}
}
I would also use the Join-Path cmdlet to join a path instead of string concatenations.
Now you can retrive the config file path using:
$configFile = Get-ConfigFile -UseLocalServer:$Global:USE_LOCAL_SERVER
And if needed, ensure that the file exists:
if (-not(Test-Path -Path $configFile))
{
throw [System.Exception] "Kan inte hitta ini fil."
}
Note:
Get-Location will give you the current powershell path (working location), if you want to get the path where your script is located, use this instead:
$workingDir = split-path -parent $MyInvocation.MyCommand.Definitio