I want to create a script that re-runs a batch file if a specific keyword is found within a log. The problem that I am having is with the function used to check the file. Currently, when I run this script it exits saying that the expression "does not match" even though the keyword does, in fact, exist within the log file. In this case, the log file to check is named is 'output.log' and the keyword to match is called 'temporary'.
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_" + $current_date
"checking in directory... --> " + $file_path
$word_to_find = "temporary"
$file_to_check = "output.log"
"Searching for matching expression '" + $word_to_find + "' in file: " + $file_to_check
$containsWord = $file_to_check | %{$_ -match $word_to_find}
if ($containsWord -contains $true) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
} else {
'The expression does not match. Feed OK.'
}
Thats because you first have to get the content of the log, atm you are just comparing the string "output.log" against the string "temporary", which ofc returns the value 'false'.
If you want to keep your attempt, try it like this (Remember that output.log should either be in the running directory (than use .\ like here) or you have to give the full path to the log file):
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_"+ $current_date
"checking in directory... --> " + $file_path
$word_to_find="temporary"
$file_to_check=".\output.log"
"Searching for matching expression '" + $word_to_find + "' in file: " + $file_to_check
$containsWord = Get-Content $file_to_check | %{$_ -match $word_to_find}
If ($containsWord -contains $true) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
}
Else {
'The expression does not match. Feed OK.'
}
If you are up to some improvements, I would do it more like this:
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_"+ $current_date
Write-Host "checking in directory... --> $file_path"
$word_to_find="temporary"
$file_to_check="<fullPathToLog>\output.log"
Write-Host "Searching for matching expression '" + $word_to_find + "' in file: $file_to_check"
If((Get-Content $file_to_check) -match $word_to_find) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
}
Else {
'The expression does not match. Feed OK.'
}
This would save you the foreach and the extra variable.
Related
I'm just getting to know PowerShell.
I have a script (got it from an old administrator who quit), it worked well with files, renamed it as needed.
Old file name for example:
mcgruz 16.11.2021 03_30_1720211116_033432.xls
was renamed to
20211116_mcgruz
or another:
prim43 15.11.2021 23_00_1920211115_320117.xls
was renamed to
20211115_prim43
Script:
# Definition
$srcFld = "\\domain\MC_REPORT\INCOMING"
$arcFld = "\\domain\MC_REPORT\ARC"
$UnsortFld = "\\domain\MC_REPORT\UNSORT"
$destFld = $UnsortFld
$mcgruzFld = "\\domain\MC_REPORT\RAIL_RUN_DATE"
$mcdatcFld = "\\domain\MC_REPORT\DEMURRAGE_AT_THE_CUSTOMS"
$demurrFld = "\\domain\MC_REPORT\DEMURRAGE"
$prim43Fld = "\\domain\MC_REPORT\FLAT_CAR_PRICE_GROUP"
$podsylFld = "\\domain\MC_REPORT\PODSYL"
$pogruzFld = "\\domain\MC_REPORT\POGRUZKA"
$claimsFld = "\\domain\MC_REPORT\CLAIMS"
$dislzvFld = "\\domain\MC_REPORT\DISLZV"
$renummFld = "\\domain\MC_REPORT\RENUM"
$kontdislFld = "\\domain\MC_REPORT\KONTDISL"
$regex = "(\b\w+\b).(\d+[.]\d+[.]\d+)"
#
$folder = Get-ChildItem $srcFld -Filter *.xls
foreach($file in $folder) {
$fn=($file.Name -split $regex)
$fn[2] = date $fn[2] -format yyyyMMdd
$filenew = $fn[2]+"_"+$fn[1]+".xls"
switch ($fn[1])
{
renumm {
$destFld = $renummFld
}
mcgruz {
$destFld = $mcgruzFld
}
mcdatc {
$destFld = $mcdatcFld
}
prim43 {
$destFld = $prim43Fld
}
demurr {
$destFld = $demurrFld
}
podsyl {
$destFld = $podsylFld
}
pogruz {
$destFld = $pogruzFld
}
dislzv {
$destFld = $dislzvFld
}
claims {
$destFld = $claimsFld
}
kontdisl {
$destFld = $kontdislFld
}
default {
$destFld = $UnsortFld
}
}
Copy-Item $file.FullName "$destFld\$filenew" -Recurse
Copy-Item $file.FullName "$arcFld\$filenew" -Recurse
Remove-Item $file.FullName -recurse
}
But recently, the vendor changed the files, and now the script does not work correctly.
New file name: prim4320211205_230050.xls script renamed it to: _.xls and not safe old file.
I must to change the code, but I don’t know exactly what to change.
$regex = "(\b\w+\b).(\d+[.]\d+[.]\d+)"
#
$folder = Get-ChildItem $srcFld -Filter *.xls
foreach($file in $folder) {
$fn = ($file.Name -split $regex)
$fn[2] = date $fn[2] -format yyyyMMdd
$filenew = $fn[2]+"_"+$fn[1]+".xls"
I will be grateful for your help
try to use code on local machine. And powershell ISE send error:
+ $fn[2] = date $fn[2] -format yyyyMMdd
+ ~~~~~~
+ CategoryInfo : WriteError: (:) [Get-Date],
ParameterBindingException
+ FullyQualifiedErrorId :
ParameterBindingFailed,Microsoft.PowerShell.Commands.GetDateCommand
To handle both file naming formats, you could use below:
foreach ($file in (Get-ChildItem -Path $srcFld -Filter '*.xls' -File)) {
if ($file.BaseName -match '^(.+)(\d{8})_.*') {
# testing new format like 'prim4320211205_230050'
$name = $matches[1]
$filenew = '{0}_{1}{2}' -f $matches[2], $matches[1], $file.Extension
}
elseif ($file.BaseName -match '^(\w+) +(\d{1,2}\.\d{1,2}\.\d{4}) .*') {
# testing old format like 'mcgruz 16.11.2021 03_30_1720211116_033432'
$name = $matches[1]
$date = [datetime]::ParseExact($matches[2], 'dd.MM.yyyy', $null)
$filenew = '{0:yyyyMMdd}_{1}{2}' -f $date, $matches[1], $file.Extension
}
else {
# output a warning thatthe filename could not be parsed out
Write-Warning "could not rename file '$($file.FullName)'"
continue # skip this file and proceed with the next one
}
# now proceed with the rest of your code
$destFld = switch ($name) {
'renumm' { $renummFld }
'mcgruz' { $mcgruzFld }
# etc.
}
$file | Copy-Item -Destination (Join-Path -Path $destFld -ChildPath $filenew)
$file | Copy-Item -Destination (Join-Path -Path $arcFld -ChildPath $filenew)
$file | Remove-Item -Force
}
Regex expression explained on regex101.com, with test strings: regex101: build, test, and debug regex
(\b\w+\b).(\d+[.]\d+[.]\d+)
1st Capturing Group (\b\w+\b)
\b assert position at a word boundary: (^\w|\w$|\W\w|\w\W)
\w matches any word character (equivalent to [a-zA-Z0-9_])
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
\b assert position at a word boundary: (^\w|\w$|\W\w|\w\W)
. matches any character (except for line terminators)
2nd Capturing Group (\d+[.]\d+[.]\d+)
\d matches a digit (equivalent to [0-9])
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
Match a single character present in the list below [.]
. matches the character . with index 4610 (2E16 or 568) literally (case sensitive)
\d matches a digit (equivalent to [0-9])
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
Match a single character present in the list below [.]
. matches the character . with index 4610 (2E16 or 568) literally (case sensitive)
\d matches a digit (equivalent to [0-9])
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
I want each row to be populated with the data retrieved from each file. Currently, the 2nd and 3rd column entries are being written to a newline.CSV file output I have tried using "export-csv" and the "-nonewline" command. Perhaps there is a regex command that would solve this?
#Column headings
$headings = "Source file, Review file existence, Review Result, Compilation Check Result, Static Analysis Result, Review up-to-date, Reviewed code version, Latest code version"
# Create array with $headings as first input
$output = #($headings)
$SourceParentDir = "C:\Document"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
$output += $BaseName
$output += ", Review Exists" # writes to a newline
$output += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
}
# write output to a csv file
$output | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
You can do things that way, but there's definitely a more-Powershelley way:
Get-ChildItem -Path $SourceParentDir -Recurse -Filter '*.?pp' |
ForEach-Object {
$File = $_
# Put your other code here
# This will output an object to the stream
[PSCustomObject]#{
'Source file' = $File.Name
'Review file existence' = 'Review Exists'
'Review Result' = $Result
'Compilation Check Result' = $Compilation
'Static Analysis Result' = $StaticAnalysis
'Review up-to-date' = $UpToDateFlag
'Reviewed code version' = $ReviewFileVersionNumber
'Latest code version' = $SourceFileVersionNumber
}
} | Export-Csv Example-csv.csv -NoTypeInformation
The big drawback here is that you don't get a lot of formatting choices about the CSV. Every field is quoted, for example.
Alternately, if you really want really detailed control of the $output string, you should use a StringBuilder instead of a String. StringBuilder is one of the most potent and widely used classes in C#. This is because strings in C# and Powershell are immutable, so when you += a String you create a new string, copy everything over with the new bit, then throw the old string away. It can be very memory intensive with large operations. StringBuilder lets you get around all that. It's a class that's designed to let you append stuff to strings and format them however you want.
You instance it like so:
$output = [System.Text.StringBuilder]::new()
And then you typically call one of two methods to add text. Append($string) appends the string, AppendLine($string) appends the line and then adds a newline. You can also call AppendLine() with no argument to just add a newline. To get your final string, you call the ToString() method. The append methods do return a status when you call them which you can prevent from outputting pretty easily with a [void], or by saving it to another variable if you need it.
$output = [System.Text.StringBuilder]::new()
[void]$output.AppendLine($headings)
$SourceParentDir = "C:\StarTeam\00011114-JSENS_TRS\ATR\04_SW_Implementation\Operational"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
[void]$output.Append($BaseName)
[void]$output.Append(", Review Exists")
[void]$output.Append(", $Result,$Compilation,$StaticAnalysis,$UpToDateFlag,$ReviewFileVersionNumber,$SourceFileVersionNumber,")
[void]$output.AppendLine()
}
$output.ToString() | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
$output is an array, so each of those += inside the loop is a new entry in the array, and therefore a new line in the file.
You can fix this by using a temporary string variable in the middle of the loop, and appending it to $output once at the end of each iteration:
foreach ($File in $Files)
{
$row = [System.IO.Path]::GetFileName($File)
$row += ", Review Exists"
$row += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
$output += $row
}
or by putting everything in one big string interpolation:
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
$output += "$BaseName, Review Exists, $Result, $Compilation, $StaticAnalysis, $UpToDateFlag, $ReviewFileVersionNumber, $SourceFileVersionNumber"
}
But I agree with the other answer that building up an array of custom objects for the Export-Csv commandlet is more idiomatic for PowerShell.
The issue is how you're populating $Output. Since it is defined as an array, each time you're adding new information it's creating a new entry rather than just adding the additional information to a string.
If you make your $output into one line with all required fields it should correct it without you changing the array.
$output += $BaseName + ", Review Exists" + ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
I'm trying to do a simple bulk file rename to remove non-standard characters (e.g. " 0123456789-_.") from the start of a filename and just add a string to the filename.
e.g. '12 -_myfilename.doc' would become '012345 - myfilename.doc'
...where 012345 is my study number. I've tried using the script below but keep on getting the following error when stepping through the script executing the trimstart line...
"'Name' is a ReadOnly" property"
I guess that this is not a trimstart problem but the way in which I'm attempting to gather the result from it.
Any help appreciated.
The relevant part of the code looks like...
$MyFileObject=0
if ($MyRecursiveFlag) {
Get-ChildItem $MyStudyPath -recurse | where {$_.extension -in ".xls",”.xlsx”,".xslt",".pdf",".doc",".docx",".xlsm",".xml",".htm",".ppt"}|
ForEach-Object{
#Check if start of the file is compliant
$mymatch = [Regex]::Match($_, '\d{5}\s-\s')
if ($mymatch.Success){
#Already renamed correctly so nothing else to do
Write-Host "Okay - no changes $($_.Name)" -ForegroundColor Green
} else {
#No, it's not compliant so let's remove any preceding numbers, spaces and dashes etc
$MyFileObject = $_
$MyFileObject.Name = $MyFileObject.Name.trimstart(" 0123456789-_.")
#...and rename the file
Rename-Item -LiteralPath $MyFileObject.FullName -NewName "$($MyStudyNumber + ' - ' + $MyFileObject.Name)"
Write-Host "Renamed to $($MyFileObject.Name)" -ForegroundColor Yellow
}
}
}
I'm have the same problem when using Windows 7 PowerShell v3 and 5.1
You cannot change the Name of the current Get-ChildItem object (in your case $MyFileObject).
This should work (part of your script):
$MyFileObject = $_
$FileName = $MyFileObject.Name.trimstart(" 0123456789-_.")
#...and rename the file
Rename-Item -LiteralPath $MyFileObject.FullName -NewName "$($MyStudyNumber + ' - ' + $FileName)"
Write-Host "Renamed to $FileName" -ForegroundColor Yellow
So I am trying to pass the file directory to the function convert at the bottom. When I run the script I receive the output:
Succes
C:\test 2\00000027627-00001\PROCESSING CHECKLIST
convert : Invalid Parameter - 2\00000027627-00001\PROCESSING
At C:\Users\pmanca\Desktop\msbxmlreader.ps1:35 char:13
+ convert([string]$hostdirectoryPlusLoanPlusDocType)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Invalid Paramet...0001\PROCESSING:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
It appears to be cutting off the file path after C:\test. Could the file path be too long? even so i wouldn't imagine getting the error at the function call and instead somewhere in the function when it couldn't resolve the path.
#? is a shortcut for where object and % is a shortcut for foreach-object
cls
#count 1 = Loan Number
#count 4 = Cust Num
#count 5 = Document Type
$hostdirectory = "C:\test 2"
$count = 0
$file = (Select-xml -Path "$hostdirectory\index.xml" -XPath / ).Node
$test = $file.ExportedResult.Docs.ExportedDoc.Doc.UdiValues.UdiValue.Value# | Select-Object {$_.UdiValue.Value.InnerXML} -Unique #|? {$_.UdiValue.Name -eq "Loan Number"}
$test | ForEach-Object{
$count++
# Write-Host $_.innerxml "----" $count
if($count -eq 1){
[string]$xmlHold = $_.InnerXML
$hostdirectoryPlusLoan = "$hostdirectory\$xmlHold"
if(!(test-path $hostdirectoryPlusLoan)){
New-Item $hostdirectoryPlusLoan -ItemType directory
}
}
if($count -eq 5){
[string]$xmlHold = $_.InnerXML
$hostdirectoryPlusLoanPlusDocType = "$hostdirectoryPlusLoan\$xmlHold"
if(!(test-path $hostdirectoryPlusLoanPlusDocType)){
New-Item $hostdirectoryPlusLoanPlusDocType -ItemType directory
}
if(Test-Path "$hostdirectory\$xmlhold.pdf"){
$check = Copy-Item "$hostdirectory\$xmlHold.pdf" -Destination $hostdirectoryPlusLoanPlusDocType -ErrorAction SilentlyContinue
if(-not $?) {write-warning "Copy Failed"; Write-Host $Error[0].exception.message}
else {write-host "Succes"}
Write-Host $hostdirectoryPlusLoanPlusDocType
convert([string]$hostdirectoryPlusLoanPlusDocType)
}
}
if($count -ge 8){
$count = 0
# Write-Host "-----------------------------------"
}
}
function convert([string]$inputDirectory){
write-host $inputDirectory
#Variable to hold current input directory
$InputPathFilter = $InputDirectory + '\*.pdf'
#Variable to hold the list of PDFs from the current input directory
$PDFList = #(gci $InputPathFilter | foreach {write-output $_.name})
#Loop through list of PDF files to convert to TIF image files.
for ($j=0; $j -lt $PDFList.count; $j++) {
#Each PDF will go into its own directory/batch
#Create a variable with only the file name
$FileName = $PDFList[$j] -replace(".pdf",'')
#Variable of the full path to the current PDF
$InputPath = $InputDirectory + '\' + $PDFList[$j]
#Variable to hold output path of each TIF file. Filename format is that used by batches in ScerIS (i.e. 00010001.tif, 00010002.tif, etc...)
$OutputFullDirectory = $inputlDirectory + '\' + $FileName + "_" + "{0:D4}" -f + '1' + '%04d.tif'
#Calls Ghostscript command line executable to process each PDF file into a group of single page TIF image files
&'C:\Program Files\gs\gs9.14\bin\gswin64c.exe' -sDEVICE=tiffg4 -dBATCH -dNOPAUSE -q -r600 "-sOutputFile=$OutputFullDirectory" "$InputPath"
#Increment the counter for the loop
$DocCounter = $j + 1
#Rename the current pdf so that it isn't processed again.
$RenamePath = $PdfList[$j] -replace("pdf", "pd_")
Rename-Item $InputPath $RenamePath -Force
}
}
First : In PowerShell, when you call a function you must not use parenthesis :
convert $hostdirectoryPlusLoanPlusDocType
or as suggested in comment
convert -inputDirectory $hostdirectoryPlusLoanPlusDocType
but not :
convert([string]$hostdirectoryPlusLoanPlusDocType)
Second : Your function should be declarated first and use after :
function toto ($var)
{
Write-Host $var
}
toto "voila"
and not
toto "voila"
function toto ($var)
{
Write-Host $var
}
I'm new to PowerShell and been trying to get this script to work.
If ((Get-Date -UFormat %a) -eq "Mon") {$intSubtract = -3}
Else {$intSubtract = -1}
$datDate = (Get-Date).AddDays($intSubtract)
Write-Output "Find expected file --------------"
$strDate = ($datDate).ToString('yyyyMMdd')
Write-Host "strDate: $strDate"
$arrGetFile = Get-ChildItem -Path "\\Computer\Data\States\NorthDakota\Cities\*_Bismark_$strDate*.txt"
$strLocalFileName = $arrGetFile
If ($arrGetFile.count -ne 2)
{
Throw "No file or more than two files with today's date exists!"
}
Else {$strLocalFileName = $arrGetFile[0].Name}
Write-Output "Found file $strLocalFileName --------------"
#Encrypt each file
foreach ($arrGetFile in $strPath)
{
Write-Output "Start Encrypt --------------"
$strPath = "\\Computer\Data\States\NorthDakota\Cities\"
$FileAndPath = Join-Path $strPath $strLocalFileName
$Recipient = "0xA49B4B5D"
Import-Module \\JAMS\C$\PSM_PGP.psm1
Get-Module Encrypt
Encrypt $FileAndPath $Recipient
$strLocalFileNamePGP = $strLocalFileName + ".pgp"
Write-Output "End Encrypt --------------"
}
#Archive files
Write-Output "Archiving --------------"
move-item -path \\Computer\Data\States\NorthDakota\Cities\*_Bismark_$strDate*.txt -destination \\Computer\Data\States\NorthDakota\Cities\Archive
The Cities folder will contain two files. Example 2015_Bismark_20150626_183121.txt and 2015_Bismark_20150626_183121_Control.txt
I am trying to get both files encrypted however it is only finding and encrypting the file without _Control. It is archiving both files correctly.
Not sure what I am missing to also find the control file.
Your for loop is incorrect. You have foreach ($arrGetFile in $strPath), but $strPath doesn't seem to contain anything at that point.
Your for loop should be:
foreach ($LocalFile in $arrGetFile)
And you need to remove the following line:
$strLocalFileName = $arrGetFile
This is making $strLocalFileName an array of file objects, but later in the script you are treating it like a string. You may have more logical errors--you need to walk through the script very carefully and identify each variable and make sure it contains what you expect it to contain.
In general you seem to be treating arrays of non-string objects as if they are strings. Note that I changed your $strLocalFileName variable to $LocalFile. This is because it is a file object, not a string object.
Following is a sample that just shows that the for loop iterates through the both files.
If ((Get-Date -UFormat %a) -eq "Mon") {$intSubtract = -3}
Else {$intSubtract = -1}
$datDate = (Get-Date).AddDays($intSubtract)
Write-Output "Find expected file --------------"
$strDate = ($datDate).ToString('yyyyMMdd')
Write-Host "strDate: $strDate"
$arrGetFile = Get-ChildItem -Path "\\Computer\Data\States\NorthDakota\Cities\*_Bismark_$strDate*.txt"
If ($arrGetFile.count -ne 2)
{
Throw "No file or more than two files with today's date exists!"
}
Write-Output "Found files " ($arrGetFile | select Name | fl) "--------------"
#Process each file
foreach ($LocalFile in $arrGetFile)
{
$FileAndPath = Join-Path $LocalFile.DirectoryName $LocalFile
$FileAndPath
}
Start with this and then carefully add your encryption processing back into the loop.
Also, The line that assigns $FileAndPath could be removed. You can just use $LocalFile.FullName wherever you need the full path and filename.