Too many if else statements - powershell

I am trying to check if a string "03-22-2019" exists (or not and show the result in output) the file is of ".sql" or ".txt" .
execution\development\process.sql
insert.sql
production\logs.txt
restore.sql
rebuild.txt
I am trying below code but I did with too many if else. The above file path stored in the $paths variable. I need to split the path with "\" and get the last part of the path to do something else.
if ($paths -like "*.sql") {
if ($paths.Contains("\")) {
$last = $paths.Split("\")[-1] #get the last part of the path
$result = "03-22-2019" #get this date from some where else
if ($result) {
#check if pattern string exists in that file.
$SEL = Select-String -Path <path location> -Pattern $result
if ($SEL -ne $null) {
Write-Host "`n $last Contains Matching date "
} else {
Write-Host "`n $last Does not Contains date"
}
} else {
Write-Host "`ndate field is blank"
}
} else {
$result = "03-22-2019" #get this date from some where else
if ($result) {
#check if pattern string exists in that file.
$SEL = Select-String -Path <path location> -Pattern $result
if ($SEL -ne $null) {
Write-Host "`n $last Contains Matching date "
} else {
Write-Host "`n $last Does not Contains date"
}
} else {
Write-Host "`ndate field is blank"
}
}
} elseIf ($paths -like "*.txt") {
if ($paths.Contains("\")) {
$last = $paths.Split("\")[-1] #get the last part of the path
$result = "03-22-2019" #get this date from some where else
if ($result) {
#check if pattern string exists in that file.
$SEL = Select-String -Path <path location> -Pattern $result
if ($SEL -ne $null) {
Write-Host "`n $last Contains Matching date "
} else {
Write-Host "`n $last Does not Contains date"
}
} else {
Write-Host "`ndate field is blank"
}
} else {
$result = "03-22-2019" #get this date from some where else
if ($result) {
#check if pattern string exists in that file.
$SEL = Select-String -Path <path location> -Pattern $result
if ($SEL -ne $null) {
Write-Host "`n $last Contains Matching date "
} else {
Write-Host "`n $last Does not Contains date"
}
} else {
Write-Host "`ndate field is blank"
}
}
} else {
Write-Host "other file types"
}

I would make this a little simpler, below is an example to determine if a file contains the date:
$paths = #("c:\path1","c:\path2\subdir")
ForEach ($path in $paths) {
$files = Get-ChildItem -LiteralPath $path -file -include "*.sql","*.txt"
$last = ($path -split "\\")[-1] # contains the last part of the path
$output = ForEach ($file in $files) {
If (Select-String -path $file -pattern "03-22-2019") {
"$($file.fullname) contains the date."
}
else {
"$($file.fullname) does not contain the date."
}
}
}
$output # outputs whether or not a file has the date string
The outer ForEach loop loops through the paths in $paths. Inside of that loop, you can do what you need to each path $path. I used $last to store the last part of the path in the current iteration. You have not said what to do with that.
The inner ForEach checks each .txt and .sql file for the date text 03-22-2019. $output stores a string indicating whether each .txt and .sql file contains the date string.
If your paths contain the file names, then you can use the following alternatives to grab the file name (last part of the path):
$path | split-path -leaf # inside of the outer ForEach loop
# Or
$file.name # inside of the inner ForEach loop

Looks like you should start with a foreach after your $paths variable like this:
foreach ($path in $paths) {
if ($path -like "*.sql") { #Note the use of one single item in the $paths array you have
$last = $path.split("\")[-1]
$result = #Whatever method you want to use to return a DateTime object
if ($result) { #### Now, this line doesn't make any sense. Do you want to compare a date to an older date or something? Maybe something like "if ($result -ge (Get-Date).addDays(-1) )
{ # Do your stuff }
Doing something like:
if ($paths -like "*.sql")
Doesn't work because $paths is an array and you are making a string comparison and never the two shall meet. Now, if you are trying to find if a string is inside a file, you should use something like "Get-Content" or "Import-Csv"
You can use the "Get-Date" cmdlet to get many different formats for the date. Read about that here. If you are trying to compare multiple dates against multiple files, I would start with a for loop on the files like I did up there, and then a for loop on each file for an array of dates. Maybe something like this:
foreach ($path in $paths) {
foreach ($date in $dates) {
# Get the contents of a file and store it in a variable
# Search for the string in that variable and store the results in a variable
# Write to the console
} # End foreach ($date in $dates)
} # End foreach ($path in $paths)
Post some more updated code and let's see what you come up with.

Related

Powershell - Variable problems

I wrote a script that will pull data from a .properties file (basically a config file). Some of the data from the properties file has environment data (i.e. %UserProfile%), so I run it through a function (Resolve–EnvVariable) that will replace the environment variable with the actual value. The replace works perfectly, but somehow the data seems to be altered.
When I try to use the values that have been run through the function, they no longer work (see results down below).
This is the file contents of c:\work\test.properties
types="*.txt"
in="%UserProfile%\Downloads"
This is my PowerShell Script
Clear-Host
#Read the properties file and replace the parameters when specified
if (Test-Path C:\work\test.properties) {
$propertiesFile = Get-Content C:\work\test.properties
Write-Host "Parameters will be substituded from properties file" -ForegroundColor Yellow
foreach ($line in $propertiesFile) {
Write-Host ("from Properties file $line")
$propSwitch = $line.Split("=")[0]
$propValue = Resolve–EnvVariable($line.Split("=")[1])
switch ($propSwitch) {
"types" { $types = $propValue }
"in" { $in = $propValue }
}
}
}
write-host ("After running through function `n in=" + $in + "<- types=" + $types + "<-")
# This function resolves environment variables
Function Resolve–EnvVariable {
[cmdletbinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, Mandatory = $True,
HelpMessage = "Enter string with env variable i.e. %APPDATA%")]
[ValidateNotNullOrEmpty()]
[string]$String
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
} #Begin
Process {
#if string contains a % then process it
if ($string -match "%\S+%") {
Write-Verbose "Resolving environmental variables in $String"
#split string into an array of values
$values = $string.split("%") | Where-Object { $_ }
foreach ($text in $values) {
#find the corresponding value in ENV:
Write-Verbose "Looking for $text"
[string]$replace = (Get-Item env:$text -erroraction "SilentlyContinue").Value
if ($replace) {
#if found append it to the new string
Write-Verbose "Found $replace"
$newstring += $replace
}
else {
#otherwise append the original text
$newstring += $text
}
} #foreach value
Write-Verbose "Writing revised string to the pipeline"
#write the string back to the pipeline
Write-Output $NewString
} #if
else {
#skip the string and write it back to the pipeline
Write-Output $String
}
} #Process
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
} #End
} #end Resolve-EnvVariable
# Hardcoded values work
$test1 = Get-ChildItem -Path "C:\Users\Paul\Downloads" -Recurse -Include "*.txt"
# Values pulled and updated through function do not work
$test2 = Get-ChildItem -Path $in -Recurse -Include $types
# If I manually assign the values, it works
$in = "C:\Users\Paul\Downloads"
$types = "*.txt"
$test3 = Get-ChildItem -Path $in -Recurse -Include $types
foreach ($test in $test1) { write-host "test1 $test" }
foreach ($test in $test2) { write-host "test2 $test" }
foreach ($test in $test3) { write-host "test3 $test" }
Results
Parameters will be substituded from properties file
from Properties file types="*.txt"
from Properties file in="%UserProfile%\Downloads"
After running through function
in="C:\Users\Paul\Downloads"<- types="*.txt"<-
test1 C:\Users\Paul\Downloads\Test\testPaul.txt
test1 C:\Users\Paul\Downloads\Test2\File1.txt
test3 C:\Users\Paul\Downloads\Test\testPaul.txt
test3 C:\Users\Paul\Downloads\Test2\File1.txt
Two alternatives:
1. Use Environment.ExpandEnvironmentVariables()
If you switched to non-qualified string values and escaped your \, it would be as simple as piping the file to ConvertFrom-StringData, at which point you could expand the variable values with Environment.ExpandEnvironmentVariables():
Properties file:
types=*.txt
in=%UserProfile%\\Downloads
Script:
# Convert file to hashtable
$properties = Get-Content file.properties -Raw |ConvertFrom-StringData
# Copy value to new hashtable, but expand env vars first
$expanded = #{}
foreach($entry in $properties.GetEnumerator()){
$expanded[$entry.Key] = [Environment]::ExpandEnvironmentVariables($entry.Value)
}
Should give you the desired values:
PS C:\> $expanded
Name Value
---- -----
in C:\Users\username\Downloads
types *.txt
2. Use and dot-source a PowerShell script for your properties
This is lifted straight out of a page of the original Exchange Server modules - place all configuration variables in separate scripts, which are in turn dot-sourced when initializing a new session:
Properties file:
$types = "*.txt"
$in = Join-Path $env:USERPROFILE Downloads
Script:
# dot source the variables
. (Join-Path $PSScriptRoot properties.ps1)
# do the actual work
Get-ChildItem $in -Include $types

Test-Path - Variable Part of FileName - Variable doesn't get substituted

SourceFile.txt has the below
<somepath>\filename_$tminus1.csv
The below is the script I've written.
$date = [DateTime]::ParseExact("21-Aug-18", "dd-MMM-yy", $null)
if ($date.DayOfWeek -eq "Monday") {
$tminus1 = $date.AddDays(-3).ToString('yyyyMMdd')
$tminus2 = $date.AddDays(-4).ToString('yyyyMMdd')
Write-Host "Date vaulues set for Monday"
} else {
$tminus1 = $date.AddDays(-1).ToString('yyyyMMdd')
$tminus2 = $date.AddDays(-2).ToString('yyyyMMdd')
Write-Host "Date vaulues set for Non-Monday"
}
$files = Get-Content -Path $PSScriptRoot\SourceFile.txt
foreach ($file in $files) {
Convert-Path $file
if (-not (Test-Path $file)) {
echo ""
Write-Host "$file doesn't exist"
} else {
echo ""
Write-Host "$file exists"
}
}
The Test-Path cmdlet doesn't get the variable value substituted while looking for the file. It prints the below.
<somepath>\filename_$tminus1.csv doesn't exist
I expect the $tminus1 value gets substituted as 20180823 so that the else part prints the below.
<somepath>\filename_$tminus1.csv exists
The problem is when you pull the text in from your CSV it is treated as a literal, as if it as was a single quoted string. You can force PowerShell to re-evalueate the string like so:
$file = $ExecutionContext.InvokeCommand.ExpandString($file)
Or an alternative method would be to change the text in the CSV to this:
<somepath>\filename_{0}.csv
You can then use the format string to apply the variable like so:
$file = $file -f $tminus1
When you read strings from a file, PowerShell does not perform the normal interpolation it does for strings in your code. One way to get around this is to use Invoke-Expression.
Say you have a file (paths.txt) with these paths in it:
C:\Data\$dir1
C:\Data\$dir2
You can get the real paths like this:
$dir1 = "Directory1"
$dir2 = "Directory2"
Get-Content paths.txt |
ForEach-Object {
Invoke-Expression """$_"""
}
This will output:
C:\Data\Directory1
C:\Data\Directory2
Obviously, you can capture these to a variable, send them along the pipeline, etc, instead of just outputting them.

Powershell output formatting?

I have a script that scans for a specific folder in users AppData folder. If it finds the folder, it then returns the path to a txt file. So we can see the computer name and username where it was found.
I would like to be able to format the what is actually written to the text file, so it removes everything from the path except the Computer and User names.
Script:
foreach($computer in $computers){
$BetterNet = "\\$computer\c$\users\*\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm"
Get-ChildItem $BetterNet | ForEach-Object {
$count++
$betternetCount++
write-host BetterNet found on: $computer
Add-Content "\\SERVERNAME\PowershellScans\$date\$time\BetterNet.txt" $_`n
write-host
}
}
The text files contain information like this
\\computer-11-1004S10\c$\users\turtle\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
\\computer-1004-24S\c$\users\camel\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
\\computer-1004-23S\c$\users\rabbit\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
If you have each line in a form of the string $string_containing_path then it is easy to split using split method and then add index(1) and (4) that you need:
$afterSplit = $string_containing_path.Split('\')
$stringThatYouNeed = $afterSplit[1] + " " + $afterSplit[4]
You can also use simple script that will fix your current logs:
$path_in = "C:\temp\list.txt"
$path_out= "C:\temp\output.txt"
$reader = [System.IO.File]::OpenText($path_in)
try {
while($true){
$line = $reader.ReadLine()
if ($line -eq $null) { break }
$line_after_split_method = $line.Split('\')
$stringToOutput = $line_after_split_method[1] + " " + $line_after_split_method[4] + "`r`n"
add-content $path_out $stringToOutput
}
add-content $path_out "End"
}
finally {
$reader.Close()
}
If you split your loop into two foreach loops, one for computer and user directory it would be easier to output the name of the user directory.
$output = foreach($computer in $computers){
$UserDirectories = Get-ChildItem "\\$computer\c$\users\" -Directory
foreach ($Directory in $UserDirectories) {
$BetterNet = Get-ChildItem (Join-Path $Directory.fullname "\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm")
Add-Content "\\SERVERNAME\PowershellScans\$date\$time\BetterNet.txt" "$computer $($Directory.name)`r`n"
write-host BetterNet found on: $computer
$BetterNet
}
}
$output.count

Search through a list of computers to find a string in a file with ForEach- Powershell

I am trying to find a string in the hosts files of computers in a text file. I want to check each machine in the text file for the change in the hosts file.
For security purposes, I cannot put the actual string I am searching for. I am very new to PowerShell, and I tried to do this in CMD, but I could not get the output I wanted.
$sys = Get-Content .\Systems.txt
$Loc = "\\$sys\c$\Windows\System32\Drivers\etc\hosts"
$SearchStr = "string"
$sel = Select-String -pattern $SearchStr -path $Loc
ForEach ($System in $sys) {
If ($sel -eq $null)
{
write-host $sys NotFound
}
Else
{
write-host $sys Found
}
}
You weren't too far off, you just needed to re-think your ForEach loop a little. Move your $Loc = line within the loop so that it updates for each system, then skip the $sel = line and just put that in your If check and you're all set:
$sys = Get-Content .\Systems.txt
$SearchStr = "string"
ForEach ($System in $sys) {
$Loc = "\\$system\c`$\Windows\System32\Drivers\etc\hosts"
If (Select-String -pattern $SearchStr -path $Loc -Quiet)
{
write-host "$system Found"
}
Else
{
write-host "$system Not Found"
}
}
I also escaped the dollar sign in the path for c$. I don't think it's needed in this case, but it's good practice in general when using dollar signs that you want to actually be used as such in strings like that.

In powershell $x.FullName not returning full path

I have a powershell script below which takes a config file and deletes files older than x days matching a regular expression.
Config File:
path,pattern,days,testrun
C:\logs\,^data_access_listener.log,7,false
However here is what is output:
Would have deleted 000a19f6-a982-4f77-88be-ca9cc51a2bcbuu_data_access_listener.log
Would have deleted 00189746-2d46-4cdd-a5bb-6fed4bee25a7uu_data_access_listener.log
I'm expecting the output to include the full file path since I'm using the .FullName attribute so I'd expect output as such:
Would have deleted C:\logs\000a19f6-a982-4f77-88be-ca9cc51a2bcbuu_data_access_listener.log
Would have deleted C:\logs\00189746-2d46-4cdd-a5bb-6fed4bee25a7uu_data_access_listener.log
If I am using $x.FullName why am I not getting the full name with path (C:\logs)?
Thanks
Brad
$LogFile = "C:\deletefiles.log"
$Config = import-csv -path C:\config.txt
function DeleteFiles ([string]$path, [string]$pattern, [int]$days, [string]$testrun){
$a = Get-ChildItem $path -recurse | where-object {$_.Name -notmatch $pattern}
foreach($x in $a) {
$y = ((Get-Date) - $x.LastWriteTime).Days
if ($y -gt $days -and $x.PsISContainer -ne $True) {
if ($testrun -eq "false") {
write-output “Deleted" $x.FullName >>$LogFile
} else {
write-output “Would have deleted $x” >>$LogFile
}
}
}
}
foreach ($line in $Config) {
$path = $line.path
$pattern = $line.pattern
$days = $line.days
$testrun = $line.testrun
DeleteFiles $path $pattern $days
}
Looks like a typo. You are not using FullName in the condition that leads to "Would have Deleted". Change:
write-output “Would have deleted $x” >>$LogFile
to:
write-output “Would have deleted " $x.FullName >>$LogFile
To answer your comment, if you want to call a property on a variable that is expanded in the string, you have to surround it in $(), so that the parser knows to invoke the whole expression. Like this:
write-output “Would have deleted $($x.FullName)" >>$LogFile